diff --git a/.changeset/all-paws-exist.md b/.changeset/all-paws-exist.md new file mode 100644 index 0000000000..95e2fe86ca --- /dev/null +++ b/.changeset/all-paws-exist.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +**Breaking changes** We are updating our Link component to use React Aria under the hood. To match their API we are updating the `to` prop to `href` to match both internal and external routing. We are also updating our variant naming to include all our new font sizes. diff --git a/.changeset/beige-ravens-count.md b/.changeset/beige-ravens-count.md new file mode 100644 index 0000000000..f7aaa00b48 --- /dev/null +++ b/.changeset/beige-ravens-count.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Added a New Frontend System App Root Element for the `` component diff --git a/.changeset/bitter-pants-watch.md b/.changeset/bitter-pants-watch.md deleted file mode 100644 index 3accf45826..0000000000 --- a/.changeset/bitter-pants-watch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': minor ---- - -We are updating the default size of the Button component in Canon to be small instead of medium. diff --git a/.changeset/chatty-clowns-dream.md b/.changeset/chatty-clowns-dream.md deleted file mode 100644 index d601f7a40d..0000000000 --- a/.changeset/chatty-clowns-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -table actions header support i18n diff --git a/.changeset/chubby-dots-kiss.md b/.changeset/chubby-dots-kiss.md new file mode 100644 index 0000000000..7a28467725 --- /dev/null +++ b/.changeset/chubby-dots-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Fixed an issue causing TechDocs to not properly handle initial redirect. diff --git a/.changeset/chubby-hotels-return.md b/.changeset/chubby-hotels-return.md new file mode 100644 index 0000000000..f3a7235a12 --- /dev/null +++ b/.changeset/chubby-hotels-return.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fixed a but in the new alpha entity predicates where the `$in` operator mistakenly case sensitive. diff --git a/.changeset/clear-bananas-end.md b/.changeset/clear-bananas-end.md new file mode 100644 index 0000000000..50e722544c --- /dev/null +++ b/.changeset/clear-bananas-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-graph': patch +--- + +Catalog graph plugin support i18n diff --git a/.changeset/cold-meals-like.md b/.changeset/cold-meals-like.md deleted file mode 100644 index 7feb978337..0000000000 --- a/.changeset/cold-meals-like.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-client': patch ---- - -Fixed `CatalogClient` error responses for `refreshEntity` and `addLocation`. diff --git a/.changeset/create-app-1748956288.md b/.changeset/create-app-1751370514.md similarity index 100% rename from .changeset/create-app-1748956288.md rename to .changeset/create-app-1751370514.md diff --git a/.changeset/cuddly-camels-lose.md b/.changeset/cuddly-camels-lose.md deleted file mode 100644 index 23162bfdc5..0000000000 --- a/.changeset/cuddly-camels-lose.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-gitlab': patch ---- - -Migrate to new action format diff --git a/.changeset/curly-zoos-thank.md b/.changeset/curly-zoos-thank.md new file mode 100644 index 0000000000..ec55b2abb9 --- /dev/null +++ b/.changeset/curly-zoos-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +We are consolidating all css files into a single styles.css in Canon. diff --git a/.changeset/curvy-peaches-relate.md b/.changeset/curvy-peaches-relate.md deleted file mode 100644 index 9fafc77abb..0000000000 --- a/.changeset/curvy-peaches-relate.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-azure': patch ---- - -Migrating `azure` actions to using the new `zod` schema format diff --git a/.changeset/curvy-webs-go.md b/.changeset/curvy-webs-go.md new file mode 100644 index 0000000000..f0ba7d8f0b --- /dev/null +++ b/.changeset/curvy-webs-go.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new Card component to Canon. diff --git a/.changeset/cyan-islands-swim.md b/.changeset/cyan-islands-swim.md new file mode 100644 index 0000000000..396cc6fc68 --- /dev/null +++ b/.changeset/cyan-islands-swim.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-catalog-backend': major +'@backstage/create-app': patch +--- + +**BREAKING:** The experimental `catalog.useUrlReadersSearch` configuration flag (introduced in v1.36) has been removed. + +The `UrlReaderProcessor` now always uses the `search` method of `UrlReaders`. Built-in `UrlReaderService` implementations have been updated accordingly. +If you use custom `UrlReaderService` implementations, you need to adapt their `search` method to correctly handle both specific URLs and potential +search patterns (see changes on built-in readers [in the original PR](https://github.com/backstage/backstage/pull/28379/files#diff-68b0452f173ee54bdd40f7b5e047a9cb8bb59200425622c212c217b76dac1d1b)). + +Previous behavior was to call the `search` method only if the parsed Git URL's filename contained a wildcard and use `readUrl` otherwise. Each `UrlReaderService` must implement this logic in the `search` method instead. + +This allows each `UrlReaderService` implementation to check whether it's a search URL (that contains a wildcard pattern) or not using logic that is specific to each provider. diff --git a/.changeset/dark-cougars-go.md b/.changeset/dark-cougars-go.md new file mode 100644 index 0000000000..f835dd2658 --- /dev/null +++ b/.changeset/dark-cougars-go.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new SearchField component in Canon diff --git a/.changeset/fresh-otters-say.md b/.changeset/dark-sheep-raise.md similarity index 50% rename from .changeset/fresh-otters-say.md rename to .changeset/dark-sheep-raise.md index ed91c9fb8d..59b2f12b71 100644 --- a/.changeset/fresh-otters-say.md +++ b/.changeset/dark-sheep-raise.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend': patch --- -Migrating to latest action format +Scaffolder audit Log now includes taskId and createdBy diff --git a/.changeset/deep-moments-love.md b/.changeset/deep-moments-love.md deleted file mode 100644 index 89b3174275..0000000000 --- a/.changeset/deep-moments-love.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': patch ---- - -Add min-width: 0; by default on every Flex components in Canon to help support truncated texts inside flex elements. diff --git a/.changeset/dry-hornets-stop.md b/.changeset/dry-hornets-stop.md new file mode 100644 index 0000000000..4979d463a8 --- /dev/null +++ b/.changeset/dry-hornets-stop.md @@ -0,0 +1,6 @@ +--- +'@backstage/config-loader': patch +'@backstage/config': patch +--- + +Loosen the requirements for a key to be considered valid config. diff --git a/.changeset/dull-buttons-press.md b/.changeset/dull-buttons-press.md deleted file mode 100644 index 1c8f8fc13c..0000000000 --- a/.changeset/dull-buttons-press.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch ---- - -Migrate `bitbucket-cloud` to new actions format diff --git a/.changeset/early-trains-relax.md b/.changeset/early-trains-relax.md new file mode 100644 index 0000000000..179e224681 --- /dev/null +++ b/.changeset/early-trains-relax.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new Header component to Canon. diff --git a/.changeset/eight-pigs-post.md b/.changeset/eight-pigs-post.md deleted file mode 100644 index 8e50cc7707..0000000000 --- a/.changeset/eight-pigs-post.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': minor ---- - -We set the default size for IconButton in Canon to be small instead of medium. diff --git a/.changeset/eight-planets-see.md b/.changeset/eight-planets-see.md deleted file mode 100644 index 20adcac7ef..0000000000 --- a/.changeset/eight-planets-see.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -render details for composite property schemas diff --git a/.changeset/eighty-showers-hide.md b/.changeset/eighty-showers-hide.md deleted file mode 100644 index 0871fea4bb..0000000000 --- a/.changeset/eighty-showers-hide.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node-test-utils': minor ---- - -**BREAKING CHANGES** - -Because of the removal of the `logStream` property to the `ActionsContext` this has been removed from the `createMockActionContext` method. - -You can remove this as it's no longer supported in the scaffolder actions. diff --git a/.changeset/eleven-pans-turn.md b/.changeset/eleven-pans-turn.md new file mode 100644 index 0000000000..a929928114 --- /dev/null +++ b/.changeset/eleven-pans-turn.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +'@backstage/plugin-search-backend-module-elasticsearch': patch +'@backstage/backend-dynamic-feature-service': patch +'@backstage/plugin-catalog-backend-module-puppetdb': patch +'@backstage/plugin-catalog-backend-module-msgraph': patch +'@backstage/plugin-catalog-backend-module-ldap': patch +'@backstage/frontend-app-api': patch +'@backstage/plugin-permission-common': patch +'@backstage/plugin-kubernetes-node': patch +'@backstage/plugin-permission-node': patch +'@backstage/catalog-model': patch +'@backstage/plugin-catalog-react': patch +'@backstage/config': patch +--- + +Just some more circular dep cleanup diff --git a/.changeset/empty-mugs-agree.md b/.changeset/empty-mugs-agree.md new file mode 100644 index 0000000000..80dd35b8cf --- /dev/null +++ b/.changeset/empty-mugs-agree.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Filter MultiEntityPicker options based on rendered option value diff --git a/.changeset/empty-poets-give.md b/.changeset/empty-poets-give.md new file mode 100644 index 0000000000..2eb63c2ed5 --- /dev/null +++ b/.changeset/empty-poets-give.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new `RadioGroup` + `Radio` component to Canon diff --git a/.changeset/evil-cooks-watch.md b/.changeset/evil-cooks-watch.md deleted file mode 100644 index 0982f24634..0000000000 --- a/.changeset/evil-cooks-watch.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -'@backstage/plugin-catalog-unprocessed-entities': patch -'@backstage/plugin-app-visualizer': patch -'@backstage/plugin-catalog-import': patch -'@backstage/plugin-catalog-graph': patch -'@backstage/plugin-notifications': patch -'@backstage/plugin-user-settings': patch -'@backstage/plugin-kubernetes': patch -'@backstage/plugin-scaffolder': patch -'@backstage/plugin-api-docs': patch -'@backstage/plugin-devtools': patch -'@backstage/plugin-techdocs': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-signals': patch -'@backstage/plugin-search': patch -'@backstage/plugin-home': patch -'@backstage/plugin-app': patch -'@backstage/plugin-org': patch ---- - -Added the `info.packageJson` option to the plugin instance for the new frontend system. diff --git a/.changeset/fair-doors-bathe.md b/.changeset/fair-doors-bathe.md deleted file mode 100644 index bbb5f238ee..0000000000 --- a/.changeset/fair-doors-bathe.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/repo-tools': patch ---- - -Fixed a bug where linting would fail with the generated clients when defining top-level `enum` schema values. diff --git a/.changeset/famous-streets-stay.md b/.changeset/famous-streets-stay.md deleted file mode 100644 index d517b889c5..0000000000 --- a/.changeset/famous-streets-stay.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node': patch ---- - -Use `LoggerService` instead of `Logger`. This is a non-breaking change, as the `LoggerService` is a subset of the `Logger` interface. diff --git a/.changeset/fancy-nails-call.md b/.changeset/fancy-nails-call.md new file mode 100644 index 0000000000..556b358dfa --- /dev/null +++ b/.changeset/fancy-nails-call.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-incremental-ingestion': patch +--- + +Fixed bug in `IncrementalIngestionEngine` by adding `burstLength` check when a burst completes diff --git a/.changeset/few-streets-accept.md b/.changeset/few-streets-accept.md deleted file mode 100644 index 4b82cc03e4..0000000000 --- a/.changeset/few-streets-accept.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch ---- - -Migrate the actions to the new format diff --git a/.changeset/fifty-geckos-join.md b/.changeset/fifty-geckos-join.md new file mode 100644 index 0000000000..57cfa5f456 --- /dev/null +++ b/.changeset/fifty-geckos-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +**Breaking change** We are moving the Select component to use React Aria under the hood. We updated most props and events according to their underlying API. diff --git a/.changeset/fix-cli-config-docs-feedback.md b/.changeset/fix-cli-config-docs-feedback.md deleted file mode 100644 index 1e7298bafa..0000000000 --- a/.changeset/fix-cli-config-docs-feedback.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added user feedback when opening config docs in browser. The command now clearly indicates what it's doing and provides fallback instructions if the browser fails to open. diff --git a/.changeset/floppy-experts-train.md b/.changeset/floppy-experts-train.md new file mode 100644 index 0000000000..880c92857b --- /dev/null +++ b/.changeset/floppy-experts-train.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Fix scrolling width and height on ScrollArea component in Canon. diff --git a/.changeset/forty-seas-worry.md b/.changeset/forty-seas-worry.md deleted file mode 100644 index a88c15df53..0000000000 --- a/.changeset/forty-seas-worry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': patch ---- - -Fix styling for the title4 prop on the Heading component in Canon. diff --git a/.changeset/fresh-brooms-follow.md b/.changeset/fresh-brooms-follow.md deleted file mode 100644 index f2858779bf..0000000000 --- a/.changeset/fresh-brooms-follow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -The `BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE` flag has been removed. Existing users are encouraged to switch to `EXPERIMENTAL_RSPACK` instead. diff --git a/.changeset/fuzzy-cooks-dress.md b/.changeset/fuzzy-cooks-dress.md new file mode 100644 index 0000000000..77fab62647 --- /dev/null +++ b/.changeset/fuzzy-cooks-dress.md @@ -0,0 +1,5 @@ +--- +'yarn-plugin-backstage': patch +--- + +Fixed a bug that would prevent the yarn plugin from installing new dependencies with the `backstage:^` protocol. diff --git a/.changeset/gentle-llamas-press.md b/.changeset/gentle-llamas-press.md new file mode 100644 index 0000000000..9c922d396f --- /dev/null +++ b/.changeset/gentle-llamas-press.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/backend-test-utils': minor +--- + +Add a standard `toString` on credentials objects diff --git a/.changeset/brave-facts-pay.md b/.changeset/giant-taxes-shine.md similarity index 59% rename from .changeset/brave-facts-pay.md rename to .changeset/giant-taxes-shine.md index 1f8e9663bf..8724b658c1 100644 --- a/.changeset/brave-facts-pay.md +++ b/.changeset/giant-taxes-shine.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend-module-confluence-to-markdown': patch --- -Migrate actions to new format +Added support for new link format for on-prem Confluence diff --git a/.changeset/good-paths-act.md b/.changeset/good-paths-act.md deleted file mode 100644 index dc33b43766..0000000000 --- a/.changeset/good-paths-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/repo-tools': patch ---- - -Fix an issue where errors were not printed to console when running `backstage-repo-tools schema openapi generate` without the `--watch` flag. diff --git a/.changeset/green-facts-wink.md b/.changeset/green-facts-wink.md new file mode 100644 index 0000000000..3c57eeac4d --- /dev/null +++ b/.changeset/green-facts-wink.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-sentry': patch +--- + +Add optional input to sentry:project:create to set the new Sentry project's platform diff --git a/.changeset/green-hornets-start.md b/.changeset/green-hornets-start.md new file mode 100644 index 0000000000..a489e8a89a --- /dev/null +++ b/.changeset/green-hornets-start.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +**Breaking changes** The Tabs components has been updates to use React Aria under the hood and to work with react-router-dom directly. diff --git a/.changeset/green-schools-live.md b/.changeset/green-schools-live.md deleted file mode 100644 index 9863021a28..0000000000 --- a/.changeset/green-schools-live.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-yeoman': patch ---- - -Migrate to new actions format diff --git a/.changeset/grumpy-dryers-act.md b/.changeset/grumpy-dryers-act.md deleted file mode 100644 index caaeac5fa8..0000000000 --- a/.changeset/grumpy-dryers-act.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/repo-tools': patch ---- - -Add missing highlight language for the `package-docs` command. diff --git a/.changeset/honest-moles-bet.md b/.changeset/honest-moles-bet.md deleted file mode 100644 index 22e0cb4533..0000000000 --- a/.changeset/honest-moles-bet.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-bitbucket-server': patch -'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch -'@backstage/plugin-scaffolder-node': patch ---- - -Update pull request creation filter to include .gitignore files in the created pull request diff --git a/.changeset/huge-taxis-sit.md b/.changeset/huge-taxis-sit.md new file mode 100644 index 0000000000..8322b2f4f7 --- /dev/null +++ b/.changeset/huge-taxis-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Fixes a module not found error when running `backstage-cli info`. diff --git a/.changeset/itchy-mirrors-juggle.md b/.changeset/itchy-mirrors-juggle.md deleted file mode 100644 index 6fd7c7543a..0000000000 --- a/.changeset/itchy-mirrors-juggle.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-notifications': patch -'@backstage/plugin-scaffolder': patch ---- - -Remove React import form notification and scaffolder plugin diff --git a/.changeset/khaki-horses-chew.md b/.changeset/khaki-horses-chew.md deleted file mode 100644 index b9f5dfda5e..0000000000 --- a/.changeset/khaki-horses-chew.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-techdocs-module-addons-contrib': patch -'@backstage/plugin-techdocs': patch ---- - -Improved Keyboard accessibility in techdocs. diff --git a/.changeset/khaki-swans-invite.md b/.changeset/khaki-swans-invite.md deleted file mode 100644 index 4e768a1978..0000000000 --- a/.changeset/khaki-swans-invite.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-notifications-backend-module-email': patch -'@backstage/plugin-notifications-backend': patch ---- - -Internal changes to switch to the non-alpha `catalogServiceRef` diff --git a/.changeset/kind-houses-feel.md b/.changeset/kind-houses-feel.md deleted file mode 100644 index a4b3ac0d14..0000000000 --- a/.changeset/kind-houses-feel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': patch ---- - -Added a render prop to the Button component in Canon to use it as a link. diff --git a/.changeset/large-baboons-prove.md b/.changeset/large-baboons-prove.md deleted file mode 100644 index 072d57de4c..0000000000 --- a/.changeset/large-baboons-prove.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@backstage/plugin-catalog': minor ---- - -Add support to customize the about card icon links via `EntityIconLinkBlueprint` and provide a default catalog view catalog source, launch scaffolder template and read techdocs docs icon links extensions. - -**BREAKING ALPHA** - -The `Scaffolder` launch template and `TechDocs` read documentation icons have been extracted from the default `Catalog` about card links and are now provided respectively by the `Scaffolder` and `TechDocs` plugins in the new frontend system. It means that they will not be available unless you install the `TechDocs` and `Scaffolder` plugins. Also If you are using translation for these icon link titles other than the default, you should now translate them using the scaffolder translation reference or the TechDocs translation reference (the translation keys are still the same, `aboutCard.viewTechdocs` and `aboutCard.launchTemplate`). diff --git a/.changeset/late-buttons-strive.md b/.changeset/late-buttons-strive.md deleted file mode 100644 index 23e1045ba0..0000000000 --- a/.changeset/late-buttons-strive.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-org': patch ---- - -Org plugin support i18n diff --git a/.changeset/lazy-rice-dream.md b/.changeset/lazy-rice-dream.md deleted file mode 100644 index fe91f85b2e..0000000000 --- a/.changeset/lazy-rice-dream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-gerrit': patch ---- - -Migrating to use new format for actions diff --git a/.changeset/long-suits-obey.md b/.changeset/long-suits-obey.md deleted file mode 100644 index 996bba6026..0000000000 --- a/.changeset/long-suits-obey.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-user-settings': patch ---- - -Add some translation to user-setting plugin diff --git a/.changeset/loose-ants-learn.md b/.changeset/loose-ants-learn.md deleted file mode 100644 index 29f357966c..0000000000 --- a/.changeset/loose-ants-learn.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-techdocs': minor ---- - -**New Frontend System Only:** -The `TechDocs` plugin is now responsible for providing an entity icon link extension to read documentation from the catalog entity page. diff --git a/.changeset/loud-turtles-mate.md b/.changeset/loud-turtles-mate.md new file mode 100644 index 0000000000..533f899c92 --- /dev/null +++ b/.changeset/loud-turtles-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-react': patch +--- + +Scroll to the top of the page when navigating between steps in template forms. diff --git a/.changeset/lucky-foxes-hide.md b/.changeset/lucky-foxes-hide.md new file mode 100644 index 0000000000..4d42a2854c --- /dev/null +++ b/.changeset/lucky-foxes-hide.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Turn on `pagination` by default in new frontend system, and also make configurable diff --git a/.changeset/major-heads-scream.md b/.changeset/major-heads-scream.md deleted file mode 100644 index 24caa6e4bb..0000000000 --- a/.changeset/major-heads-scream.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-gitlab': minor ---- - -**BREAKING CHANGE**: User and Group discovery will default to ingesting all users in sub groups that belong to the specified root group in config. Disable by setting `restrictUsersToGroup: true` in app-config under your module settings. diff --git a/.changeset/mean-lemons-dream.md b/.changeset/mean-lemons-dream.md new file mode 100644 index 0000000000..fdf7f29017 --- /dev/null +++ b/.changeset/mean-lemons-dream.md @@ -0,0 +1,5 @@ +--- +'@backstage/repo-tools': minor +--- + +Clear output directory before running `package-docs` and skip all internal packages. diff --git a/.changeset/mean-tigers-rush.md b/.changeset/mean-tigers-rush.md deleted file mode 100644 index e15b01cedc..0000000000 --- a/.changeset/mean-tigers-rush.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Switched to using the `ModuleFederationPlugin` from `@module-federation/enhanced/rspack` for Rspack, rather than the built-in one. diff --git a/.changeset/mighty-windows-boil.md b/.changeset/mighty-windows-boil.md deleted file mode 100644 index 52eb024db1..0000000000 --- a/.changeset/mighty-windows-boil.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-sentry': patch ---- - -Migrate to new actions format diff --git a/.changeset/mira-flygande-spoons.md b/.changeset/mira-flygande-spoons.md deleted file mode 100644 index c4c50f117a..0000000000 --- a/.changeset/mira-flygande-spoons.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -You can now specify an optional value when applying the `HAS_LABEL` permission rule, similar to the `HAS_ANNOTATION` permission rule. diff --git a/.changeset/new-beers-sell.md b/.changeset/new-beers-sell.md deleted file mode 100644 index fd7407007d..0000000000 --- a/.changeset/new-beers-sell.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-notifications': patch -'@backstage/plugin-notifications-backend': patch -'@backstage/plugin-notifications-common': patch -'@backstage/plugin-notifications': patch ---- - -Adds ability for user to turn on/off notifications for specific topics within an origin. diff --git a/.changeset/odd-clubs-teach.md b/.changeset/odd-clubs-teach.md new file mode 100644 index 0000000000..79b91ad2fb --- /dev/null +++ b/.changeset/odd-clubs-teach.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-dynamic-feature-service': patch +--- + +Fix wrong imports which lead to module initialization failures when enabling dynamic plugins. diff --git a/.changeset/olive-buses-serve.md b/.changeset/olive-buses-serve.md deleted file mode 100644 index d4e1798360..0000000000 --- a/.changeset/olive-buses-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-backend-module-techdocs': patch ---- - -Exports the default document collator for use in document transformation during search indexing. diff --git a/.changeset/open-bottles-attack.md b/.changeset/open-bottles-attack.md deleted file mode 100644 index e57607b55c..0000000000 --- a/.changeset/open-bottles-attack.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-notifications': patch ---- - -Removed `octokit` dependency as it was not being used diff --git a/.changeset/open-eyes-learn.md b/.changeset/open-eyes-learn.md deleted file mode 100644 index 3a0299aafb..0000000000 --- a/.changeset/open-eyes-learn.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -'@backstage/plugin-catalog-react': minor ---- - -Introduces a new `EntityIconLinkBlueprint` that customizes the `About` card icon links on the `Catalog` entity page. - -The blueprint currently accepts a `useProps` hook as `param` and this function returns the following props that will be passed to the icon link component: - -| Name | Description | Type | Default Value | -| ---------- | --------------------------------------------------- | ------------- | ------------- | -| `icon` | The icon to display. | `JSX.Element` | N/A | -| `label` | The label for the element. | `string` | N/A | -| `title` | The title for the element. | `string` | N/A | -| `disabled` | Whether the element is disabled. | `boolean` | `false` | -| `href` | The URL to navigate to when the element is clicked. | `string` | N/A | -| `onClick` | A function to call when the element is clicked. | `() => void` | N/A | - -Here is an usage example: - -```tsx -import { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha'; -//... - -EntityIconLinkBlueprint.make({ - name: 'my-icon-link', - params: { - useProps() { - const { t } = useTranslationRef(myIconLinkTranslationRef); - return { - label: t('myIconLink.label'), - icon: , - href: '/my-plugin', - }; - }, - }, -}); -``` - -Additionally, the `app-config.yaml` file allows you to override some of the default icon link parameters, including `label` and `title` values. Here's how to set them: - -```yaml -app: - extensions: - - entity-icon-link:my-plugin/my-icon-link: - config: - label: 'My Custom Icon Link label' -``` - -Finally, you can disable all links if you want to hide the About card header completely (useful, for example, when links are displayed on separate cards). The header is hidden when no icon links extensions are enabled. diff --git a/.changeset/orange-masks-decide.md b/.changeset/orange-masks-decide.md deleted file mode 100644 index 0ddb7a7e48..0000000000 --- a/.changeset/orange-masks-decide.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-rails': patch ---- - -Migrate to new actions format diff --git a/.changeset/petite-candies-tan.md b/.changeset/petite-candies-tan.md deleted file mode 100644 index 6ec5c9c813..0000000000 --- a/.changeset/petite-candies-tan.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -'@backstage/frontend-plugin-api': patch ---- - -Added a new optional `info` option to `createFrontendPlugin` that lets you provide a loaders for different sources of metadata information about the plugin. - -There are two available loaders. The first one is `info.packageJson`, which can be used to point to a `package.json` file for the plugin. This is recommended for any plugin that is defined within its own package, especially all plugins that are published to a package registry. Typical usage looks like this: - -```ts -export default createFrontendPlugin({ - pluginId: '...', - info: { - packageJson: () => import('../package.json'), - }, -}); -``` - -The second loader is `info.manifest`, which can be used to point to an opaque plugin manifest. This **MUST ONLY** be used by plugins that are intended for use within a single organization. Plugins that are published to an open package registry should **NOT** use this loader. The loader is useful for adding additional internal metadata associated with the plugin, and it is up to the Backstage app to decide how these manifests are parsed and used. The default manifest parser in an app created with `createApp` from `@backstage/frontend-defaults` is able to parse the default `catalog-info.yaml` format and built-in fields such as `spec.owner`. - -Typical usage looks like this: - -```ts -export default createFrontendPlugin({ - pluginId: '...', - info: { - manifest: () => import('../catalog-info.yaml'), - }, -}); -``` diff --git a/.changeset/petite-paths-remain.md b/.changeset/petite-paths-remain.md new file mode 100644 index 0000000000..a2bef50c80 --- /dev/null +++ b/.changeset/petite-paths-remain.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-node-test-utils': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-node': patch +--- + +An internal refactor which adds additional types to experimental checkpoints diff --git a/.changeset/plain-cobras-doubt.md b/.changeset/plain-cobras-doubt.md new file mode 100644 index 0000000000..b3f0934487 --- /dev/null +++ b/.changeset/plain-cobras-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Export Card and Skeleton components. diff --git a/.changeset/pre.json b/.changeset/pre.json index 2f88c82873..d42195c29d 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -1,255 +1,261 @@ { - "mode": "pre", + "mode": "exit", "tag": "next", "initialVersions": { - "example-app": "0.2.109", - "@backstage/app-defaults": "1.6.2", - "example-app-next": "0.0.23", - "app-next-example-plugin": "0.0.23", - "example-backend": "0.0.38", - "@backstage/backend-app-api": "1.2.3", - "@backstage/backend-defaults": "0.10.0", + "example-app": "0.2.110", + "@backstage/app-defaults": "1.6.3", + "example-app-next": "0.0.24", + "app-next-example-plugin": "0.0.24", + "example-backend": "0.0.39", + "@backstage/backend-app-api": "1.2.4", + "@backstage/backend-defaults": "0.11.0", "@backstage/backend-dev-utils": "0.1.5", - "@backstage/backend-dynamic-feature-service": "0.7.0", - "@backstage/backend-openapi-utils": "0.5.3", - "@backstage/backend-plugin-api": "1.3.1", - "@backstage/backend-test-utils": "1.5.0", - "@backstage/canon": "0.4.0", - "@backstage/catalog-client": "1.10.0", + "@backstage/backend-dynamic-feature-service": "0.7.1", + "@backstage/backend-openapi-utils": "0.5.4", + "@backstage/backend-plugin-api": "1.4.0", + "@backstage/backend-test-utils": "1.6.0", + "@backstage/canon": "0.5.0", + "@backstage/catalog-client": "1.10.1", "@backstage/catalog-model": "1.7.4", - "@backstage/cli": "0.32.1", + "@backstage/cli": "0.33.0", "@backstage/cli-common": "0.1.15", "@backstage/cli-node": "0.2.13", "@backstage/codemods": "0.1.52", "@backstage/config": "1.3.2", "@backstage/config-loader": "1.10.1", - "@backstage/core-app-api": "1.17.0", - "@backstage/core-compat-api": "0.4.2", - "@backstage/core-components": "0.17.2", - "@backstage/core-plugin-api": "1.10.7", - "@backstage/create-app": "0.6.2", - "@backstage/dev-utils": "1.1.10", - "e2e-test": "0.2.28", + "@backstage/core-app-api": "1.17.1", + "@backstage/core-compat-api": "0.4.3", + "@backstage/core-components": "0.17.3", + "@backstage/core-plugin-api": "1.10.8", + "@backstage/create-app": "0.7.0", + "@backstage/dev-utils": "1.1.11", + "e2e-test": "0.2.29", "@backstage/e2e-test-utils": "0.1.1", "@backstage/errors": "1.2.7", - "@backstage/eslint-plugin": "0.1.10", - "@backstage/frontend-app-api": "0.11.2", - "@backstage/frontend-defaults": "0.2.2", - "@backstage/frontend-dynamic-feature-loader": "0.1.1", - "@internal/frontend": "0.0.9", - "@backstage/frontend-plugin-api": "0.10.2", - "@backstage/frontend-test-utils": "0.3.2", + "@backstage/eslint-plugin": "0.1.11", + "@backstage/frontend-app-api": "0.11.3", + "@backstage/frontend-defaults": "0.2.3", + "@backstage/frontend-dynamic-feature-loader": "0.1.2", + "@internal/frontend": "0.0.10", + "@backstage/frontend-plugin-api": "0.10.3", + "@backstage/frontend-test-utils": "0.3.3", "@backstage/integration": "1.17.0", "@backstage/integration-aws-node": "0.1.16", - "@backstage/integration-react": "1.2.7", + "@backstage/integration-react": "1.2.8", "@internal/opaque": "0.0.1", "@backstage/release-manifests": "0.0.13", - "@backstage/repo-tools": "0.13.3", - "@internal/scaffolder": "0.0.9", - "@techdocs/cli": "1.9.3", - "techdocs-cli-embedded-app": "0.2.108", - "@backstage/test-utils": "1.7.8", + "@backstage/repo-tools": "0.14.0", + "@internal/scaffolder": "0.0.10", + "@techdocs/cli": "1.9.4", + "techdocs-cli-embedded-app": "0.2.109", + "@backstage/test-utils": "1.7.9", "@backstage/theme": "0.6.6", "@backstage/types": "1.2.1", "@backstage/version-bridge": "1.0.11", - "yarn-plugin-backstage": "0.0.5", - "@backstage/plugin-api-docs": "0.12.7", + "yarn-plugin-backstage": "0.0.6", + "@backstage/plugin-api-docs": "0.12.8", "@backstage/plugin-api-docs-module-protoc-gen-doc": "0.1.10", - "@backstage/plugin-app": "0.1.9", - "@backstage/plugin-app-backend": "0.5.2", - "@backstage/plugin-app-node": "0.1.33", - "@backstage/plugin-app-visualizer": "0.1.19", - "@backstage/plugin-auth-backend": "0.25.0", - "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.3", - "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.8", - "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.3", - "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-github-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-google-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-guest-provider": "0.2.8", - "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.8", - "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.3", - "@backstage/plugin-auth-backend-module-okta-provider": "0.2.3", - "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.3", - "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.3", - "@backstage/plugin-auth-node": "0.6.3", - "@backstage/plugin-auth-react": "0.1.15", + "@backstage/plugin-app": "0.1.10", + "@backstage/plugin-app-backend": "0.5.3", + "@backstage/plugin-app-node": "0.1.34", + "@backstage/plugin-app-visualizer": "0.1.20", + "@backstage/plugin-auth-backend": "0.25.1", + "@backstage/plugin-auth-backend-module-atlassian-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-auth0-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-aws-alb-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-azure-easyauth-provider": "0.2.9", + "@backstage/plugin-auth-backend-module-bitbucket-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-bitbucket-server-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-cloudflare-access-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-gcp-iap-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-github-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-gitlab-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-google-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-guest-provider": "0.2.9", + "@backstage/plugin-auth-backend-module-microsoft-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-oauth2-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "0.2.9", + "@backstage/plugin-auth-backend-module-oidc-provider": "0.4.4", + "@backstage/plugin-auth-backend-module-okta-provider": "0.2.4", + "@backstage/plugin-auth-backend-module-onelogin-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-pinniped-provider": "0.3.4", + "@backstage/plugin-auth-backend-module-vmware-cloud-provider": "0.5.4", + "@backstage/plugin-auth-node": "0.6.4", + "@backstage/plugin-auth-react": "0.1.16", "@backstage/plugin-bitbucket-cloud-common": "0.3.0", - "@backstage/plugin-catalog": "1.30.0", - "@backstage/plugin-catalog-backend": "2.0.0", - "@backstage/plugin-catalog-backend-module-aws": "0.4.11", - "@backstage/plugin-catalog-backend-module-azure": "0.3.5", - "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.2", - "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.4.8", - "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.4.1", - "@backstage/plugin-catalog-backend-module-gcp": "0.3.8", - "@backstage/plugin-catalog-backend-module-gerrit": "0.3.2", - "@backstage/plugin-catalog-backend-module-gitea": "0.1.0", - "@backstage/plugin-catalog-backend-module-github": "0.9.0", - "@backstage/plugin-catalog-backend-module-github-org": "0.3.10", - "@backstage/plugin-catalog-backend-module-gitlab": "0.6.6", - "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.9", - "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.0", - "@backstage/plugin-catalog-backend-module-ldap": "0.11.5", - "@backstage/plugin-catalog-backend-module-logs": "0.1.10", - "@backstage/plugin-catalog-backend-module-msgraph": "0.7.0", - "@backstage/plugin-catalog-backend-module-openapi": "0.2.10", - "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.10", - "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.8", - "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.0", + "@backstage/plugin-catalog": "1.31.0", + "@backstage/plugin-catalog-backend": "2.1.0", + "@backstage/plugin-catalog-backend-module-aws": "0.4.12", + "@backstage/plugin-catalog-backend-module-azure": "0.3.6", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.5.3", + "@backstage/plugin-catalog-backend-module-bitbucket-cloud": "0.5.0", + "@backstage/plugin-catalog-backend-module-bitbucket-server": "0.5.0", + "@backstage/plugin-catalog-backend-module-gcp": "0.3.9", + "@backstage/plugin-catalog-backend-module-gerrit": "0.3.3", + "@backstage/plugin-catalog-backend-module-gitea": "0.1.1", + "@backstage/plugin-catalog-backend-module-github": "0.10.0", + "@backstage/plugin-catalog-backend-module-github-org": "0.3.11", + "@backstage/plugin-catalog-backend-module-gitlab": "0.7.0", + "@backstage/plugin-catalog-backend-module-gitlab-org": "0.2.10", + "@backstage/plugin-catalog-backend-module-incremental-ingestion": "0.7.1", + "@backstage/plugin-catalog-backend-module-ldap": "0.11.6", + "@backstage/plugin-catalog-backend-module-logs": "0.1.11", + "@backstage/plugin-catalog-backend-module-msgraph": "0.7.1", + "@backstage/plugin-catalog-backend-module-openapi": "0.2.11", + "@backstage/plugin-catalog-backend-module-puppetdb": "0.2.11", + "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "0.2.9", + "@backstage/plugin-catalog-backend-module-unprocessed": "0.6.1", "@backstage/plugin-catalog-common": "1.1.4", - "@backstage/plugin-catalog-graph": "0.4.19", - "@backstage/plugin-catalog-import": "0.13.0", - "@backstage/plugin-catalog-node": "1.17.0", - "@backstage/plugin-catalog-react": "1.18.0", - "@backstage/plugin-catalog-unprocessed-entities": "0.2.17", + "@backstage/plugin-catalog-graph": "0.4.20", + "@backstage/plugin-catalog-import": "0.13.1", + "@backstage/plugin-catalog-node": "1.17.1", + "@backstage/plugin-catalog-react": "1.19.0", + "@backstage/plugin-catalog-unprocessed-entities": "0.2.18", "@backstage/plugin-catalog-unprocessed-entities-common": "0.0.8", - "@backstage/plugin-config-schema": "0.1.68", - "@backstage/plugin-devtools": "0.1.27", - "@backstage/plugin-devtools-backend": "0.5.5", + "@backstage/plugin-config-schema": "0.1.69", + "@backstage/plugin-devtools": "0.1.28", + "@backstage/plugin-devtools-backend": "0.5.6", "@backstage/plugin-devtools-common": "0.1.16", - "@backstage/plugin-events-backend": "0.5.2", - "@backstage/plugin-events-backend-module-aws-sqs": "0.4.11", - "@backstage/plugin-events-backend-module-azure": "0.2.20", - "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.20", - "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.1", - "@backstage/plugin-events-backend-module-gerrit": "0.2.20", - "@backstage/plugin-events-backend-module-github": "0.4.0", - "@backstage/plugin-events-backend-module-gitlab": "0.3.1", - "@backstage/plugin-events-backend-module-google-pubsub": "0.1.0", - "@backstage/plugin-events-backend-test-utils": "0.1.44", - "@backstage/plugin-events-node": "0.4.11", - "@internal/plugin-todo-list": "1.0.39", - "@internal/plugin-todo-list-backend": "1.0.39", + "@backstage/plugin-events-backend": "0.5.3", + "@backstage/plugin-events-backend-module-aws-sqs": "0.4.12", + "@backstage/plugin-events-backend-module-azure": "0.2.21", + "@backstage/plugin-events-backend-module-bitbucket-cloud": "0.2.21", + "@backstage/plugin-events-backend-module-bitbucket-server": "0.1.2", + "@backstage/plugin-events-backend-module-gerrit": "0.2.21", + "@backstage/plugin-events-backend-module-github": "0.4.1", + "@backstage/plugin-events-backend-module-gitlab": "0.3.2", + "@backstage/plugin-events-backend-module-google-pubsub": "0.1.1", + "@backstage/plugin-events-backend-module-kafka": "0.1.0", + "@backstage/plugin-events-backend-test-utils": "0.1.45", + "@backstage/plugin-events-node": "0.4.12", + "@internal/plugin-todo-list": "1.0.40", + "@internal/plugin-todo-list-backend": "1.0.40", "@internal/plugin-todo-list-common": "1.0.25", - "@backstage/plugin-gateway-backend": "1.0.1", - "@backstage/plugin-home": "0.8.8", - "@backstage/plugin-home-react": "0.1.26", - "@backstage/plugin-kubernetes": "0.12.7", - "@backstage/plugin-kubernetes-backend": "0.19.6", - "@backstage/plugin-kubernetes-cluster": "0.0.25", + "@backstage/plugin-gateway-backend": "1.0.2", + "@backstage/plugin-home": "0.8.9", + "@backstage/plugin-home-react": "0.1.27", + "@backstage/plugin-kubernetes": "0.12.8", + "@backstage/plugin-kubernetes-backend": "0.19.7", + "@backstage/plugin-kubernetes-cluster": "0.0.26", "@backstage/plugin-kubernetes-common": "0.9.5", - "@backstage/plugin-kubernetes-node": "0.3.0", - "@backstage/plugin-kubernetes-react": "0.5.7", - "@backstage/plugin-notifications": "0.5.5", - "@backstage/plugin-notifications-backend": "0.5.6", - "@backstage/plugin-notifications-backend-module-email": "0.3.9", - "@backstage/plugin-notifications-backend-module-slack": "0.1.1", - "@backstage/plugin-notifications-common": "0.0.8", - "@backstage/plugin-notifications-node": "0.2.15", - "@backstage/plugin-org": "0.6.39", - "@backstage/plugin-org-react": "0.1.38", - "@backstage/plugin-permission-backend": "0.7.0", - "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.8", + "@backstage/plugin-kubernetes-node": "0.3.1", + "@backstage/plugin-kubernetes-react": "0.5.8", + "@backstage/plugin-mcp-actions-backend": "0.1.0", + "@backstage/plugin-notifications": "0.5.6", + "@backstage/plugin-notifications-backend": "0.5.7", + "@backstage/plugin-notifications-backend-module-email": "0.3.10", + "@backstage/plugin-notifications-backend-module-slack": "0.1.2", + "@backstage/plugin-notifications-common": "0.0.9", + "@backstage/plugin-notifications-node": "0.2.16", + "@backstage/plugin-org": "0.6.40", + "@backstage/plugin-org-react": "0.1.39", + "@backstage/plugin-permission-backend": "0.7.1", + "@backstage/plugin-permission-backend-module-allow-all-policy": "0.2.9", "@backstage/plugin-permission-common": "0.9.0", - "@backstage/plugin-permission-node": "0.10.0", - "@backstage/plugin-permission-react": "0.4.34", - "@backstage/plugin-proxy-backend": "0.6.2", - "@backstage/plugin-proxy-node": "0.1.4", - "@backstage/plugin-scaffolder": "1.31.0", - "@backstage/plugin-scaffolder-backend": "1.33.0", - "@backstage/plugin-scaffolder-backend-module-azure": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.10", - "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.9", - "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.10", - "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-github": "0.7.1", - "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.1", - "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.10", - "@backstage/plugin-scaffolder-backend-module-rails": "0.5.9", - "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.9", - "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.10", + "@backstage/plugin-permission-node": "0.10.1", + "@backstage/plugin-permission-react": "0.4.35", + "@backstage/plugin-proxy-backend": "0.6.3", + "@backstage/plugin-proxy-node": "0.1.5", + "@backstage/plugin-scaffolder": "1.32.0", + "@backstage/plugin-scaffolder-backend": "2.0.0", + "@backstage/plugin-scaffolder-backend-module-azure": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-bitbucket": "0.3.11", + "@backstage/plugin-scaffolder-backend-module-bitbucket-cloud": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-bitbucket-server": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown": "0.3.10", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.3.11", + "@backstage/plugin-scaffolder-backend-module-gcp": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-gerrit": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-gitea": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-github": "0.8.0", + "@backstage/plugin-scaffolder-backend-module-gitlab": "0.9.2", + "@backstage/plugin-scaffolder-backend-module-notifications": "0.1.11", + "@backstage/plugin-scaffolder-backend-module-rails": "0.5.10", + "@backstage/plugin-scaffolder-backend-module-sentry": "0.2.10", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.4.11", "@backstage/plugin-scaffolder-common": "1.5.11", - "@backstage/plugin-scaffolder-node": "0.8.2", - "@backstage/plugin-scaffolder-node-test-utils": "0.2.2", - "@backstage/plugin-scaffolder-react": "1.16.0", - "@backstage/plugin-search": "1.4.26", - "@backstage/plugin-search-backend": "2.0.2", - "@backstage/plugin-search-backend-module-catalog": "0.3.4", - "@backstage/plugin-search-backend-module-elasticsearch": "1.7.2", - "@backstage/plugin-search-backend-module-explore": "0.3.2", - "@backstage/plugin-search-backend-module-pg": "0.5.44", - "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.9", - "@backstage/plugin-search-backend-module-techdocs": "0.4.2", - "@backstage/plugin-search-backend-node": "1.3.11", + "@backstage/plugin-scaffolder-node": "0.9.0", + "@backstage/plugin-scaffolder-node-test-utils": "0.3.0", + "@backstage/plugin-scaffolder-react": "1.17.0", + "@backstage/plugin-search": "1.4.27", + "@backstage/plugin-search-backend": "2.0.3", + "@backstage/plugin-search-backend-module-catalog": "0.3.5", + "@backstage/plugin-search-backend-module-elasticsearch": "1.7.3", + "@backstage/plugin-search-backend-module-explore": "0.3.3", + "@backstage/plugin-search-backend-module-pg": "0.5.45", + "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.3.10", + "@backstage/plugin-search-backend-module-techdocs": "0.4.3", + "@backstage/plugin-search-backend-node": "1.3.12", "@backstage/plugin-search-common": "1.2.18", - "@backstage/plugin-search-react": "1.9.0", - "@backstage/plugin-signals": "0.0.19", - "@backstage/plugin-signals-backend": "0.3.4", - "@backstage/plugin-signals-node": "0.1.20", - "@backstage/plugin-signals-react": "0.0.13", - "@backstage/plugin-techdocs": "1.12.6", - "@backstage/plugin-techdocs-addons-test-utils": "1.0.48", - "@backstage/plugin-techdocs-backend": "2.0.2", - "@backstage/plugin-techdocs-common": "0.1.0", - "@backstage/plugin-techdocs-module-addons-contrib": "1.1.24", - "@backstage/plugin-techdocs-node": "1.13.3", - "@backstage/plugin-techdocs-react": "1.2.17", - "@backstage/plugin-user-settings": "0.8.22", - "@backstage/plugin-user-settings-backend": "0.3.2", + "@backstage/plugin-search-react": "1.9.1", + "@backstage/plugin-signals": "0.0.20", + "@backstage/plugin-signals-backend": "0.3.5", + "@backstage/plugin-signals-node": "0.1.21", + "@backstage/plugin-signals-react": "0.0.14", + "@backstage/plugin-techdocs": "1.13.0", + "@backstage/plugin-techdocs-addons-test-utils": "1.0.49", + "@backstage/plugin-techdocs-backend": "2.0.3", + "@backstage/plugin-techdocs-common": "0.1.1", + "@backstage/plugin-techdocs-module-addons-contrib": "1.1.25", + "@backstage/plugin-techdocs-node": "1.13.4", + "@backstage/plugin-techdocs-react": "1.3.0", + "@backstage/plugin-user-settings": "0.8.23", + "@backstage/plugin-user-settings-backend": "0.3.3", "@backstage/plugin-user-settings-common": "0.0.1" }, "changesets": [ - "bitter-pants-watch", - "brave-facts-pay", - "cold-meals-like", - "create-app-1748956288", - "curvy-peaches-relate", - "dull-buttons-press", - "eight-pigs-post", - "eight-planets-see", - "evil-cooks-watch", - "famous-streets-stay", - "few-streets-accept", - "fresh-brooms-follow", - "good-paths-act", - "grumpy-dryers-act", - "honest-moles-bet", - "khaki-horses-chew", - "late-buttons-strive", - "lazy-rice-dream", - "long-suits-obey", - "mean-tigers-rush", - "new-beers-sell", - "olive-buses-serve", - "petite-candies-tan", - "puny-pillows-wave", - "red-vans-march", - "ripe-squids-sink", - "salty-worlds-float", - "scaffolder-breakages-1", - "scaffolder-breakages-2", - "scaffolder-breakages-3", - "scaffolder-deprecations-1", - "seven-beans-appear", - "shiny-bags-bet", - "six-mangos-yell", - "slow-impalas-wear", - "small-dots-march", - "smooth-eels-think", - "spotty-cases-show", - "stupid-flies-speak", - "stupid-pens-serve", - "sweet-ducks-wait", - "sweet-squids-dig", - "three-boats-sort", - "tough-heads-knock", - "true-tools-play", - "warm-taxes-show", - "wet-bars-report", - "wet-bars-reporting", - "yummy-donuts-peel" + "beige-ravens-count", + "chubby-dots-kiss", + "clear-bananas-end", + "create-app-1751370514", + "curly-zoos-thank", + "curvy-webs-go", + "cyan-islands-swim", + "dark-cougars-go", + "dry-hornets-stop", + "early-trains-relax", + "eleven-pans-turn", + "empty-mugs-agree", + "empty-poets-give", + "fancy-nails-call", + "fuzzy-cooks-dress", + "gentle-llamas-press", + "giant-taxes-shine", + "huge-taxis-sit", + "loud-turtles-mate", + "mean-lemons-dream", + "odd-clubs-teach", + "petite-paths-remain", + "public-nails-melt", + "red-trams-join", + "rich-eagles-rule", + "salty-hotels-grab", + "seven-sloths-kick", + "shaggy-cobras-rhyme", + "shaky-corners-cough", + "shaky-parents-enter", + "sharp-stars-report", + "silent-vans-tie", + "six-dots-agree", + "sixty-ties-matter", + "social-insects-cheat", + "strong-emus-drive", + "stupid-donuts-sit", + "sweet-donkeys-swim", + "tame-kings-eat", + "tender-dogs-allow", + "tender-wolves-strive", + "thin-eggs-kneel", + "three-shoes-behave", + "tiny-lions-watch", + "tired-bats-attend", + "tired-nails-fry", + "weak-bags-behave", + "whole-buckets-fix", + "wicked-socks-share", + "wise-frogs-talk", + "yellow-bobcats-join", + "yellow-streets-kiss", + "yummy-pugs-battle" ] } diff --git a/.changeset/public-nails-melt.md b/.changeset/public-nails-melt.md new file mode 100644 index 0000000000..1529ee94d0 --- /dev/null +++ b/.changeset/public-nails-melt.md @@ -0,0 +1,13 @@ +--- +'@backstage/canon': minor +--- + +**BREAKING CHANGES** + +We’re updating our Button component to provide better support for button links. + +- We’re introducing a new `ButtonLink` component, which replaces the previous render prop pattern. +- To maintain naming consistency across components, `IconButton` is being renamed to `ButtonIcon`. +- Additionally, the render prop will be removed from all button-related components. + +These changes aim to simplify usage and improve clarity in our component API. diff --git a/.changeset/puny-pillows-wave.md b/.changeset/puny-pillows-wave.md deleted file mode 100644 index 813e603007..0000000000 --- a/.changeset/puny-pillows-wave.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/frontend-plugin-api': patch ---- - -Added a new `useAppNode` hook, which can be used to get a reference to the `AppNode` from by the closest `ExtensionBoundary`. diff --git a/.changeset/purple-times-deny.md b/.changeset/purple-times-deny.md deleted file mode 100644 index 7cf4d2c3c3..0000000000 --- a/.changeset/purple-times-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Added appropriate message when global templating function metadata is absent. diff --git a/.changeset/rare-rules-move.md b/.changeset/rare-rules-move.md deleted file mode 100644 index 6abc778941..0000000000 --- a/.changeset/rare-rules-move.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-scaffolder': minor ---- - -**New Frontend System Only:** -The `Scaffolder` plugin is now responsible for providing an entity icon link extension to launch templates from the catalog entity page. diff --git a/.changeset/red-trams-join.md b/.changeset/red-trams-join.md new file mode 100644 index 0000000000..3ee245e2a8 --- /dev/null +++ b/.changeset/red-trams-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend': patch +--- + +Fix `addTopic` migration when `user_settings` present diff --git a/.changeset/red-vans-march.md b/.changeset/red-vans-march.md deleted file mode 100644 index a168e89884..0000000000 --- a/.changeset/red-vans-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-gitea': patch ---- - -Migrate to new actions format diff --git a/.changeset/rich-eagles-rule.md b/.changeset/rich-eagles-rule.md new file mode 100644 index 0000000000..100ccafb77 --- /dev/null +++ b/.changeset/rich-eagles-rule.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-mcp-actions-backend': patch +--- + +Fixed the example in the README for generating a static token by adding a subject field diff --git a/.changeset/ripe-squids-sink.md b/.changeset/ripe-squids-sink.md deleted file mode 100644 index 27d5d043b6..0000000000 --- a/.changeset/ripe-squids-sink.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -The experimental `FORCE_REACT_DEVELOPMENT` flag has been removed. diff --git a/.changeset/rotten-dragons-guess.md b/.changeset/rotten-dragons-guess.md deleted file mode 100644 index c35de83f6e..0000000000 --- a/.changeset/rotten-dragons-guess.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Fixed a bug where occasionally the initialization order of multiple modules consuming a single extension point could happen in the wrong order. diff --git a/.changeset/salty-hotels-grab.md b/.changeset/salty-hotels-grab.md new file mode 100644 index 0000000000..29a0f1eca5 --- /dev/null +++ b/.changeset/salty-hotels-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Improve Button, ButtonIcon and ButtonLink styling in Canon. diff --git a/.changeset/salty-worlds-float.md b/.changeset/salty-worlds-float.md deleted file mode 100644 index a21d0b2e7c..0000000000 --- a/.changeset/salty-worlds-float.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': minor ---- - -Add .cache directory to shipped gitignore. diff --git a/.changeset/scaffolder-breakages-1.md b/.changeset/scaffolder-breakages-1.md deleted file mode 100644 index 53a7ba9d6a..0000000000 --- a/.changeset/scaffolder-breakages-1.md +++ /dev/null @@ -1,27 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': major ---- - -**BREAKING CHANGES** - -Removal of deprecated re-exports from module packages. - -The following functions have been re-exported from the `scaffolder-backend` plugin for quite some time, and now it's time to clean them up. They've been moved as follows: - -- `createPublishAzureAction` should be imported from `@backstage/plugin-scaffolder-backend-module-azure` instead. - -- `createPublishBitbucketCloudAction` should be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` instead. - -- `createPublishBitbucketServerAction` and `createPublishBitbucketServerPullRequestAction` can be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket-server` instead. - -- `createPublishBitbucketAction` should be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket` instead. - -- `createPublishGerritAction` and `createPublishGerritReviewAction` can be imported from `@backstage/plugin-scaffolder-backend-module-gerrit` instead. - -- `createGithubActionsDispatchAction`, `createGithubDeployKeyAction`, `createGithubEnvironmentAction`, `createGithubIssuesLabelAction`, `CreateGithubPullRequestActionOptions`, `createGithubRepoCreateAction`, `createGithubRepoPushAction`, `createGithubWebhookAction`, and `createPublishGithubAction` can be imported from `@backstage/plugin-scaffolder-backend-module-github` instead. - -- `createPublishGitlabAction` should be imported from `@backstage/plugin-scaffolder-backend-module-gitlab` instead. - -- `ActionContext`. `createTemplateAction`, `executeShellCommand`, `ExecuteShellCommandOptions`, `fetchContents`, `TaskSecrets`, and `TemplateAction` should be imported from `@backstage/plugin-scaffolder-node` instead. - -- `ScaffolderEntitiesProcessor` should be imported from `@backstage/plugin-catalog-backend-module-scaffolder-entity-model` instead. diff --git a/.changeset/scaffolder-breakages-2.md b/.changeset/scaffolder-breakages-2.md deleted file mode 100644 index 77ffe5fc42..0000000000 --- a/.changeset/scaffolder-breakages-2.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': major ---- - -**BREAKING ALPHA**: The `/alpha` export no longer exports the plugin. Please use `import('@backstage/plugin-scaffolder-backend')` instead as this has been removed. - -**BREAKING CHANGES**: The old `createRouter` function which was used in the old backend system has been removed along with the `RouterOptions` type. diff --git a/.changeset/scaffolder-breakages-3.md b/.changeset/scaffolder-breakages-3.md deleted file mode 100644 index f6dca6416f..0000000000 --- a/.changeset/scaffolder-breakages-3.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': major ---- - -**BREAKING CHANGES** - -The following functions have been re-exported from the `scaffolder-backend` plugin for quite some time, and now it's time to clean them up. They've been moved as follows: - -- `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskContext`, `TaskEventType`, `TaskStatus`, `TemplateFilter`, and `TemplateGlobal` should be imported from `@backstage/plugin-scaffolder-node` instead. - -- The deprecated `copyWithoutRender` option has been removed from `fetch:template` action. You should rename the option to `copyWithoutTemplating` instead. diff --git a/.changeset/scaffolder-breakages-4.md b/.changeset/scaffolder-breakages-4.md deleted file mode 100644 index e9f0c81256..0000000000 --- a/.changeset/scaffolder-breakages-4.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -'@backstage/plugin-scaffolder-node': minor ---- - -**BREAKING CHANGES** - -The legacy methods to define `createTemplateActions` have been replaced with the new native `zod` approaches for defining input and output schemas. - -You can migrate actions that look like the following with the below examples: - -```ts -// really old legacy json schema -createTemplateAction<{ repoUrl: string }, { repoOutput: string }>({ - id: 'test', - schema: { - input: { - type: 'object' - required: ['repoUrl'] - properties: { - repoUrl: { - type: 'string', - description: 'repository url description' - } - } - } - } -}); - -// old zod method -createTemplateAction({ - id: 'test' - schema: { - input: { - repoUrl: z.string({ description: 'repository url description' }) - } - } -}) - -// new method: -createTemplateAction({ - id: 'test', - schema: { - input: { - repoUrl: z => z.string({ description: 'repository url description' }) - } - } -}) - -// or for more complex zod types like unions -createTemplateAction({ - id: 'test', - schema: { - input: z => z.object({ - repoUrl: z.string({ description: 'repository url description' }) - }) - } -}) -``` - -This breaking change also means that `logStream` has been removed entirely from `ActionsContext`, and that the `logger` is now just a `LoggerService` implementation instead. There is no replacement for the `logStream`, if you wish to still keep using a `logStream` we recommend that you create your own stream that writes to `ctx.logger` instead. diff --git a/.changeset/scaffolder-breakages-5.md b/.changeset/scaffolder-breakages-5.md deleted file mode 100644 index 8047ec3dc3..0000000000 --- a/.changeset/scaffolder-breakages-5.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-github': minor ---- - -**BREAKING CHANGES** - -The `createGithubEnvironmentAction` action no longer requires an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. - -Unless you're providing your own override action to the default, this should be a non-breaking change. - -You can migrate using the following if you're getting typescript errors: - -```ts -import { catalogServiceRef } from '@backstage/plugin-catalog-node'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; - -export const myModule = createBackendModule({ - pluginId: 'scaffolder', - moduleId: 'test', - register({ registerInit }) { - registerInit({ - deps: { - scaffolder: scaffolderActionsExtensionPoint, - catalog: catalogServiceRef, - }, - async init({ scaffolder, catalog }) { - scaffolder.addActions( - createGithubEnvironmentAction({ - catalog, - }), - ); - }, - }); - }, -}); -``` diff --git a/.changeset/scaffolder-breakages-6.md b/.changeset/scaffolder-breakages-6.md deleted file mode 100644 index 74ff76c27d..0000000000 --- a/.changeset/scaffolder-breakages-6.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': major ---- - -**BREAKING CHANGES** - -- The `createBuiltinActions` method has been removed, as this should no longer be needed with the new backend system route, and was only useful when passing the default list of actions again in the old backend system. You should be able to rely on the default behaviour of the new backend system which is to merge the actions. - -- The `createCatalogRegisterAction` and `createFetchCatalogEntityAction` actions no longer require an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. - -Unless you're providing your own override action to the default, this should be a non-breaking change. - -You can migrate using the following if you're getting typescript errors: - -```ts -import { catalogServiceRef } from '@backstage/plugin-catalog-node'; -import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; - -export const myModule = createBackendModule({ - pluginId: 'scaffolder', - moduleId: 'test', - register({ registerInit }) { - registerInit({ - deps: { - scaffolder: scaffolderActionsExtensionPoint, - catalog: catalogServiceRef, - }, - async init({ scaffolder, catalog }) { - scaffolder.addActions( - createCatalogRegisterAction({ - catalog, - }), - createFetchCatalogEntityAction({ - catalog, - integrations, - }), - ); - }, - }); - }, -}); -``` diff --git a/.changeset/scaffolder-deprecations-1.md b/.changeset/scaffolder-deprecations-1.md deleted file mode 100644 index 7d87013536..0000000000 --- a/.changeset/scaffolder-deprecations-1.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -**DEPRECATIONS** - -The following types and implementations have been deprecated, either because they're no longer relevant, or because upcoming changes to the `scaffolder-backend` after `2.0.0` will influence the changes to these API surfaces. - -- `CreateWorkerOptions` -- `DatabaseTaskStore` -- `DatabaseTaskStoreOptions` -- `TaskManager` -- `TaskStoreCreateTaskOptions` -- `TaskStoreCreateTaskResult` -- `TaskStoreEmitOptions` -- `TaskStoreListEventsOptions` -- `TaskStoreRecoverTaskOptions` -- `TaskStoreShutDownTaskOptions` - -There is no current path off deprecation, these types are going to be removed and rethought with a better way to define workers in the new backend system. diff --git a/.changeset/seven-beans-appear.md b/.changeset/seven-beans-appear.md deleted file mode 100644 index 82fe9f9889..0000000000 --- a/.changeset/seven-beans-appear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-gitlab': patch ---- - -Implement retry for GitLab API calls to handle rate limiting diff --git a/.changeset/seven-sloths-kick.md b/.changeset/seven-sloths-kick.md new file mode 100644 index 0000000000..a33924efc7 --- /dev/null +++ b/.changeset/seven-sloths-kick.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Update return types for Heading & Text components for React 19. diff --git a/.changeset/shaggy-cobras-rhyme.md b/.changeset/shaggy-cobras-rhyme.md new file mode 100644 index 0000000000..d8adefd112 --- /dev/null +++ b/.changeset/shaggy-cobras-rhyme.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +We are transforming how we structure our class names and data attributes definitions for all components. They are now all set in the same place. diff --git a/.changeset/shaky-corners-cough.md b/.changeset/shaky-corners-cough.md new file mode 100644 index 0000000000..6382101525 --- /dev/null +++ b/.changeset/shaky-corners-cough.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Small internal refactor to move out the `userInfo` database from the `tokenIssuer`. Also removes `exp` from being stored in `UserInfo` and it's now replaced with `created_at` and `updated_at` in the database instead. diff --git a/.changeset/shaky-parents-enter.md b/.changeset/shaky-parents-enter.md new file mode 100644 index 0000000000..5e81402bed --- /dev/null +++ b/.changeset/shaky-parents-enter.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-client': patch +--- + +allow arrays in the InMemoryCatalogClient to filter entities diff --git a/.changeset/sharp-stars-report.md b/.changeset/sharp-stars-report.md new file mode 100644 index 0000000000..c37e0e39ea --- /dev/null +++ b/.changeset/sharp-stars-report.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Replaced deprecated uses of `@backstage/backend-common` with the equivalents in `@backstage/backend-defaults` and `@backstage/backend-plugin-api`. diff --git a/.changeset/shiny-bags-bet.md b/.changeset/shiny-bags-bet.md deleted file mode 100644 index 0d9f2c0f30..0000000000 --- a/.changeset/shiny-bags-bet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-bitbucket': patch ---- - -Migrating `bitbucket` actions to use the new `zod` format diff --git a/.changeset/silent-vans-tie.md b/.changeset/silent-vans-tie.md new file mode 100644 index 0000000000..b81564be7e --- /dev/null +++ b/.changeset/silent-vans-tie.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Added placeholder prop to TextField component. diff --git a/.changeset/silver-mice-smile.md b/.changeset/silver-mice-smile.md new file mode 100644 index 0000000000..ae6f1a127f --- /dev/null +++ b/.changeset/silver-mice-smile.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/core-plugin-api': patch +--- + +Add optional message field for auth providers. This is intended to be a user friendly message that displays in the OAuth request dialog. A default message will be displayed if one is not provided. diff --git a/.changeset/six-dots-agree.md b/.changeset/six-dots-agree.md new file mode 100644 index 0000000000..97f9b3017f --- /dev/null +++ b/.changeset/six-dots-agree.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/integration': patch +--- + +UrlReader: Fix handling of access tokens for GitLab readURL requests diff --git a/.changeset/six-mangos-yell.md b/.changeset/six-mangos-yell.md deleted file mode 100644 index 65ddae86f8..0000000000 --- a/.changeset/six-mangos-yell.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/repo-tools': minor ---- - -Add support for caching the per-package output from the `package-docs` command. diff --git a/.changeset/sweet-squids-dig.md b/.changeset/sixty-ties-matter.md similarity index 58% rename from .changeset/sweet-squids-dig.md rename to .changeset/sixty-ties-matter.md index 78a388ec52..3be0bb08a8 100644 --- a/.changeset/sweet-squids-dig.md +++ b/.changeset/sixty-ties-matter.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend-module-cookiecutter': patch --- -Migrate using new actions format +Fixing the typescript issue with using `z.unknown()` diff --git a/.changeset/slick-rats-vanish.md b/.changeset/slick-rats-vanish.md new file mode 100644 index 0000000000..0928218a4b --- /dev/null +++ b/.changeset/slick-rats-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Updated card extensions for the new frontend system to use the new entity predicates, and switch the card type to `info` for the user and group profile cards. diff --git a/.changeset/slimy-cups-shake.md b/.changeset/slimy-cups-shake.md deleted file mode 100644 index 164b741499..0000000000 --- a/.changeset/slimy-cups-shake.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -Catalog import plugin full support i18n diff --git a/.changeset/slow-impalas-wear.md b/.changeset/slow-impalas-wear.md deleted file mode 100644 index b5151a6ed4..0000000000 --- a/.changeset/slow-impalas-wear.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Only use the caching Jest module loader for frontend packages in order to avoid breaking real ESM module imports. diff --git a/.changeset/small-dots-march.md b/.changeset/small-dots-march.md deleted file mode 100644 index 0796ef68bb..0000000000 --- a/.changeset/small-dots-march.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-gitlab': patch ---- - -Support merge request labels in publish:gitlab:merge-request diff --git a/.changeset/smooth-eels-think.md b/.changeset/smooth-eels-think.md deleted file mode 100644 index c5e2fe0c85..0000000000 --- a/.changeset/smooth-eels-think.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-techdocs': minor -'@backstage/plugin-techdocs-react': minor -'@backstage/plugin-catalog': minor -'@backstage/plugin-techdocs-common': patch ---- - -Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. diff --git a/.changeset/social-insects-cheat.md b/.changeset/social-insects-cheat.md new file mode 100644 index 0000000000..494cba15e7 --- /dev/null +++ b/.changeset/social-insects-cheat.md @@ -0,0 +1,21 @@ +--- +'@backstage/plugin-scaffolder-backend-module-github': patch +--- + +Added support for file deletion to `publish:github:pull-request` action. + +Example usage: + +```diff + - action: publish:github:pull-request + id: clean-up-pr + input: + description: This is the description ++ filesToDelete: ++ - outdated/changelog.md ++ - sample-file.txt + owner: owner + repo: repo + title: Title Goes Here + +``` diff --git a/.changeset/some-llamas-go.md b/.changeset/some-llamas-go.md deleted file mode 100644 index d07b6a6c75..0000000000 --- a/.changeset/some-llamas-go.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-bitbucket-cloud': minor ---- - -**BREAKING**: `BitbucketCloudEntityProvider` now accepts a `CatalogService` instead of a `CatalogApi`. diff --git a/.changeset/spicy-rivers-notice.md b/.changeset/spicy-rivers-notice.md deleted file mode 100644 index 941f2d257d..0000000000 --- a/.changeset/spicy-rivers-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-home': patch ---- - -Fixed the `WelcomeTitle` to properly default to the previous value of `inherit` diff --git a/.changeset/spotty-cases-show.md b/.changeset/spotty-cases-show.md deleted file mode 100644 index 3e9a607733..0000000000 --- a/.changeset/spotty-cases-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/frontend-defaults': patch ---- - -Forwarded the new `pluginInfoResolver` option for `createApp`. diff --git a/.changeset/strong-emus-drive.md b/.changeset/strong-emus-drive.md new file mode 100644 index 0000000000..00a9786354 --- /dev/null +++ b/.changeset/strong-emus-drive.md @@ -0,0 +1,5 @@ +--- +'@backstage/theme': patch +--- + +Add a different background colour to focused `MenuItem`s to fix a bug in MUI 4 (which is fixed in MUI 5) diff --git a/.changeset/strong-trams-joke.md b/.changeset/strong-trams-joke.md deleted file mode 100644 index 8b6a831d7d..0000000000 --- a/.changeset/strong-trams-joke.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Updating the scaffolder action boilerplate to use new `zod` schema diff --git a/.changeset/stupid-donuts-sit.md b/.changeset/stupid-donuts-sit.md new file mode 100644 index 0000000000..e829d49843 --- /dev/null +++ b/.changeset/stupid-donuts-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-import': patch +--- + +Fixed bug with error message since ResponseError is now thrown from CatalogClient diff --git a/.changeset/stupid-flies-speak.md b/.changeset/stupid-flies-speak.md deleted file mode 100644 index 5156a5b8b6..0000000000 --- a/.changeset/stupid-flies-speak.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/frontend-app-api': patch ---- - -Implemented support for the `plugin.info()` method in specialized apps with a default resolved for `package.json` and `catalog-info.yaml`. The default resolution logic can be overridden via the `pluginInfoResolver` option to `createSpecializedApp`, and plugin-specific overrides can be applied via the new `app.pluginOverrides` key in static configuration. diff --git a/.changeset/stupid-goats-teach.md b/.changeset/stupid-goats-teach.md deleted file mode 100644 index f90d9373e7..0000000000 --- a/.changeset/stupid-goats-teach.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Added `backstage.pluginId` field in `package.json` to all default plugin package templates for the `new` command. diff --git a/.changeset/stupid-pens-serve.md b/.changeset/stupid-pens-serve.md deleted file mode 100644 index f7fe733254..0000000000 --- a/.changeset/stupid-pens-serve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -Internal refactor to combine alpha `build` and `start` modules. diff --git a/.changeset/sweet-donkeys-swim.md b/.changeset/sweet-donkeys-swim.md new file mode 100644 index 0000000000..90c76c24e8 --- /dev/null +++ b/.changeset/sweet-donkeys-swim.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +Removed deprecated 'allowedHosts' field from GitHub Publish action input in default template files to fix validation errors with strict Zod validation. diff --git a/.changeset/sweet-ducks-wait.md b/.changeset/sweet-ducks-wait.md deleted file mode 100644 index 4792fafe84..0000000000 --- a/.changeset/sweet-ducks-wait.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-test-utils': minor ---- - -Added mock implementations for `ActionsService` and `ActionsRegistryService` diff --git a/.changeset/tame-kings-eat.md b/.changeset/tame-kings-eat.md new file mode 100644 index 0000000000..74dfc25f3d --- /dev/null +++ b/.changeset/tame-kings-eat.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': minor +--- + +if session exists and refresh fails, then create a new session if not instant popup diff --git a/.changeset/tender-dogs-allow.md b/.changeset/tender-dogs-allow.md new file mode 100644 index 0000000000..2be8d77a18 --- /dev/null +++ b/.changeset/tender-dogs-allow.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Internal refactor of OIDC endpoints and `UserInfoDatabase` diff --git a/.changeset/tender-trees-open.md b/.changeset/tender-trees-open.md deleted file mode 100644 index c54fd6574f..0000000000 --- a/.changeset/tender-trees-open.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Add tests for Scaffolder diff --git a/.changeset/tender-wolves-lie.md b/.changeset/tender-wolves-lie.md deleted file mode 100644 index d5c77b44a9..0000000000 --- a/.changeset/tender-wolves-lie.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-test-utils': patch ---- - -Make the `user` credentials mock behave more like production diff --git a/.changeset/tender-wolves-strive.md b/.changeset/tender-wolves-strive.md new file mode 100644 index 0000000000..63de61a2f9 --- /dev/null +++ b/.changeset/tender-wolves-strive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-notifications-backend-module-email': patch +--- + +Add optional config for `ses` mail options with `sourceArn`, `fromArn`, `configurationSetName` diff --git a/.changeset/dull-cars-shout.md b/.changeset/thick-emus-see.md similarity index 70% rename from .changeset/dull-cars-shout.md rename to .changeset/thick-emus-see.md index be213a167c..cd17027f9b 100644 --- a/.changeset/dull-cars-shout.md +++ b/.changeset/thick-emus-see.md @@ -2,4 +2,4 @@ '@backstage/plugin-scaffolder-backend-module-github': patch --- -Migrate to using new actions +Fix typo in `InputError` diff --git a/.changeset/thin-eggs-kneel.md b/.changeset/thin-eggs-kneel.md new file mode 100644 index 0000000000..bf70c70710 --- /dev/null +++ b/.changeset/thin-eggs-kneel.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new tertiary variant to Button, ButtonIcon and ButtonLink in Canon. diff --git a/.changeset/three-boats-sort.md b/.changeset/three-boats-sort.md deleted file mode 100644 index 180a4e236c..0000000000 --- a/.changeset/three-boats-sort.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/cli': patch ---- - -fix: merge eslint reports when using json format diff --git a/.changeset/three-shoes-behave.md b/.changeset/three-shoes-behave.md new file mode 100644 index 0000000000..51205ffa21 --- /dev/null +++ b/.changeset/three-shoes-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +adding export for ButtonLink so it's importable diff --git a/.changeset/tiny-lions-watch.md b/.changeset/tiny-lions-watch.md new file mode 100644 index 0000000000..e23ba791f9 --- /dev/null +++ b/.changeset/tiny-lions-watch.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-user-settings': patch +--- + +Added `signIn` and `signOut` analytic events to the `@backstage/core-components` of sign in and sign out. diff --git a/.changeset/tired-bats-attend.md b/.changeset/tired-bats-attend.md new file mode 100644 index 0000000000..cfe01f14fe --- /dev/null +++ b/.changeset/tired-bats-attend.md @@ -0,0 +1,5 @@ +--- +'@backstage/integration-react': patch +--- + +Separated gitlab `write_repository` and `api` scope to pass checks in `RefreshingAuthSessionManager` diff --git a/.changeset/tired-nails-fry.md b/.changeset/tired-nails-fry.md new file mode 100644 index 0000000000..0da1c4e845 --- /dev/null +++ b/.changeset/tired-nails-fry.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +We are moving our Tooltip component to use React Aria under the hood. In doing so, the structure of the component and its prop are changing to follow the new underlying structure. diff --git a/.changeset/tough-heads-knock.md b/.changeset/tough-heads-knock.md deleted file mode 100644 index da2e2c1163..0000000000 --- a/.changeset/tough-heads-knock.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Update `refresh_state_references.id` to be a big int diff --git a/.changeset/tricky-cooks-ask.md b/.changeset/tricky-cooks-ask.md new file mode 100644 index 0000000000..9021d84dbb --- /dev/null +++ b/.changeset/tricky-cooks-ask.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': minor +--- + +We are renaming @backstage/canon into @backstage/ui. As part of this move we are renaming all class names and CSS variables to follow the new name. "--canon" prefix is becoming "--bui" and all component class names starting with ".canon" will now start with ".bui" diff --git a/.changeset/true-tools-play.md b/.changeset/true-tools-play.md deleted file mode 100644 index 4d1ae4d4e8..0000000000 --- a/.changeset/true-tools-play.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend-module-bitbucket-cloud': patch ---- - -Fix `bitbucketCloudBranchRestrictions` API calls to accept null to prevent 400 errors for some branch restriction kinds defined. diff --git a/.changeset/warm-hornets-unite.md b/.changeset/warm-hornets-unite.md deleted file mode 100644 index 65a56233cb..0000000000 --- a/.changeset/warm-hornets-unite.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-search-react': patch ---- - -SearchBar clear button support i18n diff --git a/.changeset/warm-taxes-show.md b/.changeset/warm-taxes-show.md deleted file mode 100644 index edebd0048e..0000000000 --- a/.changeset/warm-taxes-show.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': minor ---- - -Fixes spacing props on layout components and aligned on naming for the Grid component. You should now call the Grid root component using instead of just . diff --git a/.changeset/weak-bags-behave.md b/.changeset/weak-bags-behave.md new file mode 100644 index 0000000000..57bf1903f1 --- /dev/null +++ b/.changeset/weak-bags-behave.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +'@backstage/plugin-scaffolder-common': minor +'@backstage/plugin-scaffolder-react': minor +'@backstage/plugin-scaffolder-node': minor +'@backstage/plugin-scaffolder': minor +--- + +BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + +BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + +BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. diff --git a/.changeset/wet-bars-report.md b/.changeset/wet-bars-report.md deleted file mode 100644 index d556f6cc61..0000000000 --- a/.changeset/wet-bars-report.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-plugin-api': minor ---- - -Added `coreServices.actionsRegistry` and `coreServices.actions` to allow registration of distributed actions from plugins, and the ability to invoke these actions diff --git a/.changeset/wet-bars-reporting.md b/.changeset/wet-bars-reporting.md deleted file mode 100644 index 7dc66cb280..0000000000 --- a/.changeset/wet-bars-reporting.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-defaults': patch ---- - -Added some default implementations for the `ActionsService` and `ActionsRegistryService` that allow registration of actions for a particular plugin. diff --git a/.changeset/whole-buckets-fix.md b/.changeset/whole-buckets-fix.md new file mode 100644 index 0000000000..87097859f4 --- /dev/null +++ b/.changeset/whole-buckets-fix.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Updated the error message thrown by parseReferenceAnnotation to reflect the annotation value passed as an argument rather than in correctly assuming location. diff --git a/.changeset/wicked-socks-share.md b/.changeset/wicked-socks-share.md new file mode 100644 index 0000000000..d3e1275732 --- /dev/null +++ b/.changeset/wicked-socks-share.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend': patch +--- + +Error messages should not contain backend SQL query strings in the API response, this change will ensure that messages are logged and empty response is returned to the user diff --git a/.changeset/wise-frogs-talk.md b/.changeset/wise-frogs-talk.md new file mode 100644 index 0000000000..6a25abc246 --- /dev/null +++ b/.changeset/wise-frogs-talk.md @@ -0,0 +1,6 @@ +--- +'@backstage/backend-defaults': patch +'@backstage/integration': patch +--- + +Fixed bug where the GitLab user token and GitLab integration token were being merged together diff --git a/.changeset/wise-signs-invite.md b/.changeset/wise-signs-invite.md new file mode 100644 index 0000000000..e6426c7f09 --- /dev/null +++ b/.changeset/wise-signs-invite.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Updated card extensions for the new frontend system to use the new entity predicates, and to not show the about card on User and Group pages. diff --git a/.changeset/yellow-bobcats-join.md b/.changeset/yellow-bobcats-join.md new file mode 100644 index 0000000000..87caccfe51 --- /dev/null +++ b/.changeset/yellow-bobcats-join.md @@ -0,0 +1,5 @@ +--- +'@backstage/canon': patch +--- + +Add new Skeleton component in Canon diff --git a/.changeset/yellow-spies-rescue.md b/.changeset/yellow-spies-rescue.md new file mode 100644 index 0000000000..83ae084288 --- /dev/null +++ b/.changeset/yellow-spies-rescue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-app': patch +--- + +Remove trailing slashes in the `AppRoutes` extension to ensure any nested routing behaves correctly. diff --git a/.changeset/yellow-streets-kiss.md b/.changeset/yellow-streets-kiss.md new file mode 100644 index 0000000000..d6c22887c3 --- /dev/null +++ b/.changeset/yellow-streets-kiss.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-signals': patch +--- + +Added a New Frontend System App Root Element for the `` component diff --git a/.changeset/yummy-donuts-peel.md b/.changeset/yummy-donuts-peel.md deleted file mode 100644 index 6c66573025..0000000000 --- a/.changeset/yummy-donuts-peel.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/canon': patch ---- - -Remove leftover console.log from Container component. diff --git a/.changeset/yummy-pugs-battle.md b/.changeset/yummy-pugs-battle.md new file mode 100644 index 0000000000..a17b934deb --- /dev/null +++ b/.changeset/yummy-pugs-battle.md @@ -0,0 +1,5 @@ +--- +'@backstage/theme': patch +--- + +Update uncomfortably bright color in recently visited and top visited home page cards diff --git a/.eslintrc.js b/.eslintrc.js index 409b820783..54f9e69576 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -18,8 +18,26 @@ var path = require('path'); module.exports = { root: true, - plugins: ['@spotify', 'notice', 'react', 'testing-library'], + plugins: ['@spotify', 'notice', 'react', 'testing-library', '@backstage'], rules: { + '@backstage/no-mixed-plugin-imports': [ + 'error', + { + // TODO: Fix these either by right role or by moving things to new packages + excludedTargetPackages: [ + '@backstage/plugin-catalog', + '@backstage/plugin-techdocs', + '@backstage/plugin-app', + '@backstage/plugin-catalog-backend', + '@backstage/test-utils', + '@backstage/plugin-auth-backend', + '@backstage/plugin-permission-backend', + '@backstage/plugin-kubernetes-backend', + '@backstage/config-loader', + '@backstage/plugin-app-backend' + ], + } + ], 'react/react-in-jsx-scope': 'off', 'notice/notice': [ 'error', diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 77faa9354f..4716f1363f 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -35,6 +35,11 @@ rangeStrategy: 'replace', matchSourceUrls: ['https://github.com/gregberge/svgr{/,}**'], }, + { + groupName: 'Module-federation monorepo packages', + rangeStrategy: 'replace', + matchSourceUrls: ['https://github.com/module-federation/core{/,}**'], + }, // We update yarn packages manually as it's gzip'd and we don't want to pollute the repository too much. { matchSourceUrls: ['https://github.com/yarnpkg/berry'], diff --git a/.github/vale/config/vocabularies/Backstage/accept.txt b/.github/vale/config/vocabularies/Backstage/accept.txt index 36f9335b1f..430e76a2ce 100644 --- a/.github/vale/config/vocabularies/Backstage/accept.txt +++ b/.github/vale/config/vocabularies/Backstage/accept.txt @@ -10,6 +10,7 @@ Airbrakes ajv Alaria Alef +allowedHosts allowlisted Anddddd Apdex @@ -43,6 +44,7 @@ bugfixes builtins bundler bundlers +bursty callout CDNs Chai @@ -102,6 +104,8 @@ deliverables denormalized dependabot deps +dequeue +dequeueing deserialization destructured destructuring @@ -111,8 +115,6 @@ devcontainer devcontainers devops devs -dequeue -dequeueing dialogs disambiguator discoverability @@ -150,8 +152,8 @@ FireHydrant Firekube Firestore Fiverr -Flightcontrol flightcontrol +Flightcontrol Francesco Frontside Gaurav @@ -175,8 +177,8 @@ Hackathons haproxy hardcoded hardcoding -Harness harness +Harness Helidon Henneke Heroku @@ -199,9 +201,10 @@ Indal indexable ingestors inlined -inlining inlinehilite +inlining integrator's +intellij interop interoperate iterable @@ -312,8 +315,8 @@ OpenSearch OpenShift openssl orgs -Overridable overridable +Overridable padding paddings pagerduty @@ -380,10 +383,10 @@ retryable reusability Reusability roadmaps -rollbar -rollout Roboto +rollbar Rollbar +rollout Rollup routable Routable @@ -399,9 +402,10 @@ sanitization scaffolded scaffolder Scaffolder -scrollbar SCM SCMs +scrollable +scrollbar sdks seb semlas @@ -409,14 +413,15 @@ semver sendmail serializable Serverless +severities shoutout SIG signoff SIGs siloed Sinon -snackbars snackbar +snackbars Snyk Sonarqube sourcemaps @@ -433,6 +438,7 @@ statefulsets stdout storable storages +Streamable stringified stringify subcommand @@ -486,6 +492,7 @@ transpiled transpiler transpilers truthy +tsconfig TSDoc typeahead ui @@ -536,7 +543,3 @@ zod Zolotusky zoomable zsh -scrollable -severities -intellij -tsconfig diff --git a/.github/workflows/api-breaking-changes-comment.yml b/.github/workflows/api-breaking-changes-comment.yml index af625f67ab..937e035470 100644 --- a/.github/workflows/api-breaking-changes-comment.yml +++ b/.github/workflows/api-breaking-changes-comment.yml @@ -23,7 +23,7 @@ jobs: comment-cache-key: ${{ steps.hash.outputs.COMMENT_FILE_HASH }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: disable-sudo: true egress-policy: block diff --git a/.github/workflows/api-breaking-changes.yml b/.github/workflows/api-breaking-changes.yml index 74473f30d5..a4e9b57939 100644 --- a/.github/workflows/api-breaking-changes.yml +++ b/.github/workflows/api-breaking-changes.yml @@ -14,7 +14,7 @@ jobs: if: ${{ github.event_name != 'pull_request' || github.event.action != 'closed' }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/automate_area-labels.yml b/.github/workflows/automate_area-labels.yml index ec6dd0c85c..32131b1b92 100644 --- a/.github/workflows/automate_area-labels.yml +++ b/.github/workflows/automate_area-labels.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index c1245c59b9..8e82bb9572 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index ebe2ef739d..4aa923fc93 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 6be74ae99f..e281d7be79 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/ci-noop.yml b/.github/workflows/ci-noop.yml index 472a7a4e72..2ddb6e79df 100644 --- a/.github/workflows/ci-noop.yml +++ b/.github/workflows/ci-noop.yml @@ -40,7 +40,7 @@ jobs: name: Test ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 059596265e..12044fd530 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,7 +28,7 @@ jobs: name: Install ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -64,7 +64,7 @@ jobs: name: Verify ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index b48bd2683d..4204b18864 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 10 steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index e1547801ef..c741f23873 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -52,17 +52,17 @@ jobs: working-directory: ./example-app - name: Login to GitHub Container Registry - uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0 + uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Build and push - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: './example-app' file: ./example-app/packages/backend/Dockerfile diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index 67fcc5d709..164da4f2e8 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -23,7 +23,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -135,7 +135,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -246,7 +246,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -321,12 +321,12 @@ jobs: name: storybook path: microsite/build/storybook - - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: stable-reference-beta path: microsite/build/api/stable/ - - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: next-reference-beta path: microsite/build/api/next/ diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index b1d79713a4..57bcb5cd18 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -151,7 +151,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 50d0e4ceb0..a073ece267 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -16,7 +16,7 @@ jobs: if: github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/pr-review-comment-trigger.yaml b/.github/workflows/pr-review-comment-trigger.yaml index e2bdb4c1cb..c3bc767379 100644 --- a/.github/workflows/pr-review-comment-trigger.yaml +++ b/.github/workflows/pr-review-comment-trigger.yaml @@ -20,7 +20,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index e108f3a4d7..e922db9550 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -17,7 +17,7 @@ jobs: steps: # Inspired by https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#using-data-from-the-triggering-workflow - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index ab2630c2d9..cbfdb3b1e9 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -18,7 +18,7 @@ jobs: if: github.repository == 'backstage/backstage' && ( github.event.pull_request || github.event.issue.pull_request ) steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 45eb5de5c8..e0ab6aacc1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_canon.yml b/.github/workflows/sync_canon.yml index 2177177df0..1187ddcc16 100644 --- a/.github/workflows/sync_canon.yml +++ b/.github/workflows/sync_canon.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index ee97f3fef3..93a78a894f 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -30,7 +30,7 @@ jobs: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: Run Prettier on ADOPTERS.md - uses: creyD/prettier_action@31355f8eef017f8aeba2e0bc09d8502b13dbbad1 # v4.3 + uses: creyD/prettier_action@8c18391fdc98ed0d884c6345f03975edac71b8f0 # v4.6 with: # Modifies commit only if prettier autofixed the ADOPTERS.md prettier_options: --config docs/prettier.config.js --write ADOPTERS.md diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index 42efaa1142..8fab209548 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'dependabot[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 1c9d37aef0..49ba9950fb 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 2eb8f13dbc..8a6eacf36e 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -11,7 +11,7 @@ jobs: if: github.actor == 'renovate[bot]' && github.repository == 'backstage/backstage' steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index b7cc0b33c7..406b99b129 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 741471d558..0ca407283f 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 42dadb5a57..2fb34431ad 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_accessibility-noop.yml b/.github/workflows/verify_accessibility-noop.yml index c33acfd86d..9d517e8021 100644 --- a/.github/workflows/verify_accessibility-noop.yml +++ b/.github/workflows/verify_accessibility-noop.yml @@ -26,7 +26,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index 02a303f9ea..b4987d28b4 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index c4b21a9e1f..0098eec8f2 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -42,7 +42,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index a3b80f498a..b6bd21e04e 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-linux-noop.yml b/.github/workflows/verify_e2e-linux-noop.yml index 65fc6eccc9..3c3e631082 100644 --- a/.github/workflows/verify_e2e-linux-noop.yml +++ b/.github/workflows/verify_e2e-linux-noop.yml @@ -29,7 +29,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 0995a85216..840af4806c 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -43,7 +43,7 @@ jobs: name: E2E Linux ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index fc30b377bc..6284bc14ea 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -32,7 +32,7 @@ jobs: name: Techdocs steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-windows-noop.yml b/.github/workflows/verify_e2e-windows-noop.yml index e713f33ed6..2088ce86ff 100644 --- a/.github/workflows/verify_e2e-windows-noop.yml +++ b/.github/workflows/verify_e2e-windows-noop.yml @@ -25,7 +25,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 0dc0bdaade..cd58c42dc1 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -33,7 +33,7 @@ jobs: name: E2E Windows ${{ matrix.node-version }} steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 7bc612bf64..30a1eccebc 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite-noop.yml b/.github/workflows/verify_microsite-noop.yml index da4bc6ba25..218a9bd992 100644 --- a/.github/workflows/verify_microsite-noop.yml +++ b/.github/workflows/verify_microsite-noop.yml @@ -21,7 +21,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index df68cff2cf..e8bb1924cc 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -28,7 +28,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -137,7 +137,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -240,7 +240,7 @@ jobs: name: Microsite steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -325,13 +325,13 @@ jobs: working-directory: microsite - name: download stable reference (beta) - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: stable-reference-beta path: api/stable/ - name: download next reference (beta) - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 with: name: next-reference-beta path: api/next/ diff --git a/.github/workflows/verify_microsite_accessibility-noop.yml b/.github/workflows/verify_microsite_accessibility-noop.yml index d6f9d24b5e..c66ff0dc4e 100644 --- a/.github/workflows/verify_microsite_accessibility-noop.yml +++ b/.github/workflows/verify_microsite_accessibility-noop.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_microsite_accessibility.yml b/.github/workflows/verify_microsite_accessibility.yml index ee841f6fa8..05701231dc 100644 --- a/.github/workflows/verify_microsite_accessibility.yml +++ b/.github/workflows/verify_microsite_accessibility.yml @@ -15,7 +15,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook-noop.yml b/.github/workflows/verify_storybook-noop.yml index 392a218a83..9f66456edd 100644 --- a/.github/workflows/verify_storybook-noop.yml +++ b/.github/workflows/verify_storybook-noop.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index 3cc846a706..06e5a921f2 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -28,7 +28,7 @@ jobs: name: Storybook steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit @@ -54,7 +54,7 @@ jobs: - run: yarn --cwd packages/canon build-storybook - - uses: chromaui/action@e8cc4c31775280b175a3c440076c00d19a9014d7 # v11 + - uses: chromaui/action@1cfa065cbdab28f6ca3afaeb3d761383076a35aa # v11 with: token: ${{ secrets.GITHUB_TOKEN }} # projectToken intentionally shared to allow collaborators to run Chromatic on forks diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index d7b172256d..0ef88d473b 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -29,7 +29,7 @@ jobs: steps: - name: Harden Runner - uses: step-security/harden-runner@cb605e52c26070c328afc4562f0b4ada7618a84e # v2.10.4 + uses: step-security/harden-runner@6c439dc8bdf85cadbbce9ed30d1c7b959517bc49 # v2.12.2 with: egress-policy: audit diff --git a/app-config.yaml b/app-config.yaml index eaee401d09..910a3e4f99 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -36,6 +36,12 @@ backend: # keys: # - secret: ${BACKEND_SECRET} + # Used for testing rate limiting locally + # rateLimit: + # windowMs: 1m + # incomingRequestLimit: 1 + # ipAllowList: [] + auth: # TODO: once plugins have been migrated we can remove this, but right now it # is require for the backend-next to work in this repo @@ -132,7 +138,6 @@ integrations: secretAccessKey: ${AWS_SECRET_ACCESS_KEY} catalog: - useUrlReadersSearch: true import: entityFilename: catalog-info.yaml pullRequestBranchName: backstage-integration diff --git a/canon-docs/next.config.mjs b/canon-docs/next.config.mjs index ca221d9a8c..8e6ed8fdc0 100644 --- a/canon-docs/next.config.mjs +++ b/canon-docs/next.config.mjs @@ -8,6 +8,11 @@ const nextConfig = { images: { unoptimized: true, }, + typescript: { + // Ignore TypeScript errors during build - safe for React 18/19 compatibility issues + // These are type-level conflicts that don't affect runtime behavior + ignoreBuildErrors: true, + }, }; const withMDX = createMDX({}); diff --git a/canon-docs/package.json b/canon-docs/package.json index 5a4affc45b..b6faca63ae 100644 --- a/canon-docs/package.json +++ b/canon-docs/package.json @@ -3,24 +3,35 @@ "version": "0.1.0", "private": true, "scripts": { - "build": "npm run build:css && next build", - "build:css": "node scripts/build-css.js", + "prebuild": "yarn sync:css", + "build": "next build", "lint": "next lint", - "start": "npm run build:css && next dev" + "prestart": "yarn sync:css", + "start": "concurrently \"yarn sync:css:watch\" \"next dev\"", + "sync:css": "node scripts/sync-css.js", + "sync:css:watch": "node scripts/sync-css.js --watch" + }, + "resolutions": { + "@types/react": "19.1.8", + "@types/react-dom": "19.1.6" }, "dependencies": { "@codemirror/lang-sass": "^6.0.2", + "@codemirror/view": "^6.34.4", "@lezer/highlight": "^1.2.1", "@mdx-js/loader": "^3.1.0", "@mdx-js/react": "^3.1.0", - "@next/mdx": "^15.1.4", + "@next/mdx": "15.3.4", "@storybook/react": "^8.6.8", "@uiw/codemirror-themes": "^4.23.7", "@uiw/react-codemirror": "^4.23.7", + "html-react-parser": "^5.2.5", "motion": "^12.4.1", - "next": "14.2.26", - "react": "^18.0.2", - "react-dom": "^18.0.2", + "next": "15.3.4", + "next-mdx-remote-client": "^2.1.2", + "prop-types": "^15.8.1", + "react": "19.1.0", + "react-dom": "19.1.0", "react-frame-component": "^5.2.7", "shiki": "^1.26.1", "storybook": "^8.6.8" @@ -28,10 +39,12 @@ "devDependencies": { "@types/mdx": "^2.0.13", "@types/node": "^20", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", + "@types/react": "19.1.8", + "@types/react-dom": "19.1.6", + "chokidar": "^3.6.0", + "concurrently": "^8.2.2", "eslint": "^8", - "eslint-config-next": "14.2.23", + "eslint-config-next": "15.3.4", "lightningcss": "^1.28.2", "typescript": "^5" } diff --git a/canon-docs/public/header.png b/canon-docs/public/header.png index 507bd05ca7..8d11151a3e 100644 Binary files a/canon-docs/public/header.png and b/canon-docs/public/header.png differ diff --git a/canon-docs/public/theme-backstage.css b/canon-docs/public/theme-backstage.css new file mode 100644 index 0000000000..3e5246bf82 --- /dev/null +++ b/canon-docs/public/theme-backstage.css @@ -0,0 +1,2 @@ +/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */ +@layer base{*,:before,:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;tab-size:4;font-family:system-ui,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;line-height:1.15}body{margin:0}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{border-color:currentColor}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:100%;line-height:1.15}button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}}.bui-p{padding:var(--p)}.bui-p-0\.5{padding:var(--bui-space-0_5)}.bui-p-1{padding:var(--bui-space-1)}.bui-p-1\.5{padding:var(--bui-space-1_5)}.bui-p-2{padding:var(--bui-space-2)}.bui-p-3{padding:var(--bui-space-3)}.bui-p-4{padding:var(--bui-space-4)}.bui-p-5{padding:var(--bui-space-5)}.bui-p-6{padding:var(--bui-space-6)}.bui-p-7{padding:var(--bui-space-7)}.bui-p-8{padding:var(--bui-space-8)}.bui-p-9{padding:var(--bui-space-9)}.bui-p-10{padding:var(--bui-space-10)}.bui-p-11{padding:var(--bui-space-11)}.bui-p-12{padding:var(--bui-space-12)}.bui-p-13{padding:var(--bui-space-13)}.bui-p-14{padding:var(--bui-space-14)}@media (width>=640px){.xs\:bui-p{padding:var(--p-xs)}.xs\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xs\:bui-p-1{padding:var(--bui-space-1)}.xs\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xs\:bui-p-2{padding:var(--bui-space-2)}.xs\:bui-p-3{padding:var(--bui-space-3)}.xs\:bui-p-4{padding:var(--bui-space-4)}.xs\:bui-p-5{padding:var(--bui-space-5)}.xs\:bui-p-6{padding:var(--bui-space-6)}.xs\:bui-p-7{padding:var(--bui-space-7)}.xs\:bui-p-8{padding:var(--bui-space-8)}.xs\:bui-p-9{padding:var(--bui-space-9)}.xs\:bui-p-10{padding:var(--bui-space-10)}.xs\:bui-p-11{padding:var(--bui-space-11)}.xs\:bui-p-12{padding:var(--bui-space-12)}.xs\:bui-p-13{padding:var(--bui-space-13)}.xs\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-p{padding:var(--p-sm)}.sm\:bui-p-0\.5{padding:var(--bui-space-0_5)}.sm\:bui-p-1{padding:var(--bui-space-1)}.sm\:bui-p-1\.5{padding:var(--bui-space-1_5)}.sm\:bui-p-2{padding:var(--bui-space-2)}.sm\:bui-p-3{padding:var(--bui-space-3)}.sm\:bui-p-4{padding:var(--bui-space-4)}.sm\:bui-p-5{padding:var(--bui-space-5)}.sm\:bui-p-6{padding:var(--bui-space-6)}.sm\:bui-p-7{padding:var(--bui-space-7)}.sm\:bui-p-8{padding:var(--bui-space-8)}.sm\:bui-p-9{padding:var(--bui-space-9)}.sm\:bui-p-10{padding:var(--bui-space-10)}.sm\:bui-p-11{padding:var(--bui-space-11)}.sm\:bui-p-12{padding:var(--bui-space-12)}.sm\:bui-p-13{padding:var(--bui-space-13)}.sm\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-p{padding:var(--p-md)}.md\:bui-p-0\.5{padding:var(--bui-space-0_5)}.md\:bui-p-1{padding:var(--bui-space-1)}.md\:bui-p-1\.5{padding:var(--bui-space-1_5)}.md\:bui-p-2{padding:var(--bui-space-2)}.md\:bui-p-3{padding:var(--bui-space-3)}.md\:bui-p-4{padding:var(--bui-space-4)}.md\:bui-p-5{padding:var(--bui-space-5)}.md\:bui-p-6{padding:var(--bui-space-6)}.md\:bui-p-7{padding:var(--bui-space-7)}.md\:bui-p-8{padding:var(--bui-space-8)}.md\:bui-p-9{padding:var(--bui-space-9)}.md\:bui-p-10{padding:var(--bui-space-10)}.md\:bui-p-11{padding:var(--bui-space-11)}.md\:bui-p-12{padding:var(--bui-space-12)}.md\:bui-p-13{padding:var(--bui-space-13)}.md\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-p{padding:var(--p-lg)}.lg\:bui-p-0\.5{padding:var(--bui-space-0_5)}.lg\:bui-p-1{padding:var(--bui-space-1)}.lg\:bui-p-1\.5{padding:var(--bui-space-1_5)}.lg\:bui-p-2{padding:var(--bui-space-2)}.lg\:bui-p-3{padding:var(--bui-space-3)}.lg\:bui-p-4{padding:var(--bui-space-4)}.lg\:bui-p-5{padding:var(--bui-space-5)}.lg\:bui-p-6{padding:var(--bui-space-6)}.lg\:bui-p-7{padding:var(--bui-space-7)}.lg\:bui-p-8{padding:var(--bui-space-8)}.lg\:bui-p-9{padding:var(--bui-space-9)}.lg\:bui-p-10{padding:var(--bui-space-10)}.lg\:bui-p-11{padding:var(--bui-space-11)}.lg\:bui-p-12{padding:var(--bui-space-12)}.lg\:bui-p-13{padding:var(--bui-space-13)}.lg\:bui-p-14{padding:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-p{padding:var(--p-xl)}.xl\:bui-p-0\.5{padding:var(--bui-space-0_5)}.xl\:bui-p-1{padding:var(--bui-space-1)}.xl\:bui-p-1\.5{padding:var(--bui-space-1_5)}.xl\:bui-p-2{padding:var(--bui-space-2)}.xl\:bui-p-3{padding:var(--bui-space-3)}.xl\:bui-p-4{padding:var(--bui-space-4)}.xl\:bui-p-5{padding:var(--bui-space-5)}.xl\:bui-p-6{padding:var(--bui-space-6)}.xl\:bui-p-7{padding:var(--bui-space-7)}.xl\:bui-p-8{padding:var(--bui-space-8)}.xl\:bui-p-9{padding:var(--bui-space-9)}.xl\:bui-p-10{padding:var(--bui-space-10)}.xl\:bui-p-11{padding:var(--bui-space-11)}.xl\:bui-p-12{padding:var(--bui-space-12)}.xl\:bui-p-13{padding:var(--bui-space-13)}.xl\:bui-p-14{padding:var(--bui-space-14)}}.bui-pl{padding-left:var(--pl)}.bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.bui-pl-1{padding-left:var(--bui-space-1)}.bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.bui-pl-2{padding-left:var(--bui-space-2)}.bui-pl-3{padding-left:var(--bui-space-3)}.bui-pl-4{padding-left:var(--bui-space-4)}.bui-pl-5{padding-left:var(--bui-space-5)}.bui-pl-6{padding-left:var(--bui-space-6)}.bui-pl-7{padding-left:var(--bui-space-7)}.bui-pl-8{padding-left:var(--bui-space-8)}.bui-pl-9{padding-left:var(--bui-space-9)}.bui-pl-10{padding-left:var(--bui-space-10)}.bui-pl-11{padding-left:var(--bui-space-11)}.bui-pl-12{padding-left:var(--bui-space-12)}.bui-pl-13{padding-left:var(--bui-space-13)}.bui-pl-14{padding-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pl{padding-left:var(--pl-xs)}.xs\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xs\:bui-pl-1{padding-left:var(--bui-space-1)}.xs\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xs\:bui-pl-2{padding-left:var(--bui-space-2)}.xs\:bui-pl-3{padding-left:var(--bui-space-3)}.xs\:bui-pl-4{padding-left:var(--bui-space-4)}.xs\:bui-pl-5{padding-left:var(--bui-space-5)}.xs\:bui-pl-6{padding-left:var(--bui-space-6)}.xs\:bui-pl-7{padding-left:var(--bui-space-7)}.xs\:bui-pl-8{padding-left:var(--bui-space-8)}.xs\:bui-pl-9{padding-left:var(--bui-space-9)}.xs\:bui-pl-10{padding-left:var(--bui-space-10)}.xs\:bui-pl-11{padding-left:var(--bui-space-11)}.xs\:bui-pl-12{padding-left:var(--bui-space-12)}.xs\:bui-pl-13{padding-left:var(--bui-space-13)}.xs\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pl{padding-left:var(--pl-sm)}.sm\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.sm\:bui-pl-1{padding-left:var(--bui-space-1)}.sm\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.sm\:bui-pl-2{padding-left:var(--bui-space-2)}.sm\:bui-pl-3{padding-left:var(--bui-space-3)}.sm\:bui-pl-4{padding-left:var(--bui-space-4)}.sm\:bui-pl-5{padding-left:var(--bui-space-5)}.sm\:bui-pl-6{padding-left:var(--bui-space-6)}.sm\:bui-pl-7{padding-left:var(--bui-space-7)}.sm\:bui-pl-8{padding-left:var(--bui-space-8)}.sm\:bui-pl-9{padding-left:var(--bui-space-9)}.sm\:bui-pl-10{padding-left:var(--bui-space-10)}.sm\:bui-pl-11{padding-left:var(--bui-space-11)}.sm\:bui-pl-12{padding-left:var(--bui-space-12)}.sm\:bui-pl-13{padding-left:var(--bui-space-13)}.sm\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pl{padding-left:var(--pl-md)}.md\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.md\:bui-pl-1{padding-left:var(--bui-space-1)}.md\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.md\:bui-pl-2{padding-left:var(--bui-space-2)}.md\:bui-pl-3{padding-left:var(--bui-space-3)}.md\:bui-pl-4{padding-left:var(--bui-space-4)}.md\:bui-pl-5{padding-left:var(--bui-space-5)}.md\:bui-pl-6{padding-left:var(--bui-space-6)}.md\:bui-pl-7{padding-left:var(--bui-space-7)}.md\:bui-pl-8{padding-left:var(--bui-space-8)}.md\:bui-pl-9{padding-left:var(--bui-space-9)}.md\:bui-pl-10{padding-left:var(--bui-space-10)}.md\:bui-pl-11{padding-left:var(--bui-space-11)}.md\:bui-pl-12{padding-left:var(--bui-space-12)}.md\:bui-pl-13{padding-left:var(--bui-space-13)}.md\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pl{padding-left:var(--pl-lg)}.lg\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.lg\:bui-pl-1{padding-left:var(--bui-space-1)}.lg\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.lg\:bui-pl-2{padding-left:var(--bui-space-2)}.lg\:bui-pl-3{padding-left:var(--bui-space-3)}.lg\:bui-pl-4{padding-left:var(--bui-space-4)}.lg\:bui-pl-5{padding-left:var(--bui-space-5)}.lg\:bui-pl-6{padding-left:var(--bui-space-6)}.lg\:bui-pl-7{padding-left:var(--bui-space-7)}.lg\:bui-pl-8{padding-left:var(--bui-space-8)}.lg\:bui-pl-9{padding-left:var(--bui-space-9)}.lg\:bui-pl-10{padding-left:var(--bui-space-10)}.lg\:bui-pl-11{padding-left:var(--bui-space-11)}.lg\:bui-pl-12{padding-left:var(--bui-space-12)}.lg\:bui-pl-13{padding-left:var(--bui-space-13)}.lg\:bui-pl-14{padding-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pl{padding-left:var(--pl-xl)}.xl\:bui-pl-0\.5{padding-left:var(--bui-space-0_5)}.xl\:bui-pl-1{padding-left:var(--bui-space-1)}.xl\:bui-pl-1\.5{padding-left:var(--bui-space-1_5)}.xl\:bui-pl-2{padding-left:var(--bui-space-2)}.xl\:bui-pl-3{padding-left:var(--bui-space-3)}.xl\:bui-pl-4{padding-left:var(--bui-space-4)}.xl\:bui-pl-5{padding-left:var(--bui-space-5)}.xl\:bui-pl-6{padding-left:var(--bui-space-6)}.xl\:bui-pl-7{padding-left:var(--bui-space-7)}.xl\:bui-pl-8{padding-left:var(--bui-space-8)}.xl\:bui-pl-9{padding-left:var(--bui-space-9)}.xl\:bui-pl-10{padding-left:var(--bui-space-10)}.xl\:bui-pl-11{padding-left:var(--bui-space-11)}.xl\:bui-pl-12{padding-left:var(--bui-space-12)}.xl\:bui-pl-13{padding-left:var(--bui-space-13)}.xl\:bui-pl-14{padding-left:var(--bui-space-14)}}.bui-pr{padding-right:var(--pr)}.bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.bui-pr-1{padding-right:var(--bui-space-1)}.bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.bui-pr-2{padding-right:var(--bui-space-2)}.bui-pr-3{padding-right:var(--bui-space-3)}.bui-pr-4{padding-right:var(--bui-space-4)}.bui-pr-5{padding-right:var(--bui-space-5)}.bui-pr-6{padding-right:var(--bui-space-6)}.bui-pr-7{padding-right:var(--bui-space-7)}.bui-pr-8{padding-right:var(--bui-space-8)}.bui-pr-9{padding-right:var(--bui-space-9)}.bui-pr-10{padding-right:var(--bui-space-10)}.bui-pr-11{padding-right:var(--bui-space-11)}.bui-pr-12{padding-right:var(--bui-space-12)}.bui-pr-13{padding-right:var(--bui-space-13)}.bui-pr-14{padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pr{padding-right:var(--pr-xs)}.xs\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xs\:bui-pr-1{padding-right:var(--bui-space-1)}.xs\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xs\:bui-pr-2{padding-right:var(--bui-space-2)}.xs\:bui-pr-3{padding-right:var(--bui-space-3)}.xs\:bui-pr-4{padding-right:var(--bui-space-4)}.xs\:bui-pr-5{padding-right:var(--bui-space-5)}.xs\:bui-pr-6{padding-right:var(--bui-space-6)}.xs\:bui-pr-7{padding-right:var(--bui-space-7)}.xs\:bui-pr-8{padding-right:var(--bui-space-8)}.xs\:bui-pr-9{padding-right:var(--bui-space-9)}.xs\:bui-pr-10{padding-right:var(--bui-space-10)}.xs\:bui-pr-11{padding-right:var(--bui-space-11)}.xs\:bui-pr-12{padding-right:var(--bui-space-12)}.xs\:bui-pr-13{padding-right:var(--bui-space-13)}.xs\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pr{padding-right:var(--pr-sm)}.sm\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.sm\:bui-pr-1{padding-right:var(--bui-space-1)}.sm\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.sm\:bui-pr-2{padding-right:var(--bui-space-2)}.sm\:bui-pr-3{padding-right:var(--bui-space-3)}.sm\:bui-pr-4{padding-right:var(--bui-space-4)}.sm\:bui-pr-5{padding-right:var(--bui-space-5)}.sm\:bui-pr-6{padding-right:var(--bui-space-6)}.sm\:bui-pr-7{padding-right:var(--bui-space-7)}.sm\:bui-pr-8{padding-right:var(--bui-space-8)}.sm\:bui-pr-9{padding-right:var(--bui-space-9)}.sm\:bui-pr-10{padding-right:var(--bui-space-10)}.sm\:bui-pr-11{padding-right:var(--bui-space-11)}.sm\:bui-pr-12{padding-right:var(--bui-space-12)}.sm\:bui-pr-13{padding-right:var(--bui-space-13)}.sm\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pr{padding-right:var(--pr-md)}.md\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.md\:bui-pr-1{padding-right:var(--bui-space-1)}.md\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.md\:bui-pr-2{padding-right:var(--bui-space-2)}.md\:bui-pr-3{padding-right:var(--bui-space-3)}.md\:bui-pr-4{padding-right:var(--bui-space-4)}.md\:bui-pr-5{padding-right:var(--bui-space-5)}.md\:bui-pr-6{padding-right:var(--bui-space-6)}.md\:bui-pr-7{padding-right:var(--bui-space-7)}.md\:bui-pr-8{padding-right:var(--bui-space-8)}.md\:bui-pr-9{padding-right:var(--bui-space-9)}.md\:bui-pr-10{padding-right:var(--bui-space-10)}.md\:bui-pr-11{padding-right:var(--bui-space-11)}.md\:bui-pr-12{padding-right:var(--bui-space-12)}.md\:bui-pr-13{padding-right:var(--bui-space-13)}.md\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pr{padding-right:var(--pr-lg)}.lg\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.lg\:bui-pr-1{padding-right:var(--bui-space-1)}.lg\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.lg\:bui-pr-2{padding-right:var(--bui-space-2)}.lg\:bui-pr-3{padding-right:var(--bui-space-3)}.lg\:bui-pr-4{padding-right:var(--bui-space-4)}.lg\:bui-pr-5{padding-right:var(--bui-space-5)}.lg\:bui-pr-6{padding-right:var(--bui-space-6)}.lg\:bui-pr-7{padding-right:var(--bui-space-7)}.lg\:bui-pr-8{padding-right:var(--bui-space-8)}.lg\:bui-pr-9{padding-right:var(--bui-space-9)}.lg\:bui-pr-10{padding-right:var(--bui-space-10)}.lg\:bui-pr-11{padding-right:var(--bui-space-11)}.lg\:bui-pr-12{padding-right:var(--bui-space-12)}.lg\:bui-pr-13{padding-right:var(--bui-space-13)}.lg\:bui-pr-14{padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pr{padding-right:var(--pr-xl)}.xl\:bui-pr-0\.5{padding-right:var(--bui-space-0_5)}.xl\:bui-pr-1{padding-right:var(--bui-space-1)}.xl\:bui-pr-1\.5{padding-right:var(--bui-space-1_5)}.xl\:bui-pr-2{padding-right:var(--bui-space-2)}.xl\:bui-pr-3{padding-right:var(--bui-space-3)}.xl\:bui-pr-4{padding-right:var(--bui-space-4)}.xl\:bui-pr-5{padding-right:var(--bui-space-5)}.xl\:bui-pr-6{padding-right:var(--bui-space-6)}.xl\:bui-pr-7{padding-right:var(--bui-space-7)}.xl\:bui-pr-8{padding-right:var(--bui-space-8)}.xl\:bui-pr-9{padding-right:var(--bui-space-9)}.xl\:bui-pr-10{padding-right:var(--bui-space-10)}.xl\:bui-pr-11{padding-right:var(--bui-space-11)}.xl\:bui-pr-12{padding-right:var(--bui-space-12)}.xl\:bui-pr-13{padding-right:var(--bui-space-13)}.xl\:bui-pr-14{padding-right:var(--bui-space-14)}}.bui-pt{padding-top:var(--pt)}.bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.bui-pt-1{padding-top:var(--bui-space-1)}.bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.bui-pt-2{padding-top:var(--bui-space-2)}.bui-pt-3{padding-top:var(--bui-space-3)}.bui-pt-4{padding-top:var(--bui-space-4)}.bui-pt-5{padding-top:var(--bui-space-5)}.bui-pt-6{padding-top:var(--bui-space-6)}.bui-pt-7{padding-top:var(--bui-space-7)}.bui-pt-8{padding-top:var(--bui-space-8)}.bui-pt-9{padding-top:var(--bui-space-9)}.bui-pt-10{padding-top:var(--bui-space-10)}.bui-pt-11{padding-top:var(--bui-space-11)}.bui-pt-12{padding-top:var(--bui-space-12)}.bui-pt-13{padding-top:var(--bui-space-13)}.bui-pt-14{padding-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pt{padding-top:var(--pt-xs)}.xs\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xs\:bui-pt-1{padding-top:var(--bui-space-1)}.xs\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xs\:bui-pt-2{padding-top:var(--bui-space-2)}.xs\:bui-pt-3{padding-top:var(--bui-space-3)}.xs\:bui-pt-4{padding-top:var(--bui-space-4)}.xs\:bui-pt-5{padding-top:var(--bui-space-5)}.xs\:bui-pt-6{padding-top:var(--bui-space-6)}.xs\:bui-pt-7{padding-top:var(--bui-space-7)}.xs\:bui-pt-8{padding-top:var(--bui-space-8)}.xs\:bui-pt-9{padding-top:var(--bui-space-9)}.xs\:bui-pt-10{padding-top:var(--bui-space-10)}.xs\:bui-pt-11{padding-top:var(--bui-space-11)}.xs\:bui-pt-12{padding-top:var(--bui-space-12)}.xs\:bui-pt-13{padding-top:var(--bui-space-13)}.xs\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pt{padding-top:var(--pt-sm)}.sm\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.sm\:bui-pt-1{padding-top:var(--bui-space-1)}.sm\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.sm\:bui-pt-2{padding-top:var(--bui-space-2)}.sm\:bui-pt-3{padding-top:var(--bui-space-3)}.sm\:bui-pt-4{padding-top:var(--bui-space-4)}.sm\:bui-pt-5{padding-top:var(--bui-space-5)}.sm\:bui-pt-6{padding-top:var(--bui-space-6)}.sm\:bui-pt-7{padding-top:var(--bui-space-7)}.sm\:bui-pt-8{padding-top:var(--bui-space-8)}.sm\:bui-pt-9{padding-top:var(--bui-space-9)}.sm\:bui-pt-10{padding-top:var(--bui-space-10)}.sm\:bui-pt-11{padding-top:var(--bui-space-11)}.sm\:bui-pt-12{padding-top:var(--bui-space-12)}.sm\:bui-pt-13{padding-top:var(--bui-space-13)}.sm\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pt{padding-top:var(--pt-md)}.md\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.md\:bui-pt-1{padding-top:var(--bui-space-1)}.md\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.md\:bui-pt-2{padding-top:var(--bui-space-2)}.md\:bui-pt-3{padding-top:var(--bui-space-3)}.md\:bui-pt-4{padding-top:var(--bui-space-4)}.md\:bui-pt-5{padding-top:var(--bui-space-5)}.md\:bui-pt-6{padding-top:var(--bui-space-6)}.md\:bui-pt-7{padding-top:var(--bui-space-7)}.md\:bui-pt-8{padding-top:var(--bui-space-8)}.md\:bui-pt-9{padding-top:var(--bui-space-9)}.md\:bui-pt-10{padding-top:var(--bui-space-10)}.md\:bui-pt-11{padding-top:var(--bui-space-11)}.md\:bui-pt-12{padding-top:var(--bui-space-12)}.md\:bui-pt-13{padding-top:var(--bui-space-13)}.md\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pt{padding-top:var(--pt-lg)}.lg\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.lg\:bui-pt-1{padding-top:var(--bui-space-1)}.lg\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.lg\:bui-pt-2{padding-top:var(--bui-space-2)}.lg\:bui-pt-3{padding-top:var(--bui-space-3)}.lg\:bui-pt-4{padding-top:var(--bui-space-4)}.lg\:bui-pt-5{padding-top:var(--bui-space-5)}.lg\:bui-pt-6{padding-top:var(--bui-space-6)}.lg\:bui-pt-7{padding-top:var(--bui-space-7)}.lg\:bui-pt-8{padding-top:var(--bui-space-8)}.lg\:bui-pt-9{padding-top:var(--bui-space-9)}.lg\:bui-pt-10{padding-top:var(--bui-space-10)}.lg\:bui-pt-11{padding-top:var(--bui-space-11)}.lg\:bui-pt-12{padding-top:var(--bui-space-12)}.lg\:bui-pt-13{padding-top:var(--bui-space-13)}.lg\:bui-pt-14{padding-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pt{padding-top:var(--pt-xl)}.xl\:bui-pt-0\.5{padding-top:var(--bui-space-0_5)}.xl\:bui-pt-1{padding-top:var(--bui-space-1)}.xl\:bui-pt-1\.5{padding-top:var(--bui-space-1_5)}.xl\:bui-pt-2{padding-top:var(--bui-space-2)}.xl\:bui-pt-3{padding-top:var(--bui-space-3)}.xl\:bui-pt-4{padding-top:var(--bui-space-4)}.xl\:bui-pt-5{padding-top:var(--bui-space-5)}.xl\:bui-pt-6{padding-top:var(--bui-space-6)}.xl\:bui-pt-7{padding-top:var(--bui-space-7)}.xl\:bui-pt-8{padding-top:var(--bui-space-8)}.xl\:bui-pt-9{padding-top:var(--bui-space-9)}.xl\:bui-pt-10{padding-top:var(--bui-space-10)}.xl\:bui-pt-11{padding-top:var(--bui-space-11)}.xl\:bui-pt-12{padding-top:var(--bui-space-12)}.xl\:bui-pt-13{padding-top:var(--bui-space-13)}.xl\:bui-pt-14{padding-top:var(--bui-space-14)}}.bui-pb{padding-bottom:var(--pb)}.bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.bui-pb-1{padding-bottom:var(--bui-space-1)}.bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.bui-pb-2{padding-bottom:var(--bui-space-2)}.bui-pb-3{padding-bottom:var(--bui-space-3)}.bui-pb-4{padding-bottom:var(--bui-space-4)}.bui-pb-5{padding-bottom:var(--bui-space-5)}.bui-pb-6{padding-bottom:var(--bui-space-6)}.bui-pb-7{padding-bottom:var(--bui-space-7)}.bui-pb-8{padding-bottom:var(--bui-space-8)}.bui-pb-9{padding-bottom:var(--bui-space-9)}.bui-pb-10{padding-bottom:var(--bui-space-10)}.bui-pb-11{padding-bottom:var(--bui-space-11)}.bui-pb-12{padding-bottom:var(--bui-space-12)}.bui-pb-13{padding-bottom:var(--bui-space-13)}.bui-pb-14{padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-pb{padding-bottom:var(--pb-xs)}.xs\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xs\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xs\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xs\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xs\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xs\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xs\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xs\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xs\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xs\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xs\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xs\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xs\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xs\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xs\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xs\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-pb{padding-bottom:var(--pb-sm)}.sm\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.sm\:bui-pb-1{padding-bottom:var(--bui-space-1)}.sm\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.sm\:bui-pb-2{padding-bottom:var(--bui-space-2)}.sm\:bui-pb-3{padding-bottom:var(--bui-space-3)}.sm\:bui-pb-4{padding-bottom:var(--bui-space-4)}.sm\:bui-pb-5{padding-bottom:var(--bui-space-5)}.sm\:bui-pb-6{padding-bottom:var(--bui-space-6)}.sm\:bui-pb-7{padding-bottom:var(--bui-space-7)}.sm\:bui-pb-8{padding-bottom:var(--bui-space-8)}.sm\:bui-pb-9{padding-bottom:var(--bui-space-9)}.sm\:bui-pb-10{padding-bottom:var(--bui-space-10)}.sm\:bui-pb-11{padding-bottom:var(--bui-space-11)}.sm\:bui-pb-12{padding-bottom:var(--bui-space-12)}.sm\:bui-pb-13{padding-bottom:var(--bui-space-13)}.sm\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-pb{padding-bottom:var(--pb-md)}.md\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.md\:bui-pb-1{padding-bottom:var(--bui-space-1)}.md\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.md\:bui-pb-2{padding-bottom:var(--bui-space-2)}.md\:bui-pb-3{padding-bottom:var(--bui-space-3)}.md\:bui-pb-4{padding-bottom:var(--bui-space-4)}.md\:bui-pb-5{padding-bottom:var(--bui-space-5)}.md\:bui-pb-6{padding-bottom:var(--bui-space-6)}.md\:bui-pb-7{padding-bottom:var(--bui-space-7)}.md\:bui-pb-8{padding-bottom:var(--bui-space-8)}.md\:bui-pb-9{padding-bottom:var(--bui-space-9)}.md\:bui-pb-10{padding-bottom:var(--bui-space-10)}.md\:bui-pb-11{padding-bottom:var(--bui-space-11)}.md\:bui-pb-12{padding-bottom:var(--bui-space-12)}.md\:bui-pb-13{padding-bottom:var(--bui-space-13)}.md\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-pb{padding-bottom:var(--pb-lg)}.lg\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.lg\:bui-pb-1{padding-bottom:var(--bui-space-1)}.lg\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.lg\:bui-pb-2{padding-bottom:var(--bui-space-2)}.lg\:bui-pb-3{padding-bottom:var(--bui-space-3)}.lg\:bui-pb-4{padding-bottom:var(--bui-space-4)}.lg\:bui-pb-5{padding-bottom:var(--bui-space-5)}.lg\:bui-pb-6{padding-bottom:var(--bui-space-6)}.lg\:bui-pb-7{padding-bottom:var(--bui-space-7)}.lg\:bui-pb-8{padding-bottom:var(--bui-space-8)}.lg\:bui-pb-9{padding-bottom:var(--bui-space-9)}.lg\:bui-pb-10{padding-bottom:var(--bui-space-10)}.lg\:bui-pb-11{padding-bottom:var(--bui-space-11)}.lg\:bui-pb-12{padding-bottom:var(--bui-space-12)}.lg\:bui-pb-13{padding-bottom:var(--bui-space-13)}.lg\:bui-pb-14{padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-pb{padding-bottom:var(--pb-xl)}.xl\:bui-pb-0\.5{padding-bottom:var(--bui-space-0_5)}.xl\:bui-pb-1{padding-bottom:var(--bui-space-1)}.xl\:bui-pb-1\.5{padding-bottom:var(--bui-space-1_5)}.xl\:bui-pb-2{padding-bottom:var(--bui-space-2)}.xl\:bui-pb-3{padding-bottom:var(--bui-space-3)}.xl\:bui-pb-4{padding-bottom:var(--bui-space-4)}.xl\:bui-pb-5{padding-bottom:var(--bui-space-5)}.xl\:bui-pb-6{padding-bottom:var(--bui-space-6)}.xl\:bui-pb-7{padding-bottom:var(--bui-space-7)}.xl\:bui-pb-8{padding-bottom:var(--bui-space-8)}.xl\:bui-pb-9{padding-bottom:var(--bui-space-9)}.xl\:bui-pb-10{padding-bottom:var(--bui-space-10)}.xl\:bui-pb-11{padding-bottom:var(--bui-space-11)}.xl\:bui-pb-12{padding-bottom:var(--bui-space-12)}.xl\:bui-pb-13{padding-bottom:var(--bui-space-13)}.xl\:bui-pb-14{padding-bottom:var(--bui-space-14)}}.bui-py{padding-top:var(--py);padding-bottom:var(--py)}.bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-py{padding-top:var(--py-xs);padding-bottom:var(--py-xs)}.xs\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xs\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xs\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xs\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xs\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xs\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xs\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xs\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xs\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xs\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xs\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xs\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xs\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xs\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xs\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xs\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-py{padding-top:var(--py-sm);padding-bottom:var(--py-sm)}.sm\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.sm\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.sm\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.sm\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.sm\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.sm\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.sm\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.sm\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.sm\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.sm\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.sm\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.sm\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.sm\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.sm\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.sm\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.sm\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-py{padding-top:var(--py-md);padding-bottom:var(--py-md)}.md\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.md\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.md\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.md\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.md\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.md\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.md\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.md\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.md\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.md\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.md\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.md\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.md\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.md\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.md\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.md\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-py{padding-top:var(--py-lg);padding-bottom:var(--py-lg)}.lg\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.lg\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.lg\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.lg\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.lg\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.lg\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.lg\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.lg\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.lg\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.lg\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.lg\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.lg\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.lg\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.lg\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.lg\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.lg\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-py{padding-top:var(--py-xl);padding-bottom:var(--py-xl)}.xl\:bui-py-0\.5{padding-top:var(--bui-space-0_5);padding-bottom:var(--bui-space-0_5)}.xl\:bui-py-1{padding-top:var(--bui-space-1);padding-bottom:var(--bui-space-1)}.xl\:bui-py-1\.5{padding-top:var(--bui-space-1_5);padding-bottom:var(--bui-space-1_5)}.xl\:bui-py-2{padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-2)}.xl\:bui-py-3{padding-top:var(--bui-space-3);padding-bottom:var(--bui-space-3)}.xl\:bui-py-4{padding-top:var(--bui-space-4);padding-bottom:var(--bui-space-4)}.xl\:bui-py-5{padding-top:var(--bui-space-5);padding-bottom:var(--bui-space-5)}.xl\:bui-py-6{padding-top:var(--bui-space-6);padding-bottom:var(--bui-space-6)}.xl\:bui-py-7{padding-top:var(--bui-space-7);padding-bottom:var(--bui-space-7)}.xl\:bui-py-8{padding-top:var(--bui-space-8);padding-bottom:var(--bui-space-8)}.xl\:bui-py-9{padding-top:var(--bui-space-9);padding-bottom:var(--bui-space-9)}.xl\:bui-py-10{padding-top:var(--bui-space-10);padding-bottom:var(--bui-space-10)}.xl\:bui-py-11{padding-top:var(--bui-space-11);padding-bottom:var(--bui-space-11)}.xl\:bui-py-12{padding-top:var(--bui-space-12);padding-bottom:var(--bui-space-12)}.xl\:bui-py-13{padding-top:var(--bui-space-13);padding-bottom:var(--bui-space-13)}.xl\:bui-py-14{padding-top:var(--bui-space-14);padding-bottom:var(--bui-space-14)}}.bui-px{padding-left:var(--px);padding-right:var(--px)}.bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-px{padding-left:var(--px-xs);padding-right:var(--px-xs)}.xs\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xs\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xs\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xs\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xs\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xs\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xs\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xs\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xs\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xs\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xs\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xs\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xs\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xs\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xs\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xs\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-px{padding-left:var(--px-sm);padding-right:var(--px-sm)}.sm\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.sm\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.sm\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.sm\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.sm\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.sm\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.sm\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.sm\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.sm\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.sm\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.sm\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.sm\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.sm\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.sm\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.sm\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.sm\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-px{padding-left:var(--px-md);padding-right:var(--px-md)}.md\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.md\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.md\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.md\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.md\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.md\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.md\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.md\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.md\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.md\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.md\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.md\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.md\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.md\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.md\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.md\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-px{padding-left:var(--px-lg);padding-right:var(--px-lg)}.lg\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.lg\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.lg\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.lg\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.lg\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.lg\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.lg\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.lg\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.lg\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.lg\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.lg\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.lg\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.lg\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.lg\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.lg\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.lg\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-px{padding-left:var(--px-xl);padding-right:var(--px-xl)}.xl\:bui-px-0\.5{padding-left:var(--bui-space-0_5);padding-right:var(--bui-space-0_5)}.xl\:bui-px-1{padding-left:var(--bui-space-1);padding-right:var(--bui-space-1)}.xl\:bui-px-1\.5{padding-left:var(--bui-space-1_5);padding-right:var(--bui-space-1_5)}.xl\:bui-px-2{padding-left:var(--bui-space-2);padding-right:var(--bui-space-2)}.xl\:bui-px-3{padding-left:var(--bui-space-3);padding-right:var(--bui-space-3)}.xl\:bui-px-4{padding-left:var(--bui-space-4);padding-right:var(--bui-space-4)}.xl\:bui-px-5{padding-left:var(--bui-space-5);padding-right:var(--bui-space-5)}.xl\:bui-px-6{padding-left:var(--bui-space-6);padding-right:var(--bui-space-6)}.xl\:bui-px-7{padding-left:var(--bui-space-7);padding-right:var(--bui-space-7)}.xl\:bui-px-8{padding-left:var(--bui-space-8);padding-right:var(--bui-space-8)}.xl\:bui-px-9{padding-left:var(--bui-space-9);padding-right:var(--bui-space-9)}.xl\:bui-px-10{padding-left:var(--bui-space-10);padding-right:var(--bui-space-10)}.xl\:bui-px-11{padding-left:var(--bui-space-11);padding-right:var(--bui-space-11)}.xl\:bui-px-12{padding-left:var(--bui-space-12);padding-right:var(--bui-space-12)}.xl\:bui-px-13{padding-left:var(--bui-space-13);padding-right:var(--bui-space-13)}.xl\:bui-px-14{padding-left:var(--bui-space-14);padding-right:var(--bui-space-14)}}.bui-m{margin:var(--m)}.bui-m-0\.5{margin:var(--bui-space-0_5)}.bui-m-1{margin:var(--bui-space-1)}.bui-m-1\.5{margin:var(--bui-space-1_5)}.bui-m-2{margin:var(--bui-space-2)}.bui-m-3{margin:var(--bui-space-3)}.bui-m-4{margin:var(--bui-space-4)}.bui-m-5{margin:var(--bui-space-5)}.bui-m-6{margin:var(--bui-space-6)}.bui-m-7{margin:var(--bui-space-7)}.bui-m-8{margin:var(--bui-space-8)}.bui-m-9{margin:var(--bui-space-9)}.bui-m-10{margin:var(--bui-space-10)}.bui-m-11{margin:var(--bui-space-11)}.bui-m-12{margin:var(--bui-space-12)}.bui-m-13{margin:var(--bui-space-13)}.bui-m-14{margin:var(--bui-space-14)}@media (width>=640px){.xs\:bui-m{margin:var(--p-xs)}.xs\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xs\:bui-m-1{margin:var(--bui-space-1)}.xs\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xs\:bui-m-2{margin:var(--bui-space-2)}.xs\:bui-m-3{margin:var(--bui-space-3)}.xs\:bui-m-4{margin:var(--bui-space-4)}.xs\:bui-m-5{margin:var(--bui-space-5)}.xs\:bui-m-6{margin:var(--bui-space-6)}.xs\:bui-m-7{margin:var(--bui-space-7)}.xs\:bui-m-8{margin:var(--bui-space-8)}.xs\:bui-m-9{margin:var(--bui-space-9)}.xs\:bui-m-10{margin:var(--bui-space-10)}.xs\:bui-m-11{margin:var(--bui-space-11)}.xs\:bui-m-12{margin:var(--bui-space-12)}.xs\:bui-m-13{margin:var(--bui-space-13)}.xs\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-m{margin:var(--p-sm)}.sm\:bui-m-0\.5{margin:var(--bui-space-0_5)}.sm\:bui-m-1{margin:var(--bui-space-1)}.sm\:bui-m-1\.5{margin:var(--bui-space-1_5)}.sm\:bui-m-2{margin:var(--bui-space-2)}.sm\:bui-m-3{margin:var(--bui-space-3)}.sm\:bui-m-4{margin:var(--bui-space-4)}.sm\:bui-m-5{margin:var(--bui-space-5)}.sm\:bui-m-6{margin:var(--bui-space-6)}.sm\:bui-m-7{margin:var(--bui-space-7)}.sm\:bui-m-8{margin:var(--bui-space-8)}.sm\:bui-m-9{margin:var(--bui-space-9)}.sm\:bui-m-10{margin:var(--bui-space-10)}.sm\:bui-m-11{margin:var(--bui-space-11)}.sm\:bui-m-12{margin:var(--bui-space-12)}.sm\:bui-m-13{margin:var(--bui-space-13)}.sm\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-m{margin:var(--p-md)}.md\:bui-m-0\.5{margin:var(--bui-space-0_5)}.md\:bui-m-1{margin:var(--bui-space-1)}.md\:bui-m-1\.5{margin:var(--bui-space-1_5)}.md\:bui-m-2{margin:var(--bui-space-2)}.md\:bui-m-3{margin:var(--bui-space-3)}.md\:bui-m-4{margin:var(--bui-space-4)}.md\:bui-m-5{margin:var(--bui-space-5)}.md\:bui-m-6{margin:var(--bui-space-6)}.md\:bui-m-7{margin:var(--bui-space-7)}.md\:bui-m-8{margin:var(--bui-space-8)}.md\:bui-m-9{margin:var(--bui-space-9)}.md\:bui-m-10{margin:var(--bui-space-10)}.md\:bui-m-11{margin:var(--bui-space-11)}.md\:bui-m-12{margin:var(--bui-space-12)}.md\:bui-m-13{margin:var(--bui-space-13)}.md\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-m{margin:var(--p-lg)}.lg\:bui-m-0\.5{margin:var(--bui-space-0_5)}.lg\:bui-m-1{margin:var(--bui-space-1)}.lg\:bui-m-1\.5{margin:var(--bui-space-1_5)}.lg\:bui-m-2{margin:var(--bui-space-2)}.lg\:bui-m-3{margin:var(--bui-space-3)}.lg\:bui-m-4{margin:var(--bui-space-4)}.lg\:bui-m-5{margin:var(--bui-space-5)}.lg\:bui-m-6{margin:var(--bui-space-6)}.lg\:bui-m-7{margin:var(--bui-space-7)}.lg\:bui-m-8{margin:var(--bui-space-8)}.lg\:bui-m-9{margin:var(--bui-space-9)}.lg\:bui-m-10{margin:var(--bui-space-10)}.lg\:bui-m-11{margin:var(--bui-space-11)}.lg\:bui-m-12{margin:var(--bui-space-12)}.lg\:bui-m-13{margin:var(--bui-space-13)}.lg\:bui-m-14{margin:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-m{margin:var(--p-xl)}.xl\:bui-m-0\.5{margin:var(--bui-space-0_5)}.xl\:bui-m-1{margin:var(--bui-space-1)}.xl\:bui-m-1\.5{margin:var(--bui-space-1_5)}.xl\:bui-m-2{margin:var(--bui-space-2)}.xl\:bui-m-3{margin:var(--bui-space-3)}.xl\:bui-m-4{margin:var(--bui-space-4)}.xl\:bui-m-5{margin:var(--bui-space-5)}.xl\:bui-m-6{margin:var(--bui-space-6)}.xl\:bui-m-7{margin:var(--bui-space-7)}.xl\:bui-m-8{margin:var(--bui-space-8)}.xl\:bui-m-9{margin:var(--bui-space-9)}.xl\:bui-m-10{margin:var(--bui-space-10)}.xl\:bui-m-11{margin:var(--bui-space-11)}.xl\:bui-m-12{margin:var(--bui-space-12)}.xl\:bui-m-13{margin:var(--bui-space-13)}.xl\:bui-m-14{margin:var(--bui-space-14)}}.bui-ml{margin-left:var(--ml)}.bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.bui-ml-1{margin-left:var(--bui-space-1)}.bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.bui-ml-2{margin-left:var(--bui-space-2)}.bui-ml-3{margin-left:var(--bui-space-3)}.bui-ml-4{margin-left:var(--bui-space-4)}.bui-ml-5{margin-left:var(--bui-space-5)}.bui-ml-6{margin-left:var(--bui-space-6)}.bui-ml-7{margin-left:var(--bui-space-7)}.bui-ml-8{margin-left:var(--bui-space-8)}.bui-ml-9{margin-left:var(--bui-space-9)}.bui-ml-10{margin-left:var(--bui-space-10)}.bui-ml-11{margin-left:var(--bui-space-11)}.bui-ml-12{margin-left:var(--bui-space-12)}.bui-ml-13{margin-left:var(--bui-space-13)}.bui-ml-14{margin-left:var(--bui-space-14)}@media (width>=640px){.xs\:bui-ml{margin-left:var(--pl-xs)}.xs\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xs\:bui-ml-1{margin-left:var(--bui-space-1)}.xs\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xs\:bui-ml-2{margin-left:var(--bui-space-2)}.xs\:bui-ml-3{margin-left:var(--bui-space-3)}.xs\:bui-ml-4{margin-left:var(--bui-space-4)}.xs\:bui-ml-5{margin-left:var(--bui-space-5)}.xs\:bui-ml-6{margin-left:var(--bui-space-6)}.xs\:bui-ml-7{margin-left:var(--bui-space-7)}.xs\:bui-ml-8{margin-left:var(--bui-space-8)}.xs\:bui-ml-9{margin-left:var(--bui-space-9)}.xs\:bui-ml-10{margin-left:var(--bui-space-10)}.xs\:bui-ml-11{margin-left:var(--bui-space-11)}.xs\:bui-ml-12{margin-left:var(--bui-space-12)}.xs\:bui-ml-13{margin-left:var(--bui-space-13)}.xs\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-ml{margin-left:var(--pl-sm)}.sm\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.sm\:bui-ml-1{margin-left:var(--bui-space-1)}.sm\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.sm\:bui-ml-2{margin-left:var(--bui-space-2)}.sm\:bui-ml-3{margin-left:var(--bui-space-3)}.sm\:bui-ml-4{margin-left:var(--bui-space-4)}.sm\:bui-ml-5{margin-left:var(--bui-space-5)}.sm\:bui-ml-6{margin-left:var(--bui-space-6)}.sm\:bui-ml-7{margin-left:var(--bui-space-7)}.sm\:bui-ml-8{margin-left:var(--bui-space-8)}.sm\:bui-ml-9{margin-left:var(--bui-space-9)}.sm\:bui-ml-10{margin-left:var(--bui-space-10)}.sm\:bui-ml-11{margin-left:var(--bui-space-11)}.sm\:bui-ml-12{margin-left:var(--bui-space-12)}.sm\:bui-ml-13{margin-left:var(--bui-space-13)}.sm\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-ml{margin-left:var(--pl-md)}.md\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.md\:bui-ml-1{margin-left:var(--bui-space-1)}.md\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.md\:bui-ml-2{margin-left:var(--bui-space-2)}.md\:bui-ml-3{margin-left:var(--bui-space-3)}.md\:bui-ml-4{margin-left:var(--bui-space-4)}.md\:bui-ml-5{margin-left:var(--bui-space-5)}.md\:bui-ml-6{margin-left:var(--bui-space-6)}.md\:bui-ml-7{margin-left:var(--bui-space-7)}.md\:bui-ml-8{margin-left:var(--bui-space-8)}.md\:bui-ml-9{margin-left:var(--bui-space-9)}.md\:bui-ml-10{margin-left:var(--bui-space-10)}.md\:bui-ml-11{margin-left:var(--bui-space-11)}.md\:bui-ml-12{margin-left:var(--bui-space-12)}.md\:bui-ml-13{margin-left:var(--bui-space-13)}.md\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-ml{margin-left:var(--pl-lg)}.lg\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.lg\:bui-ml-1{margin-left:var(--bui-space-1)}.lg\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.lg\:bui-ml-2{margin-left:var(--bui-space-2)}.lg\:bui-ml-3{margin-left:var(--bui-space-3)}.lg\:bui-ml-4{margin-left:var(--bui-space-4)}.lg\:bui-ml-5{margin-left:var(--bui-space-5)}.lg\:bui-ml-6{margin-left:var(--bui-space-6)}.lg\:bui-ml-7{margin-left:var(--bui-space-7)}.lg\:bui-ml-8{margin-left:var(--bui-space-8)}.lg\:bui-ml-9{margin-left:var(--bui-space-9)}.lg\:bui-ml-10{margin-left:var(--bui-space-10)}.lg\:bui-ml-11{margin-left:var(--bui-space-11)}.lg\:bui-ml-12{margin-left:var(--bui-space-12)}.lg\:bui-ml-13{margin-left:var(--bui-space-13)}.lg\:bui-ml-14{margin-left:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-ml{margin-left:var(--pl-xl)}.xl\:bui-ml-0\.5{margin-left:var(--bui-space-0_5)}.xl\:bui-ml-1{margin-left:var(--bui-space-1)}.xl\:bui-ml-1\.5{margin-left:var(--bui-space-1_5)}.xl\:bui-ml-2{margin-left:var(--bui-space-2)}.xl\:bui-ml-3{margin-left:var(--bui-space-3)}.xl\:bui-ml-4{margin-left:var(--bui-space-4)}.xl\:bui-ml-5{margin-left:var(--bui-space-5)}.xl\:bui-ml-6{margin-left:var(--bui-space-6)}.xl\:bui-ml-7{margin-left:var(--bui-space-7)}.xl\:bui-ml-8{margin-left:var(--bui-space-8)}.xl\:bui-ml-9{margin-left:var(--bui-space-9)}.xl\:bui-ml-10{margin-left:var(--bui-space-10)}.xl\:bui-ml-11{margin-left:var(--bui-space-11)}.xl\:bui-ml-12{margin-left:var(--bui-space-12)}.xl\:bui-ml-13{margin-left:var(--bui-space-13)}.xl\:bui-ml-14{margin-left:var(--bui-space-14)}}.bui-mr{margin-right:var(--mr)}.bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.bui-mr-1{margin-right:var(--bui-space-1)}.bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.bui-mr-2{margin-right:var(--bui-space-2)}.bui-mr-3{margin-right:var(--bui-space-3)}.bui-mr-4{margin-right:var(--bui-space-4)}.bui-mr-5{margin-right:var(--bui-space-5)}.bui-mr-6{margin-right:var(--bui-space-6)}.bui-mr-7{margin-right:var(--bui-space-7)}.bui-mr-8{margin-right:var(--bui-space-8)}.bui-mr-9{margin-right:var(--bui-space-9)}.bui-mr-10{margin-right:var(--bui-space-10)}.bui-mr-11{margin-right:var(--bui-space-11)}.bui-mr-12{margin-right:var(--bui-space-12)}.bui-mr-13{margin-right:var(--bui-space-13)}.bui-mr-14{margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mr{margin-right:var(--pr-xs)}.xs\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xs\:bui-mr-1{margin-right:var(--bui-space-1)}.xs\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xs\:bui-mr-2{margin-right:var(--bui-space-2)}.xs\:bui-mr-3{margin-right:var(--bui-space-3)}.xs\:bui-mr-4{margin-right:var(--bui-space-4)}.xs\:bui-mr-5{margin-right:var(--bui-space-5)}.xs\:bui-mr-6{margin-right:var(--bui-space-6)}.xs\:bui-mr-7{margin-right:var(--bui-space-7)}.xs\:bui-mr-8{margin-right:var(--bui-space-8)}.xs\:bui-mr-9{margin-right:var(--bui-space-9)}.xs\:bui-mr-10{margin-right:var(--bui-space-10)}.xs\:bui-mr-11{margin-right:var(--bui-space-11)}.xs\:bui-mr-12{margin-right:var(--bui-space-12)}.xs\:bui-mr-13{margin-right:var(--bui-space-13)}.xs\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mr{margin-right:var(--pr-sm)}.sm\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.sm\:bui-mr-1{margin-right:var(--bui-space-1)}.sm\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.sm\:bui-mr-2{margin-right:var(--bui-space-2)}.sm\:bui-mr-3{margin-right:var(--bui-space-3)}.sm\:bui-mr-4{margin-right:var(--bui-space-4)}.sm\:bui-mr-5{margin-right:var(--bui-space-5)}.sm\:bui-mr-6{margin-right:var(--bui-space-6)}.sm\:bui-mr-7{margin-right:var(--bui-space-7)}.sm\:bui-mr-8{margin-right:var(--bui-space-8)}.sm\:bui-mr-9{margin-right:var(--bui-space-9)}.sm\:bui-mr-10{margin-right:var(--bui-space-10)}.sm\:bui-mr-11{margin-right:var(--bui-space-11)}.sm\:bui-mr-12{margin-right:var(--bui-space-12)}.sm\:bui-mr-13{margin-right:var(--bui-space-13)}.sm\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mr{margin-right:var(--pr-md)}.md\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.md\:bui-mr-1{margin-right:var(--bui-space-1)}.md\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.md\:bui-mr-2{margin-right:var(--bui-space-2)}.md\:bui-mr-3{margin-right:var(--bui-space-3)}.md\:bui-mr-4{margin-right:var(--bui-space-4)}.md\:bui-mr-5{margin-right:var(--bui-space-5)}.md\:bui-mr-6{margin-right:var(--bui-space-6)}.md\:bui-mr-7{margin-right:var(--bui-space-7)}.md\:bui-mr-8{margin-right:var(--bui-space-8)}.md\:bui-mr-9{margin-right:var(--bui-space-9)}.md\:bui-mr-10{margin-right:var(--bui-space-10)}.md\:bui-mr-11{margin-right:var(--bui-space-11)}.md\:bui-mr-12{margin-right:var(--bui-space-12)}.md\:bui-mr-13{margin-right:var(--bui-space-13)}.md\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mr{margin-right:var(--pr-lg)}.lg\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.lg\:bui-mr-1{margin-right:var(--bui-space-1)}.lg\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.lg\:bui-mr-2{margin-right:var(--bui-space-2)}.lg\:bui-mr-3{margin-right:var(--bui-space-3)}.lg\:bui-mr-4{margin-right:var(--bui-space-4)}.lg\:bui-mr-5{margin-right:var(--bui-space-5)}.lg\:bui-mr-6{margin-right:var(--bui-space-6)}.lg\:bui-mr-7{margin-right:var(--bui-space-7)}.lg\:bui-mr-8{margin-right:var(--bui-space-8)}.lg\:bui-mr-9{margin-right:var(--bui-space-9)}.lg\:bui-mr-10{margin-right:var(--bui-space-10)}.lg\:bui-mr-11{margin-right:var(--bui-space-11)}.lg\:bui-mr-12{margin-right:var(--bui-space-12)}.lg\:bui-mr-13{margin-right:var(--bui-space-13)}.lg\:bui-mr-14{margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mr{margin-right:var(--pr-xl)}.xl\:bui-mr-0\.5{margin-right:var(--bui-space-0_5)}.xl\:bui-mr-1{margin-right:var(--bui-space-1)}.xl\:bui-mr-1\.5{margin-right:var(--bui-space-1_5)}.xl\:bui-mr-2{margin-right:var(--bui-space-2)}.xl\:bui-mr-3{margin-right:var(--bui-space-3)}.xl\:bui-mr-4{margin-right:var(--bui-space-4)}.xl\:bui-mr-5{margin-right:var(--bui-space-5)}.xl\:bui-mr-6{margin-right:var(--bui-space-6)}.xl\:bui-mr-7{margin-right:var(--bui-space-7)}.xl\:bui-mr-8{margin-right:var(--bui-space-8)}.xl\:bui-mr-9{margin-right:var(--bui-space-9)}.xl\:bui-mr-10{margin-right:var(--bui-space-10)}.xl\:bui-mr-11{margin-right:var(--bui-space-11)}.xl\:bui-mr-12{margin-right:var(--bui-space-12)}.xl\:bui-mr-13{margin-right:var(--bui-space-13)}.xl\:bui-mr-14{margin-right:var(--bui-space-14)}}.bui-mt{margin-top:var(--mt)}.bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.bui-mt-1{margin-top:var(--bui-space-1)}.bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.bui-mt-2{margin-top:var(--bui-space-2)}.bui-mt-3{margin-top:var(--bui-space-3)}.bui-mt-4{margin-top:var(--bui-space-4)}.bui-mt-5{margin-top:var(--bui-space-5)}.bui-mt-6{margin-top:var(--bui-space-6)}.bui-mt-7{margin-top:var(--bui-space-7)}.bui-mt-8{margin-top:var(--bui-space-8)}.bui-mt-9{margin-top:var(--bui-space-9)}.bui-mt-10{margin-top:var(--bui-space-10)}.bui-mt-11{margin-top:var(--bui-space-11)}.bui-mt-12{margin-top:var(--bui-space-12)}.bui-mt-13{margin-top:var(--bui-space-13)}.bui-mt-14{margin-top:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mt{margin-top:var(--pt-xs)}.xs\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xs\:bui-mt-1{margin-top:var(--bui-space-1)}.xs\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xs\:bui-mt-2{margin-top:var(--bui-space-2)}.xs\:bui-mt-3{margin-top:var(--bui-space-3)}.xs\:bui-mt-4{margin-top:var(--bui-space-4)}.xs\:bui-mt-5{margin-top:var(--bui-space-5)}.xs\:bui-mt-6{margin-top:var(--bui-space-6)}.xs\:bui-mt-7{margin-top:var(--bui-space-7)}.xs\:bui-mt-8{margin-top:var(--bui-space-8)}.xs\:bui-mt-9{margin-top:var(--bui-space-9)}.xs\:bui-mt-10{margin-top:var(--bui-space-10)}.xs\:bui-mt-11{margin-top:var(--bui-space-11)}.xs\:bui-mt-12{margin-top:var(--bui-space-12)}.xs\:bui-mt-13{margin-top:var(--bui-space-13)}.xs\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mt{margin-top:var(--pt-sm)}.sm\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.sm\:bui-mt-1{margin-top:var(--bui-space-1)}.sm\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.sm\:bui-mt-2{margin-top:var(--bui-space-2)}.sm\:bui-mt-3{margin-top:var(--bui-space-3)}.sm\:bui-mt-4{margin-top:var(--bui-space-4)}.sm\:bui-mt-5{margin-top:var(--bui-space-5)}.sm\:bui-mt-6{margin-top:var(--bui-space-6)}.sm\:bui-mt-7{margin-top:var(--bui-space-7)}.sm\:bui-mt-8{margin-top:var(--bui-space-8)}.sm\:bui-mt-9{margin-top:var(--bui-space-9)}.sm\:bui-mt-10{margin-top:var(--bui-space-10)}.sm\:bui-mt-11{margin-top:var(--bui-space-11)}.sm\:bui-mt-12{margin-top:var(--bui-space-12)}.sm\:bui-mt-13{margin-top:var(--bui-space-13)}.sm\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mt{margin-top:var(--pt-md)}.md\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.md\:bui-mt-1{margin-top:var(--bui-space-1)}.md\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.md\:bui-mt-2{margin-top:var(--bui-space-2)}.md\:bui-mt-3{margin-top:var(--bui-space-3)}.md\:bui-mt-4{margin-top:var(--bui-space-4)}.md\:bui-mt-5{margin-top:var(--bui-space-5)}.md\:bui-mt-6{margin-top:var(--bui-space-6)}.md\:bui-mt-7{margin-top:var(--bui-space-7)}.md\:bui-mt-8{margin-top:var(--bui-space-8)}.md\:bui-mt-9{margin-top:var(--bui-space-9)}.md\:bui-mt-10{margin-top:var(--bui-space-10)}.md\:bui-mt-11{margin-top:var(--bui-space-11)}.md\:bui-mt-12{margin-top:var(--bui-space-12)}.md\:bui-mt-13{margin-top:var(--bui-space-13)}.md\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mt{margin-top:var(--pt-lg)}.lg\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.lg\:bui-mt-1{margin-top:var(--bui-space-1)}.lg\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.lg\:bui-mt-2{margin-top:var(--bui-space-2)}.lg\:bui-mt-3{margin-top:var(--bui-space-3)}.lg\:bui-mt-4{margin-top:var(--bui-space-4)}.lg\:bui-mt-5{margin-top:var(--bui-space-5)}.lg\:bui-mt-6{margin-top:var(--bui-space-6)}.lg\:bui-mt-7{margin-top:var(--bui-space-7)}.lg\:bui-mt-8{margin-top:var(--bui-space-8)}.lg\:bui-mt-9{margin-top:var(--bui-space-9)}.lg\:bui-mt-10{margin-top:var(--bui-space-10)}.lg\:bui-mt-11{margin-top:var(--bui-space-11)}.lg\:bui-mt-12{margin-top:var(--bui-space-12)}.lg\:bui-mt-13{margin-top:var(--bui-space-13)}.lg\:bui-mt-14{margin-top:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mt{margin-top:var(--pt-xl)}.xl\:bui-mt-0\.5{margin-top:var(--bui-space-0_5)}.xl\:bui-mt-1{margin-top:var(--bui-space-1)}.xl\:bui-mt-1\.5{margin-top:var(--bui-space-1_5)}.xl\:bui-mt-2{margin-top:var(--bui-space-2)}.xl\:bui-mt-3{margin-top:var(--bui-space-3)}.xl\:bui-mt-4{margin-top:var(--bui-space-4)}.xl\:bui-mt-5{margin-top:var(--bui-space-5)}.xl\:bui-mt-6{margin-top:var(--bui-space-6)}.xl\:bui-mt-7{margin-top:var(--bui-space-7)}.xl\:bui-mt-8{margin-top:var(--bui-space-8)}.xl\:bui-mt-9{margin-top:var(--bui-space-9)}.xl\:bui-mt-10{margin-top:var(--bui-space-10)}.xl\:bui-mt-11{margin-top:var(--bui-space-11)}.xl\:bui-mt-12{margin-top:var(--bui-space-12)}.xl\:bui-mt-13{margin-top:var(--bui-space-13)}.xl\:bui-mt-14{margin-top:var(--bui-space-14)}}.bui-mb{margin-bottom:var(--mb)}.bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.bui-mb-1{margin-bottom:var(--bui-space-1)}.bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.bui-mb-2{margin-bottom:var(--bui-space-2)}.bui-mb-3{margin-bottom:var(--bui-space-3)}.bui-mb-4{margin-bottom:var(--bui-space-4)}.bui-mb-5{margin-bottom:var(--bui-space-5)}.bui-mb-6{margin-bottom:var(--bui-space-6)}.bui-mb-7{margin-bottom:var(--bui-space-7)}.bui-mb-8{margin-bottom:var(--bui-space-8)}.bui-mb-9{margin-bottom:var(--bui-space-9)}.bui-mb-10{margin-bottom:var(--bui-space-10)}.bui-mb-11{margin-bottom:var(--bui-space-11)}.bui-mb-12{margin-bottom:var(--bui-space-12)}.bui-mb-13{margin-bottom:var(--bui-space-13)}.bui-mb-14{margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mb{margin-bottom:var(--pb-xs)}.xs\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xs\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xs\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xs\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xs\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xs\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xs\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xs\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xs\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xs\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xs\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xs\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xs\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xs\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xs\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xs\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mb{margin-bottom:var(--pb-sm)}.sm\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.sm\:bui-mb-1{margin-bottom:var(--bui-space-1)}.sm\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.sm\:bui-mb-2{margin-bottom:var(--bui-space-2)}.sm\:bui-mb-3{margin-bottom:var(--bui-space-3)}.sm\:bui-mb-4{margin-bottom:var(--bui-space-4)}.sm\:bui-mb-5{margin-bottom:var(--bui-space-5)}.sm\:bui-mb-6{margin-bottom:var(--bui-space-6)}.sm\:bui-mb-7{margin-bottom:var(--bui-space-7)}.sm\:bui-mb-8{margin-bottom:var(--bui-space-8)}.sm\:bui-mb-9{margin-bottom:var(--bui-space-9)}.sm\:bui-mb-10{margin-bottom:var(--bui-space-10)}.sm\:bui-mb-11{margin-bottom:var(--bui-space-11)}.sm\:bui-mb-12{margin-bottom:var(--bui-space-12)}.sm\:bui-mb-13{margin-bottom:var(--bui-space-13)}.sm\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mb{margin-bottom:var(--pb-md)}.md\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.md\:bui-mb-1{margin-bottom:var(--bui-space-1)}.md\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.md\:bui-mb-2{margin-bottom:var(--bui-space-2)}.md\:bui-mb-3{margin-bottom:var(--bui-space-3)}.md\:bui-mb-4{margin-bottom:var(--bui-space-4)}.md\:bui-mb-5{margin-bottom:var(--bui-space-5)}.md\:bui-mb-6{margin-bottom:var(--bui-space-6)}.md\:bui-mb-7{margin-bottom:var(--bui-space-7)}.md\:bui-mb-8{margin-bottom:var(--bui-space-8)}.md\:bui-mb-9{margin-bottom:var(--bui-space-9)}.md\:bui-mb-10{margin-bottom:var(--bui-space-10)}.md\:bui-mb-11{margin-bottom:var(--bui-space-11)}.md\:bui-mb-12{margin-bottom:var(--bui-space-12)}.md\:bui-mb-13{margin-bottom:var(--bui-space-13)}.md\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mb{margin-bottom:var(--pb-lg)}.lg\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.lg\:bui-mb-1{margin-bottom:var(--bui-space-1)}.lg\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.lg\:bui-mb-2{margin-bottom:var(--bui-space-2)}.lg\:bui-mb-3{margin-bottom:var(--bui-space-3)}.lg\:bui-mb-4{margin-bottom:var(--bui-space-4)}.lg\:bui-mb-5{margin-bottom:var(--bui-space-5)}.lg\:bui-mb-6{margin-bottom:var(--bui-space-6)}.lg\:bui-mb-7{margin-bottom:var(--bui-space-7)}.lg\:bui-mb-8{margin-bottom:var(--bui-space-8)}.lg\:bui-mb-9{margin-bottom:var(--bui-space-9)}.lg\:bui-mb-10{margin-bottom:var(--bui-space-10)}.lg\:bui-mb-11{margin-bottom:var(--bui-space-11)}.lg\:bui-mb-12{margin-bottom:var(--bui-space-12)}.lg\:bui-mb-13{margin-bottom:var(--bui-space-13)}.lg\:bui-mb-14{margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mb{margin-bottom:var(--pb-xl)}.xl\:bui-mb-0\.5{margin-bottom:var(--bui-space-0_5)}.xl\:bui-mb-1{margin-bottom:var(--bui-space-1)}.xl\:bui-mb-1\.5{margin-bottom:var(--bui-space-1_5)}.xl\:bui-mb-2{margin-bottom:var(--bui-space-2)}.xl\:bui-mb-3{margin-bottom:var(--bui-space-3)}.xl\:bui-mb-4{margin-bottom:var(--bui-space-4)}.xl\:bui-mb-5{margin-bottom:var(--bui-space-5)}.xl\:bui-mb-6{margin-bottom:var(--bui-space-6)}.xl\:bui-mb-7{margin-bottom:var(--bui-space-7)}.xl\:bui-mb-8{margin-bottom:var(--bui-space-8)}.xl\:bui-mb-9{margin-bottom:var(--bui-space-9)}.xl\:bui-mb-10{margin-bottom:var(--bui-space-10)}.xl\:bui-mb-11{margin-bottom:var(--bui-space-11)}.xl\:bui-mb-12{margin-bottom:var(--bui-space-12)}.xl\:bui-mb-13{margin-bottom:var(--bui-space-13)}.xl\:bui-mb-14{margin-bottom:var(--bui-space-14)}}.bui-my{margin-top:var(--my);margin-bottom:var(--my)}.bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}@media (width>=640px){.xs\:bui-my{margin-top:var(--py-xs);margin-bottom:var(--py-xs)}.xs\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xs\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xs\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xs\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xs\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xs\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xs\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xs\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xs\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xs\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xs\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xs\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xs\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xs\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xs\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xs\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-my{margin-top:var(--py-sm);margin-bottom:var(--py-sm)}.sm\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.sm\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.sm\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.sm\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.sm\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.sm\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.sm\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.sm\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.sm\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.sm\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.sm\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.sm\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.sm\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.sm\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.sm\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.sm\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-my{margin-top:var(--py-md);margin-bottom:var(--py-md)}.md\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.md\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.md\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.md\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.md\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.md\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.md\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.md\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.md\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.md\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.md\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.md\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.md\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.md\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.md\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.md\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-my{margin-top:var(--py-lg);margin-bottom:var(--py-lg)}.lg\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.lg\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.lg\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.lg\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.lg\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.lg\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.lg\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.lg\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.lg\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.lg\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.lg\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.lg\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.lg\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.lg\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.lg\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.lg\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-my{margin-top:var(--py-xl);margin-bottom:var(--py-xl)}.xl\:bui-my-0\.5{margin-top:var(--bui-space-0_5);margin-bottom:var(--bui-space-0_5)}.xl\:bui-my-1{margin-top:var(--bui-space-1);margin-bottom:var(--bui-space-1)}.xl\:bui-my-1\.5{margin-top:var(--bui-space-1_5);margin-bottom:var(--bui-space-1_5)}.xl\:bui-my-2{margin-top:var(--bui-space-2);margin-bottom:var(--bui-space-2)}.xl\:bui-my-3{margin-top:var(--bui-space-3);margin-bottom:var(--bui-space-3)}.xl\:bui-my-4{margin-top:var(--bui-space-4);margin-bottom:var(--bui-space-4)}.xl\:bui-my-5{margin-top:var(--bui-space-5);margin-bottom:var(--bui-space-5)}.xl\:bui-my-6{margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6)}.xl\:bui-my-7{margin-top:var(--bui-space-7);margin-bottom:var(--bui-space-7)}.xl\:bui-my-8{margin-top:var(--bui-space-8);margin-bottom:var(--bui-space-8)}.xl\:bui-my-9{margin-top:var(--bui-space-9);margin-bottom:var(--bui-space-9)}.xl\:bui-my-10{margin-top:var(--bui-space-10);margin-bottom:var(--bui-space-10)}.xl\:bui-my-11{margin-top:var(--bui-space-11);margin-bottom:var(--bui-space-11)}.xl\:bui-my-12{margin-top:var(--bui-space-12);margin-bottom:var(--bui-space-12)}.xl\:bui-my-13{margin-top:var(--bui-space-13);margin-bottom:var(--bui-space-13)}.xl\:bui-my-14{margin-top:var(--bui-space-14);margin-bottom:var(--bui-space-14)}}.bui-mx{margin-left:var(--mx);margin-right:var(--mx)}.bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}@media (width>=640px){.xs\:bui-mx{margin-left:var(--px-xs);margin-right:var(--px-xs)}.xs\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xs\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xs\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xs\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xs\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xs\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xs\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xs\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xs\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xs\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xs\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xs\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xs\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xs\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xs\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xs\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-mx{margin-left:var(--px-sm);margin-right:var(--px-sm)}.sm\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.sm\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.sm\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.sm\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.sm\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.sm\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.sm\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.sm\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.sm\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.sm\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.sm\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.sm\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.sm\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.sm\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.sm\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.sm\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-mx{margin-left:var(--px-md);margin-right:var(--px-md)}.md\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.md\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.md\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.md\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.md\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.md\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.md\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.md\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.md\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.md\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.md\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.md\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.md\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.md\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.md\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.md\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-mx{margin-left:var(--px-lg);margin-right:var(--px-lg)}.lg\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.lg\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.lg\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.lg\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.lg\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.lg\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.lg\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.lg\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.lg\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.lg\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.lg\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.lg\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.lg\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.lg\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.lg\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.lg\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-mx{margin-left:var(--px-xl);margin-right:var(--px-xl)}.xl\:bui-mx-0\.5{margin-left:var(--bui-space-0_5);margin-right:var(--bui-space-0_5)}.xl\:bui-mx-1{margin-left:var(--bui-space-1);margin-right:var(--bui-space-1)}.xl\:bui-mx-1\.5{margin-left:var(--bui-space-1_5);margin-right:var(--bui-space-1_5)}.xl\:bui-mx-2{margin-left:var(--bui-space-2);margin-right:var(--bui-space-2)}.xl\:bui-mx-3{margin-left:var(--bui-space-3);margin-right:var(--bui-space-3)}.xl\:bui-mx-4{margin-left:var(--bui-space-4);margin-right:var(--bui-space-4)}.xl\:bui-mx-5{margin-left:var(--bui-space-5);margin-right:var(--bui-space-5)}.xl\:bui-mx-6{margin-left:var(--bui-space-6);margin-right:var(--bui-space-6)}.xl\:bui-mx-7{margin-left:var(--bui-space-7);margin-right:var(--bui-space-7)}.xl\:bui-mx-8{margin-left:var(--bui-space-8);margin-right:var(--bui-space-8)}.xl\:bui-mx-9{margin-left:var(--bui-space-9);margin-right:var(--bui-space-9)}.xl\:bui-mx-10{margin-left:var(--bui-space-10);margin-right:var(--bui-space-10)}.xl\:bui-mx-11{margin-left:var(--bui-space-11);margin-right:var(--bui-space-11)}.xl\:bui-mx-12{margin-left:var(--bui-space-12);margin-right:var(--bui-space-12)}.xl\:bui-mx-13{margin-left:var(--bui-space-13);margin-right:var(--bui-space-13)}.xl\:bui-mx-14{margin-left:var(--bui-space-14);margin-right:var(--bui-space-14)}}.bui-display-none{display:none}.bui-display-inline{display:inline}.bui-display-inline-block{display:inline-block}.bui-display-block{display:block}@media (width>=640px){.xs\:bui-display-none{display:none}.xs\:bui-display-inline{display:inline}.xs\:bui-display-inline-block{display:inline-block}.xs\:bui-display-block{display:block}}@media (width>=768px){.sm\:bui-display-none{display:none}.sm\:bui-display-inline{display:inline}.sm\:bui-display-inline-block{display:inline-block}.sm\:bui-display-block{display:block}}@media (width>=1024px){.md\:bui-display-none{display:none}.md\:bui-display-inline{display:inline}.md\:bui-display-inline-block{display:inline-block}.md\:bui-display-block{display:block}}@media (width>=1280px){.lg\:bui-display-none{display:none}.lg\:bui-display-inline{display:inline}.lg\:bui-display-inline-block{display:inline-block}.lg\:bui-display-block{display:block}}@media (width>=1536px){.xl\:bui-display-none{display:none}.xl\:bui-display-inline{display:inline}.xl\:bui-display-inline-block{display:inline-block}.xl\:bui-display-block{display:block}}.bui-w{width:var(--width)}.bui-min-w{min-width:var(--min-width)}.bui-max-w{max-width:var(--max-width)}@media (width>=640px){.xs\:bui-w{width:var(--width)}.xs\:bui-min-w{min-width:var(--min-width)}.xs\:bui-max-w{max-width:var(--max-width)}}@media (width>=768px){.sm\:bui-w{width:var(--width)}.sm\:bui-min-w{min-width:var(--min-width)}.sm\:bui-max-w{max-width:var(--max-width)}}@media (width>=1024px){.md\:bui-w{width:var(--width)}.md\:bui-min-w{min-width:var(--min-width)}.md\:bui-max-w{max-width:var(--max-width)}}@media (width>=1280px){.lg\:bui-w{width:var(--width)}.lg\:bui-min-w{min-width:var(--min-width)}.lg\:bui-max-w{max-width:var(--max-width)}}@media (width>=1536px){.xl\:bui-w{width:var(--width)}.xl\:bui-min-w{min-width:var(--min-width)}.xl\:bui-max-w{max-width:var(--max-width)}}.bui-h{height:var(--height)}.bui-min-h{min-height:var(--min-height)}.bui-max-h{max-height:var(--max-height)}@media (width>=640px){.xs\:bui-h{height:var(--height)}.xs\:bui-min-h{min-height:var(--min-height)}.xs\:bui-max-h{max-height:var(--max-height)}}@media (width>=768px){.sm\:bui-h{height:var(--height)}.sm\:bui-min-h{min-height:var(--min-height)}.sm\:bui-max-h{max-height:var(--max-height)}}@media (width>=1024px){.md\:bui-h{height:var(--height)}.md\:bui-min-h{min-height:var(--min-height)}.md\:bui-max-h{max-height:var(--max-height)}}@media (width>=1280px){.lg\:bui-h{height:var(--height)}.lg\:bui-min-h{min-height:var(--min-height)}.lg\:bui-max-h{max-height:var(--max-height)}}@media (width>=1536px){.xl\:bui-h{height:var(--height)}.xl\:bui-min-h{min-height:var(--min-height)}.xl\:bui-max-h{max-height:var(--max-height)}}.bui-position-absolute{position:absolute}.bui-position-fixed{position:fixed}.bui-position-sticky{position:sticky}.bui-position-relative{position:relative}.bui-position-static{position:static}@media (width>=640px){.xs\:bui-position-absolute{position:absolute}.xs\:bui-position-fixed{position:fixed}.xs\:bui-position-sticky{position:sticky}.xs\:bui-position-relative{position:relative}.xs\:bui-position-static{position:static}}@media (width>=768px){.sm\:bui-position-absolute{position:absolute}.sm\:bui-position-fixed{position:fixed}.sm\:bui-position-sticky{position:sticky}.sm\:bui-position-relative{position:relative}.sm\:bui-position-static{position:static}}@media (width>=1024px){.md\:bui-position-absolute{position:absolute}.md\:bui-position-fixed{position:fixed}.md\:bui-position-sticky{position:sticky}.md\:bui-position-relative{position:relative}.md\:bui-position-static{position:static}}@media (width>=1280px){.lg\:bui-position-absolute{position:absolute}.lg\:bui-position-fixed{position:fixed}.lg\:bui-position-sticky{position:sticky}.lg\:bui-position-relative{position:relative}.lg\:bui-position-static{position:static}}@media (width>=1536px){.xl\:bui-position-absolute{position:absolute}.xl\:bui-position-fixed{position:fixed}.xl\:bui-position-sticky{position:sticky}.xl\:bui-position-relative{position:relative}.xl\:bui-position-static{position:static}}.bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.bui-col-span-1{grid-column:span 1/span 1}.bui-col-span-2{grid-column:span 2/span 2}.bui-col-span-3{grid-column:span 3/span 3}.bui-col-span-4{grid-column:span 4/span 4}.bui-col-span-5{grid-column:span 5/span 5}.bui-col-span-6{grid-column:span 6/span 6}.bui-col-span-7{grid-column:span 7/span 7}.bui-col-span-8{grid-column:span 8/span 8}.bui-col-span-9{grid-column:span 9/span 9}.bui-col-span-10{grid-column:span 10/span 10}.bui-col-span-11{grid-column:span 11/span 11}.bui-col-span-12{grid-column:span 12/span 12}.bui-col-span-auto{grid-column:span auto/span auto}.bui-col-start-1{grid-column-start:1}.bui-col-start-2{grid-column-start:2}.bui-col-start-3{grid-column-start:3}.bui-col-start-4{grid-column-start:4}.bui-col-start-5{grid-column-start:5}.bui-col-start-6{grid-column-start:6}.bui-col-start-7{grid-column-start:7}.bui-col-start-8{grid-column-start:8}.bui-col-start-9{grid-column-start:9}.bui-col-start-10{grid-column-start:10}.bui-col-start-11{grid-column-start:11}.bui-col-start-12{grid-column-start:12}.bui-col-start-13{grid-column-start:13}.bui-col-start-auto{grid-column-start:auto}.bui-col-end-1{grid-column-end:1}.bui-col-end-2{grid-column-end:2}.bui-col-end-3{grid-column-end:3}.bui-col-end-4{grid-column-end:4}.bui-col-end-5{grid-column-end:5}.bui-col-end-6{grid-column-end:6}.bui-col-end-7{grid-column-end:7}.bui-col-end-8{grid-column-end:8}.bui-col-end-9{grid-column-end:9}.bui-col-end-10{grid-column-end:10}.bui-col-end-11{grid-column-end:11}.bui-col-end-12{grid-column-end:12}.bui-col-end-13{grid-column-end:13}.bui-col-end-auto{grid-column-end:auto}.bui-row-span-1{grid-row:span 1/span 1}.bui-row-span-2{grid-row:span 2/span 2}.bui-row-span-3{grid-row:span 3/span 3}.bui-row-span-4{grid-row:span 4/span 4}.bui-row-span-5{grid-row:span 5/span 5}.bui-row-span-6{grid-row:span 6/span 6}.bui-row-span-7{grid-row:span 7/span 7}.bui-row-span-8{grid-row:span 8/span 8}.bui-row-span-9{grid-row:span 9/span 9}.bui-row-span-10{grid-row:span 10/span 10}.bui-row-span-11{grid-row:span 11/span 11}.bui-row-span-12{grid-row:span 12/span 12}.bui-row-span-auto{grid-row:span auto/span auto}@media (width>=640px){.xs\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xs\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xs\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xs\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xs\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xs\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xs\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xs\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xs\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xs\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xs\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xs\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xs\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xs\:bui-col-span-1{grid-column:span 1/span 1}.xs\:bui-col-span-2{grid-column:span 2/span 2}.xs\:bui-col-span-3{grid-column:span 3/span 3}.xs\:bui-col-span-4{grid-column:span 4/span 4}.xs\:bui-col-span-5{grid-column:span 5/span 5}.xs\:bui-col-span-6{grid-column:span 6/span 6}.xs\:bui-col-span-7{grid-column:span 7/span 7}.xs\:bui-col-span-8{grid-column:span 8/span 8}.xs\:bui-col-span-9{grid-column:span 9/span 9}.xs\:bui-col-span-10{grid-column:span 10/span 10}.xs\:bui-col-span-11{grid-column:span 11/span 11}.xs\:bui-col-span-12{grid-column:span 12/span 12}.xs\:bui-col-span-auto{grid-column:span auto/span auto}.xs\:bui-col-start-1{grid-column-start:1}.xs\:bui-col-start-2{grid-column-start:2}.xs\:bui-col-start-3{grid-column-start:3}.xs\:bui-col-start-4{grid-column-start:4}.xs\:bui-col-start-5{grid-column-start:5}.xs\:bui-col-start-6{grid-column-start:6}.xs\:bui-col-start-7{grid-column-start:7}.xs\:bui-col-start-8{grid-column-start:8}.xs\:bui-col-start-9{grid-column-start:9}.xs\:bui-col-start-10{grid-column-start:10}.xs\:bui-col-start-11{grid-column-start:11}.xs\:bui-col-start-12{grid-column-start:12}.xs\:bui-col-start-13{grid-column-start:13}.xs\:bui-col-start-auto{grid-column-start:auto}.xs\:bui-col-end-1{grid-column-end:1}.xs\:bui-col-end-2{grid-column-end:2}.xs\:bui-col-end-3{grid-column-end:3}.xs\:bui-col-end-4{grid-column-end:4}.xs\:bui-col-end-5{grid-column-end:5}.xs\:bui-col-end-6{grid-column-end:6}.xs\:bui-col-end-7{grid-column-end:7}.xs\:bui-col-end-8{grid-column-end:8}.xs\:bui-col-end-9{grid-column-end:9}.xs\:bui-col-end-10{grid-column-end:10}.xs\:bui-col-end-11{grid-column-end:11}.xs\:bui-col-end-12{grid-column-end:12}.xs\:bui-col-end-13{grid-column-end:13}.xs\:bui-col-end-auto{grid-column-end:auto}.xs\:bui-row-span-1{grid-row:span 1/span 1}.xs\:bui-row-span-2{grid-row:span 2/span 2}.xs\:bui-row-span-3{grid-row:span 3/span 3}.xs\:bui-row-span-4{grid-row:span 4/span 4}.xs\:bui-row-span-5{grid-row:span 5/span 5}.xs\:bui-row-span-6{grid-row:span 6/span 6}.xs\:bui-row-span-7{grid-row:span 7/span 7}.xs\:bui-row-span-8{grid-row:span 8/span 8}.xs\:bui-row-span-9{grid-row:span 9/span 9}.xs\:bui-row-span-10{grid-row:span 10/span 10}.xs\:bui-row-span-11{grid-row:span 11/span 11}.xs\:bui-row-span-12{grid-row:span 12/span 12}.xs\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=768px){.sm\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.sm\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.sm\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.sm\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.sm\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.sm\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.sm\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.sm\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.sm\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.sm\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.sm\:bui-col-span-1{grid-column:span 1/span 1}.sm\:bui-col-span-2{grid-column:span 2/span 2}.sm\:bui-col-span-3{grid-column:span 3/span 3}.sm\:bui-col-span-4{grid-column:span 4/span 4}.sm\:bui-col-span-5{grid-column:span 5/span 5}.sm\:bui-col-span-6{grid-column:span 6/span 6}.sm\:bui-col-span-7{grid-column:span 7/span 7}.sm\:bui-col-span-8{grid-column:span 8/span 8}.sm\:bui-col-span-9{grid-column:span 9/span 9}.sm\:bui-col-span-10{grid-column:span 10/span 10}.sm\:bui-col-span-11{grid-column:span 11/span 11}.sm\:bui-col-span-12{grid-column:span 12/span 12}.sm\:bui-col-span-auto{grid-column:span auto/span auto}.sm\:bui-col-start-1{grid-column-start:1}.sm\:bui-col-start-2{grid-column-start:2}.sm\:bui-col-start-3{grid-column-start:3}.sm\:bui-col-start-4{grid-column-start:4}.sm\:bui-col-start-5{grid-column-start:5}.sm\:bui-col-start-6{grid-column-start:6}.sm\:bui-col-start-7{grid-column-start:7}.sm\:bui-col-start-8{grid-column-start:8}.sm\:bui-col-start-9{grid-column-start:9}.sm\:bui-col-start-10{grid-column-start:10}.sm\:bui-col-start-11{grid-column-start:11}.sm\:bui-col-start-12{grid-column-start:12}.sm\:bui-col-start-13{grid-column-start:13}.sm\:bui-col-start-auto{grid-column-start:auto}.sm\:bui-col-end-1{grid-column-end:1}.sm\:bui-col-end-2{grid-column-end:2}.sm\:bui-col-end-3{grid-column-end:3}.sm\:bui-col-end-4{grid-column-end:4}.sm\:bui-col-end-5{grid-column-end:5}.sm\:bui-col-end-6{grid-column-end:6}.sm\:bui-col-end-7{grid-column-end:7}.sm\:bui-col-end-8{grid-column-end:8}.sm\:bui-col-end-9{grid-column-end:9}.sm\:bui-col-end-10{grid-column-end:10}.sm\:bui-col-end-11{grid-column-end:11}.sm\:bui-col-end-12{grid-column-end:12}.sm\:bui-col-end-13{grid-column-end:13}.sm\:bui-col-end-auto{grid-column-end:auto}.sm\:bui-row-span-1{grid-row:span 1/span 1}.sm\:bui-row-span-2{grid-row:span 2/span 2}.sm\:bui-row-span-3{grid-row:span 3/span 3}.sm\:bui-row-span-4{grid-row:span 4/span 4}.sm\:bui-row-span-5{grid-row:span 5/span 5}.sm\:bui-row-span-6{grid-row:span 6/span 6}.sm\:bui-row-span-7{grid-row:span 7/span 7}.sm\:bui-row-span-8{grid-row:span 8/span 8}.sm\:bui-row-span-9{grid-row:span 9/span 9}.sm\:bui-row-span-10{grid-row:span 10/span 10}.sm\:bui-row-span-11{grid-row:span 11/span 11}.sm\:bui-row-span-12{grid-row:span 12/span 12}.sm\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1024px){.md\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.md\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.md\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.md\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.md\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.md\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.md\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.md\:bui-col-span-1{grid-column:span 1/span 1}.md\:bui-col-span-2{grid-column:span 2/span 2}.md\:bui-col-span-3{grid-column:span 3/span 3}.md\:bui-col-span-4{grid-column:span 4/span 4}.md\:bui-col-span-5{grid-column:span 5/span 5}.md\:bui-col-span-6{grid-column:span 6/span 6}.md\:bui-col-span-7{grid-column:span 7/span 7}.md\:bui-col-span-8{grid-column:span 8/span 8}.md\:bui-col-span-9{grid-column:span 9/span 9}.md\:bui-col-span-10{grid-column:span 10/span 10}.md\:bui-col-span-11{grid-column:span 11/span 11}.md\:bui-col-span-12{grid-column:span 12/span 12}.md\:bui-col-span-auto{grid-column:span auto/span auto}.md\:bui-col-start-1{grid-column-start:1}.md\:bui-col-start-2{grid-column-start:2}.md\:bui-col-start-3{grid-column-start:3}.md\:bui-col-start-4{grid-column-start:4}.md\:bui-col-start-5{grid-column-start:5}.md\:bui-col-start-6{grid-column-start:6}.md\:bui-col-start-7{grid-column-start:7}.md\:bui-col-start-8{grid-column-start:8}.md\:bui-col-start-9{grid-column-start:9}.md\:bui-col-start-10{grid-column-start:10}.md\:bui-col-start-11{grid-column-start:11}.md\:bui-col-start-12{grid-column-start:12}.md\:bui-col-start-13{grid-column-start:13}.md\:bui-col-start-auto{grid-column-start:auto}.md\:bui-col-end-1{grid-column-end:1}.md\:bui-col-end-2{grid-column-end:2}.md\:bui-col-end-3{grid-column-end:3}.md\:bui-col-end-4{grid-column-end:4}.md\:bui-col-end-5{grid-column-end:5}.md\:bui-col-end-6{grid-column-end:6}.md\:bui-col-end-7{grid-column-end:7}.md\:bui-col-end-8{grid-column-end:8}.md\:bui-col-end-9{grid-column-end:9}.md\:bui-col-end-10{grid-column-end:10}.md\:bui-col-end-11{grid-column-end:11}.md\:bui-col-end-12{grid-column-end:12}.md\:bui-col-end-13{grid-column-end:13}.md\:bui-col-end-auto{grid-column-end:auto}.md\:bui-row-span-1{grid-row:span 1/span 1}.md\:bui-row-span-2{grid-row:span 2/span 2}.md\:bui-row-span-3{grid-row:span 3/span 3}.md\:bui-row-span-4{grid-row:span 4/span 4}.md\:bui-row-span-5{grid-row:span 5/span 5}.md\:bui-row-span-6{grid-row:span 6/span 6}.md\:bui-row-span-7{grid-row:span 7/span 7}.md\:bui-row-span-8{grid-row:span 8/span 8}.md\:bui-row-span-9{grid-row:span 9/span 9}.md\:bui-row-span-10{grid-row:span 10/span 10}.md\:bui-row-span-11{grid-row:span 11/span 11}.md\:bui-row-span-12{grid-row:span 12/span 12}.md\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1280px){.lg\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.lg\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.lg\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.lg\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.lg\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.lg\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.lg\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.lg\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.lg\:bui-col-span-1{grid-column:span 1/span 1}.lg\:bui-col-span-2{grid-column:span 2/span 2}.lg\:bui-col-span-3{grid-column:span 3/span 3}.lg\:bui-col-span-4{grid-column:span 4/span 4}.lg\:bui-col-span-5{grid-column:span 5/span 5}.lg\:bui-col-span-6{grid-column:span 6/span 6}.lg\:bui-col-span-7{grid-column:span 7/span 7}.lg\:bui-col-span-8{grid-column:span 8/span 8}.lg\:bui-col-span-9{grid-column:span 9/span 9}.lg\:bui-col-span-10{grid-column:span 10/span 10}.lg\:bui-col-span-11{grid-column:span 11/span 11}.lg\:bui-col-span-12{grid-column:span 12/span 12}.lg\:bui-col-span-auto{grid-column:span auto/span auto}.lg\:bui-col-start-1{grid-column-start:1}.lg\:bui-col-start-2{grid-column-start:2}.lg\:bui-col-start-3{grid-column-start:3}.lg\:bui-col-start-4{grid-column-start:4}.lg\:bui-col-start-5{grid-column-start:5}.lg\:bui-col-start-6{grid-column-start:6}.lg\:bui-col-start-7{grid-column-start:7}.lg\:bui-col-start-8{grid-column-start:8}.lg\:bui-col-start-9{grid-column-start:9}.lg\:bui-col-start-10{grid-column-start:10}.lg\:bui-col-start-11{grid-column-start:11}.lg\:bui-col-start-12{grid-column-start:12}.lg\:bui-col-start-13{grid-column-start:13}.lg\:bui-col-start-auto{grid-column-start:auto}.lg\:bui-col-end-1{grid-column-end:1}.lg\:bui-col-end-2{grid-column-end:2}.lg\:bui-col-end-3{grid-column-end:3}.lg\:bui-col-end-4{grid-column-end:4}.lg\:bui-col-end-5{grid-column-end:5}.lg\:bui-col-end-6{grid-column-end:6}.lg\:bui-col-end-7{grid-column-end:7}.lg\:bui-col-end-8{grid-column-end:8}.lg\:bui-col-end-9{grid-column-end:9}.lg\:bui-col-end-10{grid-column-end:10}.lg\:bui-col-end-11{grid-column-end:11}.lg\:bui-col-end-12{grid-column-end:12}.lg\:bui-col-end-13{grid-column-end:13}.lg\:bui-col-end-auto{grid-column-end:auto}.lg\:bui-row-span-1{grid-row:span 1/span 1}.lg\:bui-row-span-2{grid-row:span 2/span 2}.lg\:bui-row-span-3{grid-row:span 3/span 3}.lg\:bui-row-span-4{grid-row:span 4/span 4}.lg\:bui-row-span-5{grid-row:span 5/span 5}.lg\:bui-row-span-6{grid-row:span 6/span 6}.lg\:bui-row-span-7{grid-row:span 7/span 7}.lg\:bui-row-span-8{grid-row:span 8/span 8}.lg\:bui-row-span-9{grid-row:span 9/span 9}.lg\:bui-row-span-10{grid-row:span 10/span 10}.lg\:bui-row-span-11{grid-row:span 11/span 11}.lg\:bui-row-span-12{grid-row:span 12/span 12}.lg\:bui-row-span-auto{grid-row:span auto/span auto}}@media (width>=1536px){.xl\:bui-columns-1{grid-template-columns:repeat(1,minmax(0,1fr))}.xl\:bui-columns-2{grid-template-columns:repeat(2,minmax(0,1fr))}.xl\:bui-columns-3{grid-template-columns:repeat(3,minmax(0,1fr))}.xl\:bui-columns-4{grid-template-columns:repeat(4,minmax(0,1fr))}.xl\:bui-columns-5{grid-template-columns:repeat(5,minmax(0,1fr))}.xl\:bui-columns-6{grid-template-columns:repeat(6,minmax(0,1fr))}.xl\:bui-columns-7{grid-template-columns:repeat(7,minmax(0,1fr))}.xl\:bui-columns-8{grid-template-columns:repeat(8,minmax(0,1fr))}.xl\:bui-columns-9{grid-template-columns:repeat(9,minmax(0,1fr))}.xl\:bui-columns-10{grid-template-columns:repeat(10,minmax(0,1fr))}.xl\:bui-columns-11{grid-template-columns:repeat(11,minmax(0,1fr))}.xl\:bui-columns-12{grid-template-columns:repeat(12,minmax(0,1fr))}.xl\:bui-columns-auto{grid-template-columns:repeat(auto-fit,minmax(0,1fr))}.xl\:bui-col-span-1{grid-column:span 1/span 1}.xl\:bui-col-span-2{grid-column:span 2/span 2}.xl\:bui-col-span-3{grid-column:span 3/span 3}.xl\:bui-col-span-4{grid-column:span 4/span 4}.xl\:bui-col-span-5{grid-column:span 5/span 5}.xl\:bui-col-span-6{grid-column:span 6/span 6}.xl\:bui-col-span-7{grid-column:span 7/span 7}.xl\:bui-col-span-8{grid-column:span 8/span 8}.xl\:bui-col-span-9{grid-column:span 9/span 9}.xl\:bui-col-span-10{grid-column:span 10/span 10}.xl\:bui-col-span-11{grid-column:span 11/span 11}.xl\:bui-col-span-12{grid-column:span 12/span 12}.xl\:bui-col-span-auto{grid-column:span auto/span auto}.xl\:bui-col-start-1{grid-column-start:1}.xl\:bui-col-start-2{grid-column-start:2}.xl\:bui-col-start-3{grid-column-start:3}.xl\:bui-col-start-4{grid-column-start:4}.xl\:bui-col-start-5{grid-column-start:5}.xl\:bui-col-start-6{grid-column-start:6}.xl\:bui-col-start-7{grid-column-start:7}.xl\:bui-col-start-8{grid-column-start:8}.xl\:bui-col-start-9{grid-column-start:9}.xl\:bui-col-start-10{grid-column-start:10}.xl\:bui-col-start-11{grid-column-start:11}.xl\:bui-col-start-12{grid-column-start:12}.xl\:bui-col-start-13{grid-column-start:13}.xl\:bui-col-start-auto{grid-column-start:auto}.xl\:bui-col-end-1{grid-column-end:1}.xl\:bui-col-end-2{grid-column-end:2}.xl\:bui-col-end-3{grid-column-end:3}.xl\:bui-col-end-4{grid-column-end:4}.xl\:bui-col-end-5{grid-column-end:5}.xl\:bui-col-end-6{grid-column-end:6}.xl\:bui-col-end-7{grid-column-end:7}.xl\:bui-col-end-8{grid-column-end:8}.xl\:bui-col-end-9{grid-column-end:9}.xl\:bui-col-end-10{grid-column-end:10}.xl\:bui-col-end-11{grid-column-end:11}.xl\:bui-col-end-12{grid-column-end:12}.xl\:bui-col-end-13{grid-column-end:13}.xl\:bui-col-end-auto{grid-column-end:auto}.xl\:bui-row-span-1{grid-row:span 1/span 1}.xl\:bui-row-span-2{grid-row:span 2/span 2}.xl\:bui-row-span-3{grid-row:span 3/span 3}.xl\:bui-row-span-4{grid-row:span 4/span 4}.xl\:bui-row-span-5{grid-row:span 5/span 5}.xl\:bui-row-span-6{grid-row:span 6/span 6}.xl\:bui-row-span-7{grid-row:span 7/span 7}.xl\:bui-row-span-8{grid-row:span 8/span 8}.xl\:bui-row-span-9{grid-row:span 9/span 9}.xl\:bui-row-span-10{grid-row:span 10/span 10}.xl\:bui-row-span-11{grid-row:span 11/span 11}.xl\:bui-row-span-12{grid-row:span 12/span 12}.xl\:bui-row-span-auto{grid-row:span auto/span auto}}.bui-gap{gap:var(--gap)}.bui-gap-0\.5{gap:var(--bui-space-0_5)}.bui-gap-1{gap:var(--bui-space-1)}.bui-gap-1\.5{gap:var(--bui-space-1_5)}.bui-gap-2{gap:var(--bui-space-2)}.bui-gap-3{gap:var(--bui-space-3)}.bui-gap-4{gap:var(--bui-space-4)}.bui-gap-5{gap:var(--bui-space-5)}.bui-gap-6{gap:var(--bui-space-6)}.bui-gap-7{gap:var(--bui-space-7)}.bui-gap-8{gap:var(--bui-space-8)}.bui-gap-9{gap:var(--bui-space-9)}.bui-gap-10{gap:var(--bui-space-10)}.bui-gap-11{gap:var(--bui-space-11)}.bui-gap-12{gap:var(--bui-space-12)}.bui-gap-13{gap:var(--bui-space-13)}.bui-gap-14{gap:var(--bui-space-14)}@media (width>=640px){.xs\:bui-gap{gap:var(--gap-xs)}.xs\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xs\:bui-gap-1{gap:var(--bui-space-1)}.xs\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xs\:bui-gap-2{gap:var(--bui-space-2)}.xs\:bui-gap-3{gap:var(--bui-space-3)}.xs\:bui-gap-4{gap:var(--bui-space-4)}.xs\:bui-gap-5{gap:var(--bui-space-5)}.xs\:bui-gap-6{gap:var(--bui-space-6)}.xs\:bui-gap-7{gap:var(--bui-space-7)}.xs\:bui-gap-8{gap:var(--bui-space-8)}.xs\:bui-gap-9{gap:var(--bui-space-9)}.xs\:bui-gap-10{gap:var(--bui-space-10)}.xs\:bui-gap-11{gap:var(--bui-space-11)}.xs\:bui-gap-12{gap:var(--bui-space-12)}.xs\:bui-gap-13{gap:var(--bui-space-13)}.xs\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=768px){.sm\:bui-gap{gap:var(--gap-sm)}.sm\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.sm\:bui-gap-1{gap:var(--bui-space-1)}.sm\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.sm\:bui-gap-2{gap:var(--bui-space-2)}.sm\:bui-gap-3{gap:var(--bui-space-3)}.sm\:bui-gap-4{gap:var(--bui-space-4)}.sm\:bui-gap-5{gap:var(--bui-space-5)}.sm\:bui-gap-6{gap:var(--bui-space-6)}.sm\:bui-gap-7{gap:var(--bui-space-7)}.sm\:bui-gap-8{gap:var(--bui-space-8)}.sm\:bui-gap-9{gap:var(--bui-space-9)}.sm\:bui-gap-10{gap:var(--bui-space-10)}.sm\:bui-gap-11{gap:var(--bui-space-11)}.sm\:bui-gap-12{gap:var(--bui-space-12)}.sm\:bui-gap-13{gap:var(--bui-space-13)}.sm\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1024px){.md\:bui-gap{gap:var(--gap-md)}.md\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.md\:bui-gap-1{gap:var(--bui-space-1)}.md\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.md\:bui-gap-2{gap:var(--bui-space-2)}.md\:bui-gap-3{gap:var(--bui-space-3)}.md\:bui-gap-4{gap:var(--bui-space-4)}.md\:bui-gap-5{gap:var(--bui-space-5)}.md\:bui-gap-6{gap:var(--bui-space-6)}.md\:bui-gap-7{gap:var(--bui-space-7)}.md\:bui-gap-8{gap:var(--bui-space-8)}.md\:bui-gap-9{gap:var(--bui-space-9)}.md\:bui-gap-10{gap:var(--bui-space-10)}.md\:bui-gap-11{gap:var(--bui-space-11)}.md\:bui-gap-12{gap:var(--bui-space-12)}.md\:bui-gap-13{gap:var(--bui-space-13)}.md\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1280px){.lg\:bui-gap{gap:var(--gap-lg)}.lg\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.lg\:bui-gap-1{gap:var(--bui-space-1)}.lg\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.lg\:bui-gap-2{gap:var(--bui-space-2)}.lg\:bui-gap-3{gap:var(--bui-space-3)}.lg\:bui-gap-4{gap:var(--bui-space-4)}.lg\:bui-gap-5{gap:var(--bui-space-5)}.lg\:bui-gap-6{gap:var(--bui-space-6)}.lg\:bui-gap-7{gap:var(--bui-space-7)}.lg\:bui-gap-8{gap:var(--bui-space-8)}.lg\:bui-gap-9{gap:var(--bui-space-9)}.lg\:bui-gap-10{gap:var(--bui-space-10)}.lg\:bui-gap-11{gap:var(--bui-space-11)}.lg\:bui-gap-12{gap:var(--bui-space-12)}.lg\:bui-gap-13{gap:var(--bui-space-13)}.lg\:bui-gap-14{gap:var(--bui-space-14)}}@media (width>=1536px){.xl\:bui-gap{gap:var(--gap-xl)}.xl\:bui-gap-0\.5{gap:var(--bui-space-0_5)}.xl\:bui-gap-1{gap:var(--bui-space-1)}.xl\:bui-gap-1\.5{gap:var(--bui-space-1_5)}.xl\:bui-gap-2{gap:var(--bui-space-2)}.xl\:bui-gap-3{gap:var(--bui-space-3)}.xl\:bui-gap-4{gap:var(--bui-space-4)}.xl\:bui-gap-5{gap:var(--bui-space-5)}.xl\:bui-gap-6{gap:var(--bui-space-6)}.xl\:bui-gap-7{gap:var(--bui-space-7)}.xl\:bui-gap-8{gap:var(--bui-space-8)}.xl\:bui-gap-9{gap:var(--bui-space-9)}.xl\:bui-gap-10{gap:var(--bui-space-10)}.xl\:bui-gap-11{gap:var(--bui-space-11)}.xl\:bui-gap-12{gap:var(--bui-space-12)}.xl\:bui-gap-13{gap:var(--bui-space-13)}.xl\:bui-gap-14{gap:var(--bui-space-14)}}.bui-align-start{align-items:start}.bui-align-center{align-items:center}.bui-align-end{align-items:end}.bui-align-stretch{align-items:stretch}.bui-jc-start{justify-content:start}.bui-jc-center{justify-content:center}.bui-jc-end{justify-content:end}.bui-jc-between{justify-content:space-between}.bui-fd-row{flex-direction:row}.bui-fd-column{flex-direction:column}.bui-fd-row-reverse{flex-direction:row-reverse}.bui-fd-column-reverse{flex-direction:column-reverse}@media (width>=640px){.xs\:bui-align-start{align-items:start}.xs\:bui-align-center{align-items:center}.xs\:bui-align-end{align-items:end}.xs\:bui-align-stretch{align-items:stretch}.xs\:bui-jc-start{justify-content:start}.xs\:bui-jc-center{justify-content:center}.xs\:bui-jc-end{justify-content:end}.xs\:bui-jc-between{justify-content:space-between}.xs\:bui-fd-row{flex-direction:row}.xs\:bui-fd-column{flex-direction:column}.xs\:bui-fd-row-reverse{flex-direction:row-reverse}.xs\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=768px){.sm\:bui-align-start{align-items:start}.sm\:bui-align-center{align-items:center}.sm\:bui-align-end{align-items:end}.sm\:bui-align-stretch{align-items:stretch}.sm\:bui-jc-start{justify-content:start}.sm\:bui-jc-center{justify-content:center}.sm\:bui-jc-end{justify-content:end}.sm\:bui-jc-between{justify-content:space-between}.sm\:bui-fd-row{flex-direction:row}.sm\:bui-fd-column{flex-direction:column}.sm\:bui-fd-row-reverse{flex-direction:row-reverse}.sm\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1024px){.md\:bui-align-start{align-items:start}.md\:bui-align-center{align-items:center}.md\:bui-align-end{align-items:end}.md\:bui-align-stretch{align-items:stretch}.md\:bui-jc-start{justify-content:start}.md\:bui-jc-center{justify-content:center}.md\:bui-jc-end{justify-content:end}.md\:bui-jc-between{justify-content:space-between}.md\:bui-fd-row{flex-direction:row}.md\:bui-fd-column{flex-direction:column}.md\:bui-fd-row-reverse{flex-direction:row-reverse}.md\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1280px){.lg\:bui-align-start{align-items:start}.lg\:bui-align-center{align-items:center}.lg\:bui-align-end{align-items:end}.lg\:bui-align-stretch{align-items:stretch}.lg\:bui-jc-start{justify-content:start}.lg\:bui-jc-center{justify-content:center}.lg\:bui-jc-end{justify-content:end}.lg\:bui-jc-between{justify-content:space-between}.lg\:bui-fd-row{flex-direction:row}.lg\:bui-fd-column{flex-direction:column}.lg\:bui-fd-row-reverse{flex-direction:row-reverse}.lg\:bui-fd-column-reverse{flex-direction:column-reverse}}@media (width>=1536px){.xl\:bui-align-start{align-items:start}.xl\:bui-align-center{align-items:center}.xl\:bui-align-end{align-items:end}.xl\:bui-align-stretch{align-items:stretch}.xl\:bui-jc-start{justify-content:start}.xl\:bui-jc-center{justify-content:center}.xl\:bui-jc-end{justify-content:end}.xl\:bui-jc-between{justify-content:space-between}.xl\:bui-fd-row{flex-direction:row}.xl\:bui-fd-column{flex-direction:column}.xl\:bui-fd-row-reverse{flex-direction:row-reverse}.xl\:bui-fd-column-reverse{flex-direction:column-reverse}}:where(a){color:inherit;text-decoration:none}@keyframes pulse{50%{opacity:.5}}:root{--bui-font-regular:system-ui;--bui-font-monospace:ui-monospace,"Menlo","Monaco","Consolas","Liberation Mono","Courier New",monospace;--bui-font-weight-regular:400;--bui-font-weight-bold:600;--bui-font-size-1:.625rem;--bui-font-size-2:.75rem;--bui-font-size-3:.875rem;--bui-font-size-4:1rem;--bui-font-size-5:1.25rem;--bui-font-size-6:1.5rem;--bui-font-size-7:2rem;--bui-font-size-8:3rem;--bui-font-size-9:4rem;--bui-font-size-10:5.75rem;--bui-space:.25rem;--bui-space-0_5:calc(var(--bui-space)*.5);--bui-space-1:var(--bui-space);--bui-space-1_5:calc(var(--bui-space)*1.5);--bui-space-2:calc(var(--bui-space)*2);--bui-space-3:calc(var(--bui-space)*3);--bui-space-4:calc(var(--bui-space)*4);--bui-space-5:calc(var(--bui-space)*5);--bui-space-6:calc(var(--bui-space)*6);--bui-space-7:calc(var(--bui-space)*7);--bui-space-8:calc(var(--bui-space)*8);--bui-space-9:calc(var(--bui-space)*9);--bui-space-10:calc(var(--bui-space)*10);--bui-space-11:calc(var(--bui-space)*11);--bui-space-12:calc(var(--bui-space)*12);--bui-space-13:calc(var(--bui-space)*13);--bui-space-14:calc(var(--bui-space)*14);--bui-radius-1:calc(.125rem);--bui-radius-2:calc(.25rem);--bui-radius-3:calc(.5rem);--bui-radius-4:calc(.75rem);--bui-radius-5:calc(1rem);--bui-radius-6:calc(1.25rem);--bui-radius-full:9999px;--bui-black:#000;--bui-white:#fff;--bui-gray-1:#f8f8f8;--bui-gray-2:#ececec;--bui-gray-3:#d9d9d9;--bui-gray-4:#c1c1c1;--bui-gray-5:#9e9e9e;--bui-gray-6:#8c8c8c;--bui-gray-7:#757575;--bui-gray-8:#595959;--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1f5493;--bui-bg-solid-hover:#163a66;--bui-bg-solid-pressed:#0f2b4e;--bui-bg-solid-disabled:#ebebeb;--bui-bg-tint:transparent;--bui-bg-tint-hover:#1f549366;--bui-bg-tint-pressed:#1f549399;--bui-bg-tint-disabled:#ebebeb;--bui-bg-danger:#feebe7;--bui-bg-warning:#fff2b2;--bui-bg-success:#e6f6eb;--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#1f5493;--bui-fg-link-hover:#1f2d5c;--bui-fg-disabled:#9e9e9e;--bui-fg-solid:var(--bui-white);--bui-fg-solid-disabled:#9c9c9c;--bui-fg-tint:#1f5493;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#0000001a;--bui-border-hover:#0003;--bui-border-pressed:#0006;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#a0a0a03b;--bui-scrollbar-thumb:#a0a0a0;--bui-animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite}[data-theme=dark]{--bui-gray-1:#191919;--bui-gray-2:#242424;--bui-gray-3:#373737;--bui-gray-4:#464646;--bui-gray-5:#575757;--bui-gray-6:#7b7b7b;--bui-gray-7:#9e9e9e;--bui-gray-8:#b4b4b4;--bui-bg:var(--bui-black);--bui-bg-surface-1:var(--bui-gray-1);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#9cc9ff;--bui-bg-solid-hover:#83b9fd;--bui-bg-solid-pressed:#83b9fd;--bui-bg-solid-disabled:#222;--bui-bg-tint:transparent;--bui-bg-tint-hover:#9cc9ff1f;--bui-bg-tint-pressed:#9cc9ff29;--bui-bg-tint-disabled:transparent;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:#9cc9ff;--bui-fg-link-hover:#7eb5f7;--bui-fg-disabled:var(--bui-gray-7);--bui-fg-solid:#101821;--bui-fg-solid-disabled:var(--bui-gray-5);--bui-fg-tint:#9cc9ff;--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:#fff3;--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#1f5493;--bui-scrollbar:#3636363a;--bui-scrollbar-thumb:#575757}.bui-AvatarRoot{vertical-align:middle;user-select:none;color:var(--bui-fg-primary);background-color:var(--bui-bg-surface-2);border-radius:100%;justify-content:center;align-items:center;width:2rem;height:2rem;font-size:1rem;font-weight:500;line-height:1;display:inline-flex;overflow:hidden}.bui-AvatarRoot[data-size=small]{width:1.5rem;height:1.5rem}.bui-AvatarRoot[data-size=medium]{width:2rem;height:2rem}.bui-AvatarRoot[data-size=large]{width:3rem;height:3rem}.bui-AvatarImage{object-fit:cover;width:100%;height:100%}.bui-AvatarFallback{width:100%;height:100%;font-size:var(--bui-font-size-3);font-weight:var(--bui-font-weight-regular);box-shadow:inset 0 0 0 1px var(--bui-border);border-radius:var(--bui-radius-full);justify-content:center;align-items:center;display:flex}.bui-Box{font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary)}.bui-Button{user-select:none;font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-bold);cursor:pointer;border-radius:var(--bui-radius-2);justify-content:center;align-items:center;gap:var(--bui-space-1_5);border:none;flex-shrink:0;padding:0;display:inline-flex;&[data-disabled=true]{cursor:not-allowed}}.bui-Button[data-variant=primary]{background-color:var(--bui-bg-solid);color:var(--bui-fg-solid);&:hover{background-color:var(--bui-bg-solid-hover);transition:background-color .15s}&:active{background-color:var(--bui-bg-solid-pressed)}&:focus-visible{outline:2px solid var(--bui-ring);outline-offset:2px}&[data-disabled=true]{background-color:var(--bui-bg-solid-disabled);color:var(--bui-fg-solid-disabled)}}.bui-Button[data-variant=secondary]{background-color:var(--bui-bg-surface-1);box-shadow:inset 0 0 0 1px var(--bui-border);color:var(--bui-fg-primary);&:hover{box-shadow:inset 0 0 0 1px var(--bui-border-hover);transition:box-shadow .15s}&:active{box-shadow:inset 0 0 0 1px var(--bui-border-pressed)}&:focus-visible{box-shadow:inset 0 0 0 2px var(--bui-ring);outline:none;transition:none}&[data-disabled=true]{box-shadow:inset 0 0 0 1px var(--bui-border-disabled);color:var(--bui-fg-disabled)}}.bui-Button[data-variant=tertiary]{color:var(--bui-fg-primary);background-color:#0000;&:hover{background-color:var(--bui-bg-surface-1);transition:background-color .2s}&:active{background-color:var(--bui-bg-surface-2)}&:focus-visible{box-shadow:inset 0 0 0 2px var(--bui-ring);outline:none;transition:none}&[data-disabled=true]{color:var(--bui-fg-disabled);background-color:#0000}}.bui-Button[data-size=medium]{font-size:var(--bui-font-size-4);padding:0 var(--bui-space-3);height:2.5rem}.bui-Button[data-size=small]{font-size:var(--bui-font-size-3);padding:0 var(--bui-space-2);height:2rem}.bui-Button[data-size=small] svg{width:1rem;height:1rem}.bui-Button[data-size=medium] svg{width:1.25rem;height:1.25rem}.bui-ButtonIcon{justify-content:center;align-items:center}.bui-ButtonIcon[data-size=small]{width:2rem;padding:0}.bui-ButtonIcon[data-size=medium]{width:2.5rem;padding:0}.bui-Card{gap:var(--bui-space-3);background-color:var(--bui-bg-surface-1);border-radius:var(--bui-radius-3);padding-block:var(--bui-space-3);color:var(--bui-fg-primary);border:1px solid var(--bui-border);flex-direction:column;width:100%;min-height:0;display:flex;overflow:hidden}.bui-CardBody{flex:1;min-height:0;overflow:auto}.bui-CardHeader,.bui-CardFooter{padding-inline:var(--bui-space-3)}.bui-CheckboxRoot{width:1rem;height:1rem;box-shadow:inset 0 0 0 1px var(--bui-border);cursor:pointer;background-color:var(--bui-bg-surface-1);border:none;border-radius:2px;flex-shrink:0;justify-content:center;align-items:center;padding:0;transition:background-color .2s ease-in-out;display:flex}.bui-CheckboxRoot:focus-visible{outline:2px solid var(--bui-ring);outline-offset:2px;transition:none}.bui-CheckboxRoot[data-checked]{background-color:var(--bui-bg-solid);box-shadow:none;color:var(--bui-fg-solid)}.bui-CheckboxLabel{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);user-select:none;flex-direction:row;display:flex;&:hover{& .bui-CheckboxRoot:not([data-checked]){box-shadow:inset 0 0 0 1px var(--bui-border-hover)}}}.bui-CheckboxIndicator{color:var(--bui-fg-solid);justify-content:center;align-items:center;display:flex}.bui-CollapsiblePanel{height:var(--collapsible-panel-height);transition:all .15s ease-out;display:flex;overflow:hidden;&[data-starting-style],&[data-ending-style]{height:0}}.bui-Container{max-width:120rem;padding-inline:var(--bui-space-4);margin-inline:auto;transition:padding .2s ease-in-out}@media (width>=640px){.bui-Container{padding-inline:var(--bui-space-5)}}.bui-DataTableRoot{gap:var(--bui-space-3);flex-direction:column;display:flex}.bui-DataTablePagination{padding-top:var(--bui-space-5);justify-content:space-between;align-items:center;display:flex}.bui-DataTablePagination--left{justify-content:space-between;align-items:center;display:flex}.bui-DataTablePagination--right{justify-content:space-between;align-items:center;gap:var(--bui-space-2);display:flex}.bui-DataTablePagination--select{min-width:10.5rem}.bui-FieldError{color:var(--bui-fg-danger);font-size:var(--bui-font-size-2);font-weight:var(--bui-font-weight-regular);margin-top:var(--bui-space-2);display:inline-block}.bui-FieldLabelWrapper{margin-bottom:var(--bui-space-3);gap:var(--bui-space-1);flex-direction:column;display:flex}.bui-FieldLabel{color:var(--bui-fg-primary);cursor:pointer;font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-2);margin-right:auto}.bui-FieldSecondaryLabel{color:var(--bui-fg-secondary);font-weight:var(--bui-font-weight-regular);margin-left:var(--bui-space-1)}.bui-FieldDescription{font-weight:var(--bui-font-weight-regular);font-size:var(--bui-font-size-2);color:var(--bui-fg-secondary);margin:0}.bui-Flex{min-width:0;display:flex}.bui-Grid{display:grid}.bui-HeaderToolbar{z-index:10;margin-bottom:var(--bui-space-6);position:sticky;top:0;&:before{content:"";background-color:var(--bui-bg);z-index:0;height:16px;position:absolute;top:0;left:0;right:0}&[data-has-tabs=true]{margin-bottom:0}}.bui-HeaderToolbarWrapper{z-index:1;background-color:var(--bui-bg-surface-1);padding-inline:var(--bui-space-5);border-bottom:1px solid var(--bui-border);color:var(--bui-fg-primary);flex-direction:row;justify-content:space-between;align-items:center;height:52px;display:flex;position:relative}.bui-HeaderToolbarContent{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex}.bui-HeaderToolbarName{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-3);font-weight:var(--bui-font-weight-regular);flex-direction:row;flex-shrink:0;display:flex}.bui-HeaderToolbarIcon{width:16px;height:16px;color:var(--bui-fg-primary);& svg{width:100%;height:100%}}.bui-HeaderToolbarControls{right:var(--bui-space-5);align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex;position:absolute;top:50%;transform:translateY(-50%)}.bui-HeaderBreadcrumbs{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-3);font-weight:var(--bui-font-weight-regular);flex-direction:row;display:flex}.bui-HeaderBreadcrumb{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex}.bui-HeaderBreadcrumbLink{color:var(--bui-fg-secondary);cursor:pointer;text-decoration:none;&[data-active=true]{color:var(--bui-fg-primary)}}.bui-HeaderBreadcrumbSeparator{width:16px;height:16px;color:var(--bui-fg-secondary)}.bui-HeaderTabsWrapper{margin-bottom:var(--bui-space-4);padding-inline:var(--bui-space-3);border-bottom:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1)}.bui-HeaderPage{gap:var(--bui-space-1);padding-inline:var(--bui-space-5);margin-top:var(--bui-space-6);margin-bottom:var(--bui-space-6);flex-direction:column;display:flex}.bui-HeaderPageContent{flex-direction:row;justify-content:space-between;display:flex}.bui-HeaderPageTabsWrapper{margin-left:-8px}.bui-HeaderPageControls{align-items:center;gap:var(--bui-space-2);flex-direction:row;display:flex}.bui-Heading{font-family:var(--bui-font-regular);color:var(--bui-fg-primary);margin:0;padding:0;line-height:100%}.bui-Heading[data-variant=display]{font-size:var(--bui-font-size-10);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-variant=title1]{font-size:var(--bui-font-size-9);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-variant=title2]{font-size:var(--bui-font-size-8);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-variant=title3]{font-size:var(--bui-font-size-7);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-variant=title4]{font-size:var(--bui-font-size-6);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-variant=title5]{font-size:var(--bui-font-size-5);font-weight:var(--bui-font-weight-bold)}.bui-Heading[data-color=primary]{color:var(--bui-fg-primary)}.bui-Heading[data-color=secondary]{color:var(--bui-fg-secondary)}.bui-Heading[data-truncate]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.bui-Icon{width:1rem;height:1rem}.bui-Link{font-family:var(--bui-font-regular);cursor:pointer;margin:0;padding:0;text-decoration-line:none;&:hover{text-underline-offset:calc(.025em + 2px);text-decoration-line:underline;text-decoration-style:solid;text-decoration-thickness:min(2px,max(1px,.05em));text-decoration-color:color-mix(in srgb,var(--bui-fg-link-hover)30%,transparent)}}.bui-Link[data-variant=title-large]{font-size:var(--bui-font-size-8);line-height:140%}.bui-Link[data-variant=title-medium]{font-size:var(--bui-font-size-7);line-height:140%}.bui-Link[data-variant=title-small]{font-size:var(--bui-font-size-6);line-height:140%}.bui-Link[data-variant=title-x-small]{font-size:var(--bui-font-size-5);line-height:140%}.bui-Link[data-variant=body-large]{font-size:var(--bui-font-size-4);line-height:140%}.bui-Link[data-variant=body-medium]{font-size:var(--bui-font-size-3);line-height:140%}.bui-Link[data-variant=body-small]{font-size:var(--bui-font-size-2);line-height:140%}.bui-Link[data-variant=body-x-small]{font-size:var(--bui-font-size-1);line-height:140%}.bui-Link[data-weight=regular]{font-weight:var(--bui-font-weight-regular)}.bui-Link[data-weight=bold]{font-weight:var(--bui-font-weight-bold)}.bui-Link[data-color=primary]{color:var(--bui-fg-primary)}.bui-Link[data-color=secondary]{color:var(--bui-fg-secondary)}.bui-MenuPositioner{z-index:100;outline:0}.bui-MenuPopup{background-color:var(--bui-bg-surface-1);border:1px solid var(--bui-border);color:var(--bui-fg-primary);transform-origin:var(--transform-origin);max-width:min(var(--available-width),340px);max-height:min(var(--available-height),500px);padding-bottom:var(--bui-space-1);border-radius:.375rem;outline:none;flex-direction:column;transition:transform .15s,opacity .15s;display:flex;position:relative;overflow:auto;&[data-starting-style],&[data-ending-style]{opacity:0;transform:scale(.9)}}.bui-MenuItem{user-select:none;align-items:center;gap:var(--bui-space-2);height:32px;color:var(--bui-fg-primary);border-radius:var(--bui-radius-2);margin-inline:var(--bui-space-1);padding-inline:var(--bui-space-2);font-size:var(--bui-font-size-3);cursor:pointer;outline:0;flex-shrink:0;text-decoration:none;display:flex;&:first-child{margin-top:var(--bui-space-1)}&[data-highlighted]{background-color:var(--bui-gray-3)}}.bui-MenuSubmenuTrigger{user-select:none;justify-content:space-between;align-items:center;gap:var(--bui-space-2);height:32px;color:var(--bui-fg-primary);border-radius:var(--bui-radius-2);margin-inline:var(--bui-space-1);padding-inline:var(--bui-space-2);font-size:var(--bui-font-size-3);cursor:pointer;outline:0;flex-shrink:0;text-decoration:none;display:flex;& .bui-Icon{color:var(--bui-fg-secondary)}&:first-child{margin-top:var(--bui-space-1)}&[data-popup-open],&[data-highlighted]{background-color:var(--bui-gray-3);& .bui-Icon{color:var(--bui-fg-primary)}}}.bui-MenuSeparator{background-color:var(--color-gray-200);height:1px;margin:.375rem 1rem}.bui-SubmenuComboboxSearch{padding-inline:var(--bui-space-3);border:none;border-bottom:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);width:100%;height:32px;color:var(--bui-fg-primary);line-height:140%;font-size:var(--bui-font-size-3);z-index:1;outline:none;position:sticky;top:0;&::placeholder{color:var(--bui-fg-secondary)}&:disabled{opacity:.6;cursor:not-allowed}}.bui-SubmenuComboboxItems{padding-top:var(--bui-space-2);outline:none;flex-direction:column;display:flex;overflow-y:auto}.bui-SubmenuComboboxNoResults{padding-inline:var(--bui-space-3);padding-top:var(--bui-space-2);padding-bottom:var(--bui-space-4);color:var(--bui-fg-secondary);font-size:var(--bui-font-size-3)}.bui-SubmenuComboboxItem{user-select:none;justify-content:space-between;align-items:center;gap:var(--bui-space-2);height:32px;color:var(--bui-fg-primary);border-radius:var(--bui-radius-2);margin-inline:var(--bui-space-1);padding-inline:var(--bui-space-2);font-size:var(--bui-font-size-3);cursor:pointer;outline:0;flex-shrink:0;text-decoration:none;display:flex;&[data-highlighted]{background-color:var(--bui-gray-3)}&[data-disabled]{opacity:.5;cursor:not-allowed}}.bui-SubmenuComboboxItemCheckbox{width:16px;height:16px;color:var(--bui-fg-primary);border-radius:var(--bui-radius-2);border:1px solid var(--bui-border);background:var(--bui-bg-surface-1);flex-shrink:0;justify-content:center;align-items:center;display:flex}.bui-SubmenuComboboxItemLabel{text-overflow:ellipsis;white-space:nowrap;flex:1;overflow:hidden}.bui-Popover{background-color:var(--bui-bg-surface-1);border:1px solid var(--bui-border);border-radius:var(--bui-radius-3);padding-block:var(--bui-space-1);margin-right:12px;overflow:scroll;box-shadow:0 4px 12px #0000001a}.bui-RadioGroup{color:var(--bui-fg-primary);flex-direction:column;display:flex}.bui-RadioGroup[data-orientation=horizontal] .bui-RadioGroupContent{gap:var(--bui-space-4);flex-direction:row}.bui-RadioGroupContent{gap:var(--bui-space-2);flex-direction:column;display:flex}.bui-Radio{align-items:center;gap:var(--bui-space-2);font-size:var(--bui-font-size-2);color:var(--bui-fg-primary);forced-color-adjust:none;display:flex;position:relative;&:before{content:"";box-sizing:border-box;border:.125rem solid var(--bui-border);background:var(--bui-gray-1);border-radius:var(--bui-radius-full);width:1rem;height:1rem;transition:all .2s;display:block}&[data-pressed]:before{border-color:var(--bui-border)}&[data-selected]{&:before{border-color:var(--bui-bg-solid);border-width:.25rem}&[data-pressed]:before{border-color:var(--bui-bg-solid)}}&[data-focus-visible]:before{outline:2px solid var(--bui-ring);outline-offset:2px}&[data-disabled]{cursor:not-allowed;color:var(--bui-fg-disabled);&:before{border-color:var(--bui-border-disabled);background:var(--bui-bg-disabled)}&[data-selected]:before{border-color:var(--bui-border-disabled)}}&[data-invalid]:before,&[data-invalid][data-selected]:before{border-color:var(--bui-border-danger)}&[data-disabled][data-invalid]{color:var(--bui-fg-disabled);&:before{border-color:var(--bui-border-disabled);background:var(--bui-bg-disabled)}&[data-selected]:before{border-color:var(--bui-border-disabled)}}}.bui-TableRoot{caption-side:bottom;border-collapse:collapse;width:100%}.bui-TableHead{text-align:left;padding:var(--bui-space-3);font-size:var(--bui-font-size-3);color:var(--bui-fg-primary)}.bui-TableBody{color:var(--bui-fg-primary)}.bui-TableRow{border-bottom:1px solid var(--bui-border);transition:color .2s ease-in-out}.bui-TableBody .bui-TableRow:hover{background-color:var(--bui-gray-2)}.bui-TableCell{padding:var(--bui-space-3);font-size:var(--bui-font-size-3)}.bui-TableCellText,.bui-TableCellLink{gap:var(--bui-space-0_5);flex-direction:column;display:flex}.bui-TableCellProfile{gap:var(--bui-space-2);flex-direction:row;align-items:center;display:flex}.bui-TableCellProfileAvatar{vertical-align:middle;user-select:none;color:var(--bui-fg-primary);background-color:var(--bui-bg-surface-2);border-radius:100%;justify-content:center;align-items:center;width:1.25rem;height:1.25rem;font-size:1rem;font-weight:500;line-height:1;display:inline-flex;overflow:hidden}.bui-TableCellProfileAvatarImage{object-fit:cover;width:100%;height:100%}.bui-TableCellProfileAvatarFallback{width:100%;height:100%;font-size:var(--bui-font-size-2);font-weight:var(--bui-font-weight-regular);box-shadow:inset 0 0 0 1px var(--bui-border);border-radius:var(--bui-radius-full);justify-content:center;align-items:center;display:flex}.bui-Tabs{--active-tab-left:0px;--active-tab-right:0px;--active-tab-top:0px;--active-tab-bottom:0px;--active-tab-width:0px;--active-tab-height:0px;--active-transition-duration:0s;--hovered-tab-left:0px;--hovered-tab-right:0px;--hovered-tab-top:0px;--hovered-tab-bottom:0px;--hovered-tab-width:0px;--hovered-tab-height:0px;--hovered-tab-opacity:0;--hovered-transition-duration:0s}.bui-TabList{flex-direction:row;display:flex}.bui-TabListWrapper{position:relative}.bui-Tab{font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-secondary);cursor:pointer;z-index:2;height:36px;padding-inline:var(--bui-space-2);justify-content:center;align-items:center;display:flex;position:relative;&[data-selected=true]{color:var(--bui-fg-primary)}}.bui-TabActive{content:"";left:calc(var(--active-tab-left) + var(--bui-space-2));width:calc(var(--active-tab-width) - var(--bui-space-4));background-color:var(--bui-fg-primary);height:1px;transition:left var(--active-transition-duration)ease-out,opacity .15s ease-out,width var(--active-transition-duration)ease-out;opacity:1;border-radius:4px;position:absolute;bottom:-1px}.bui-TabHovered{content:"";left:var(--hovered-tab-left);top:calc(var(--hovered-tab-top) + 4px);width:var(--hovered-tab-width);height:calc(var(--hovered-tab-height) - 8px);background-color:var(--bui-gray-2);opacity:var(--hovered-tab-opacity);transition:left var(--hovered-transition-duration)ease-out,top var(--hovered-transition-duration)ease-out,width var(--hovered-transition-duration)ease-out,height var(--hovered-transition-duration)ease-out,opacity .15s ease-out;border-radius:4px;position:absolute}.bui-TabPanel{padding-inline:var(--bui-space-2);padding-top:var(--bui-space-4)}.bui-Text{font-family:var(--bui-font-regular);margin:0;padding:0}.bui-Text[data-variant=body]{font-size:var(--bui-font-size-3);line-height:140%}.bui-Text[data-variant=subtitle]{font-size:var(--bui-font-size-4);line-height:140%}.bui-Text[data-variant=caption]{font-size:var(--bui-font-size-2);line-height:140%}.bui-Text[data-variant=label]{font-size:var(--bui-font-size-1);line-height:140%}.bui-Text[data-weight=regular]{font-weight:var(--bui-font-weight-regular)}.bui-Text[data-weight=bold]{font-weight:var(--bui-font-weight-bold)}.bui-Text[data-color=primary]{color:var(--bui-fg-primary)}.bui-Text[data-color=secondary]{color:var(--bui-fg-secondary)}.bui-Text[data-color=danger]{color:var(--bui-fg-danger)}.bui-Text[data-color=warning]{color:var(--bui-fg-warning)}.bui-Text[data-color=success]{color:var(--bui-fg-success)}.bui-Text[data-truncate]{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.bui-TextField{font-family:var(--bui-font-regular);flex-direction:column;width:100%;display:flex}.bui-InputWrapper{position:relative;&[data-size=small] .bui-Input{height:2rem}&[data-size=medium] .bui-Input{height:2.5rem}&[data-size=small] .bui-Input[data-icon]{padding-left:var(--bui-space-8)}&[data-size=medium] .bui-Input[data-icon]{padding-left:var(--bui-space-9)}}.bui-InputIcon{left:var(--bui-space-3);margin-right:var(--bui-space-1);color:var(--bui-fg-primary);flex-shrink:0;position:absolute;top:50%;transform:translateY(-50%);&[data-size=small],&[data-size=small] svg{width:1rem;height:1rem}&[data-size=medium],&[data-size=medium] svg{width:1.25rem;height:1.25rem}}.bui-Input{padding:0 var(--bui-space-3);border-radius:var(--bui-radius-3);border:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);width:100%;height:100%;cursor:inherit;align-items:center;transition:border-color .2s ease-in-out,outline-color .2s ease-in-out;display:flex;&::-webkit-search-cancel-button,&::-webkit-search-decoration{-webkit-appearance:none}&::placeholder{color:var(--bui-fg-secondary)}&[data-focused]{outline-color:var(--bui-border-pressed);outline-width:0}&[data-hovered]{border-color:var(--bui-border-hover)}&[data-focused]{border-color:var(--bui-border-pressed);outline-width:0}&[data-invalid]{border-color:var(--bui-fg-danger)}&[data-disabled]{opacity:.5;cursor:not-allowed;border:1px solid var(--bui-border-disabled)}}.bui-SearchField[data-empty] .bui-InputClear{display:none}.bui-InputClear{cursor:pointer;color:var(--bui-fg-secondary);background-color:#0000;border:none;justify-content:center;align-items:center;margin:0;padding:0;transition:color .2s ease-in-out;display:flex;position:absolute;top:0;bottom:0;right:0}.bui-InputClear:hover{color:var(--bui-fg-primary)}.bui-InputClear[data-size=small]{width:2rem;height:2rem}.bui-InputClear[data-size=medium]{width:2.5rem;height:2.5rem}.bui-InputClear svg{width:1rem;height:1rem}.bui-Skeleton{animation:var(--bui-animate-pulse);background-color:var(--bui-bg-surface-2);border-radius:var(--bui-radius-2)}.bui-Skeleton[data-rounded=true]{border-radius:var(--bui-radius-full)}.bui-Tooltip{background:var(--bui-bg-surface-2);forced-color-adjust:none;padding:var(--bui-space-2)var(--bui-space-3);max-width:240px;font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);color:var(--bui-fg-primary);border-radius:4px;outline:none;transition:transform .2s,opacity .2s;transform:translate(0,0);box-shadow:0 8px 20px #0000001a;&[data-entering],&[data-exiting]{transform:var(--origin);opacity:0}&[data-placement=top]{margin-bottom:var(--bui-space-2);--origin:translateY(4px)}&[data-placement=bottom]{margin-top:var(--bui-space-2);--origin:translateY(-4px);& .bui-TooltipArrow svg{transform:rotate(180deg)}}&[data-placement=right]{margin-left:var(--bui-space-2);--origin:translateX(-4px);& .bui-TooltipArrow svg{transform:rotate(90deg)}}&[data-placement=left]{margin-right:var(--bui-space-2);--origin:translateX(4px);& .bui-TooltipArrow svg{transform:rotate(-90deg)}}}.bui-TooltipArrow svg{fill:var(--bui-bg-surface-1);display:block}.bui-ScrollAreaRoot{box-sizing:border-box;width:100%}.bui-ScrollAreaViewport{overscroll-behavior:contain;height:100%}.bui-ScrollAreaContent{padding-block:.75rem;flex-direction:column;gap:1rem;padding-left:1rem;padding-right:1.5rem;display:flex}.bui-ScrollAreaScrollbar{background-color:var(--bui-scrollbar);opacity:0;border-radius:.375rem;justify-content:center;width:.25rem;margin:.5rem;transition:opacity .15s .3s;display:flex;&[data-hovering],&[data-scrolling]{opacity:1;transition-duration:75ms;transition-delay:0s}&:before{content:"";width:1.25rem;height:100%;position:absolute}}.bui-ScrollAreaThumb{border-radius:inherit;background-color:var(--bui-scrollbar-thumb);width:100%}.bui-Select[data-invalid]{& .bui-SelectTrigger{border-color:var(--bui-fg-danger)}}.bui-SelectTrigger{box-sizing:border-box;border-radius:var(--bui-radius-3);border:1px solid var(--bui-border);background-color:var(--bui-bg-surface-1);cursor:pointer;justify-content:space-between;align-items:center;gap:var(--bui-space-2);max-width:100%;transition:border-color .2s ease-in-out,outline-color .2s ease-in-out;display:flex;& svg{flex-shrink:0}&[data-size=small]{height:2rem;padding-inline:var(--bui-space-3)}&[data-size=medium]{height:3rem;padding-inline:var(--bui-space-4)}&[data-size=small] svg{width:1rem;height:1rem}&[data-size=medium] svg{width:1.25rem;height:1.25rem}&::placeholder{color:var(--bui-fg-secondary)}&:hover{border-color:var(--bui-border-hover)}&:focus-visible{border-color:var(--bui-border-pressed);outline:0}&[data-invalid]{border-color:var(--bui-fg-danger)}&[data-invalid]:hover,&[data-invalid]:focus-visible{border-width:2px}&[disabled]{cursor:not-allowed;border-color:var(--bui-border-disabled);color:var(--bui-fg-disabled)}&[disabled] .bui-SelectValue{color:var(--bui-fg-disabled)}&[data-popup-open] .bui-SelectIcon{transform:rotate(180deg)}}.bui-SelectValue{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:var(--bui-font-size-3);font-family:var(--bui-font-regular);font-weight:var(--bui-font-weight-regular);color:var(--bui-fg-primary);overflow:hidden;& .bui-SelectItemIndicator{display:none}&[disabled]{color:var(--bui-fg-disabled)}}.bui-SelectItem{width:var(--anchor-width);padding-block:var(--bui-space-2);padding-inline:var(--bui-space-4);color:var(--bui-fg-primary);border-radius:var(--bui-radius-3);cursor:pointer;user-select:none;font-size:var(--bui-font-size-3);align-items:center;gap:var(--bui-space-2);outline:none;grid-template-columns:1rem 1fr;grid-template-areas:"icon text";display:grid;position:relative;&[data-focused]{z-index:0;color:var(--bui-fg-primary);position:relative}&[data-focused]:before{content:"";z-index:-1;background-color:var(--bui-bg-tint-hover);border-radius:.25rem;position:absolute;inset-block:0;inset-inline:.25rem}&[data-disabled]{cursor:not-allowed;color:var(--bui-fg-disabled)}&[data-selected] .bui-SelectItemIndicator{opacity:1}}.bui-SelectItemIndicator{opacity:0;grid-area:icon;justify-content:center;align-items:center;transition:opacity .2s ease-in-out;display:flex}.bui-SelectItemLabel{flex:1;grid-area:text}.bui-Switch{align-items:center;gap:var(--bui-space-3);font-size:var(--bui-font-size-3);color:var(--bui-fg-primary);cursor:pointer;display:flex;position:relative;&[data-pressed] .bui-SwitchIndicator{&:before{background:var(--bui-fg-solid)}}&[data-selected]{& .bui-SwitchIndicator{background:var(--bui-bg-solid);&:before{background:var(--bui-fg-solid);transform:translate(100%)}}&[data-pressed]{& .indicator{background:var(--bui-gray-3)}}}&[data-focus-visible] .bui-SwitchIndicator{outline-offset:2px;outline:2px solid}}.bui-SwitchIndicator{background:var(--bui-gray-3);border:2px;border-radius:1.143rem;width:2rem;height:1.143rem;transition:all .2s;&:before{content:"";background:var(--bui-fg-solid);border-radius:16px;width:.857rem;height:.857rem;margin:.143rem;transition:all .2s;display:block}} \ No newline at end of file diff --git a/canon-docs/public/theme-spotify.css b/canon-docs/public/theme-spotify.css new file mode 100644 index 0000000000..0600e828d6 --- /dev/null +++ b/canon-docs/public/theme-spotify.css @@ -0,0 +1 @@ +@font-face{font-family:CircularSpTitle;font-weight:900;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSpTitle-Black-4588c99025b967475c31695b0743dd1a.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSpTitle-Black-506746f387a26f25aa3d023b3e501d34.woff)format("woff")}@font-face{font-family:CircularSpTitle;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-b2586b06a2e1522e9d879d84c2792a58.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSpTitle-Bold-1624fb2df28c20d7203d7fb86ce8b481.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:400;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Book-a00e99ef9996a3a157fb6b746856d04f.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Book-3f73da7d35bd81c706bce7bbb84964de.woff)format("woff")}@font-face{font-family:CircularSp;font-weight:700;font-display:swap;unicode-range:U+0,U+D,U+20-7E,U+A0-17E,U+18F,U+192,U+1A0-1A1,U+1AF-1B0,U+1B5-1B6,U+1C4-1C6,U+1F1-1F3,U+1FA-1FF,U+218-21B,U+237,U+259,U+2BB-2BC,U+2C6-2C7,U+2C9,U+2D8-2DD,U+300-301,U+303,U+309,U+323,U+394,U+3A9,U+3BC,U+3C0,U+1E80-1E85,U+1E8A-1E8B,U+1EA0-1EF9,U+1FD6,U+2007-2008,U+200B,U+2010-2011,U+2013-2014,U+2018-201A,U+201C-201E,U+2020-2022,U+2026,U+2030,U+2032-2033,U+2039-203A,U+2042,U+2044,U+2051,U+2070,U+2074-2079,U+2080-2089,U+20AB-20AC,U+2113,U+2117,U+2122,U+2126,U+2160-2169,U+216C-216F,U+2190-2193,U+2196-2199,U+21A9,U+21B0-21B5,U+21C6,U+2202,U+2206,U+220F,U+2211-2212,U+2215,U+2219-221A,U+221E,U+222B,U+2248,U+2260,U+2264-2265,U+22C5,U+24C5,U+25A0-25A1,U+25AF,U+25B2-25B3,U+25CA-25CB,U+25CF,U+262E,U+2713,U+2715,U+2780-2788,U+E000,U+E002,U+F6C3,U+FB00-FB04,U+FEFF,U+FF0C,U+FF0E,U+FF1A-FF1B,U+FFFF;src:url(https://encore.scdn.co/fonts/CircularSp-Bold-602e7aefc706aa36c6ec1324b9bbc461.woff2)format("woff2"),url(https://encore.scdn.co/fonts/CircularSp-Bold-856afe2da4ba4e61239b129e2c16d633.woff)format("woff")}[data-theme-name=spotify]{--bui-font-text:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--bui-font-title:CircularSpTitle,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;--bui-font-regular:CircularSp,CircularSp-Arab,CircularSp-Hebr,CircularSp-Cyrl,CircularSp-Grek,CircularSp-Deva;& .bui-Button{border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-3)}& .bui-ButtonIcon{padding:0}& .bui-MenuPopup{box-sizing:border-box;max-width:21.25rem}& .bui-MenuSubmenuTrigger,& .bui-MenuItem{height:auto;min-height:2rem}& .bui-Text{font-family:var(--bui-font-text)}& .bui-Heading{font-family:var(--bui-font-title)}& .bui-TableRow{border-radius:var(--bui-radius-4);border:none}& .bui-TableRow:hover td:first-child{border-top-left-radius:var(--bui-radius-2);border-bottom-left-radius:var(--bui-radius-2)}& .bui-TableRow:hover td:last-child{border-top-right-radius:var(--bui-radius-2);border-bottom-right-radius:var(--bui-radius-2)}& .bui-TableBody:before,& .bui-TableBody:after{line-height:var(--bui-space-1);content:"‌";display:block}& .bui-TableHeader .bui-TableRow{border-bottom:1px solid var(--bui-border)}& .bui-TableHead{font-size:var(--bui-font-size-2);color:var(--bui-fg-secondary);font-weight:var(--bui-font-weight-regular)}& .bui-HeaderToolbar{padding-top:var(--bui-space-2);padding-inline:var(--bui-space-2)}& .bui-HeaderToolbarWrapper{border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-3);border:none}& .bui-HeaderToolbarControls{right:calc(var(--bui-space-3) - 1px)}& .bui-HeaderTabsWrapper{margin-top:var(--bui-space-2);margin-inline:var(--bui-space-2);border-radius:var(--bui-radius-3);padding-inline:var(--bui-space-1);border:none}}[data-theme=light][data-theme-name=spotify]{--bui-bg:var(--bui-gray-1);--bui-bg-surface-1:var(--bui-white);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1ed760;--bui-bg-solid-hover:#3be477;--bui-bg-solid-pressed:#1abc54;--bui-bg-solid-disabled:var(--bui-gray-2);--bui-fg-primary:var(--bui-black);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-solid:var(--bui-black);--bui-border:var(--bui-gray-3);--bui-border-hover:#0000004d;--bui-border-pressed:#00000080;--bui-border-disabled:#0000001a;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#0003;& .bui-HeaderToolbarWrapper,& .bui-HeaderTabsWrapper{border:1px solid var(--bui-border)}}[data-theme=dark][data-theme-name=spotify]{--bui-bg:var(--bui-black);--bui-bg-surface-1:var(--bui-gray-1);--bui-bg-surface-2:var(--bui-gray-2);--bui-bg-solid:#1ed760;--bui-bg-solid-hover:#3be477;--bui-bg-solid-pressed:#1abc54;--bui-bg-solid-disabled:#0f6c30;--bui-bg-tint:#242424;--bui-bg-tint-hover:#202020;--bui-bg-tint-pressed:#292929;--bui-bg-tint-disabled:#fffc;--bui-bg-danger:#3b1219;--bui-bg-warning:#302008;--bui-bg-success:#132d21;--bui-fg-primary:var(--bui-white);--bui-fg-secondary:var(--bui-gray-7);--bui-fg-link:var(--bui-white);--bui-fg-link-hover:var(--bui-white);--bui-fg-disabled:var(--bui-gray-5);--bui-fg-solid:var(--bui-black);--bui-fg-solid-disabled:#083618;--bui-fg-tint:var(--bui-white);--bui-fg-tint-disabled:var(--bui-gray-5);--bui-fg-danger:#e22b2b;--bui-fg-warning:#e36d05;--bui-fg-success:#1db954;--bui-border:var(--bui-gray-3);--bui-border-hover:#fff6;--bui-border-pressed:#ffffff80;--bui-border-disabled:#fff3;--bui-border-danger:#f87a7a;--bui-border-warning:#e36d05;--bui-border-success:#53db83;--bui-ring:#fff3} \ No newline at end of file diff --git a/canon-docs/scripts/build-css.js b/canon-docs/scripts/build-css.js deleted file mode 100644 index dd9775150f..0000000000 --- a/canon-docs/scripts/build-css.js +++ /dev/null @@ -1,39 +0,0 @@ -const fs = require('fs'); -const path = require('path'); -const { bundle } = require('lightningcss'); - -const source = '../../packages/canon/src/css'; -const destination = '../public'; - -const source1 = path.join(__dirname, `${source}/core.css`); -const destination1 = path.join(__dirname, `${destination}/core.css`); -const source2 = path.join(__dirname, `${source}/components.css`); -const destination2 = path.join(__dirname, `${destination}/components.css`); - -// Function to bundle and copy the CSS file -const bundleAndCopyFile = async (source, destination) => { - try { - const result = await bundle({ - filename: source, - minify: true, - }); - - fs.writeFileSync(destination, result.code); - console.log('File bundled and copied successfully!'); - } catch (err) { - console.error('Error bundling file:', err); - } -}; - -// Initial bundle and copy -Promise.all([ - bundleAndCopyFile(source1, destination1), - bundleAndCopyFile(source2, destination2), -]) - .then(() => { - // Add an empty line after all operations are complete - It looks better in the terminal :) - console.log(''); - }) - .catch(err => { - console.error('Error in processing files:', err); - }); diff --git a/canon-docs/scripts/sync-css.js b/canon-docs/scripts/sync-css.js new file mode 100644 index 0000000000..fcafb46297 --- /dev/null +++ b/canon-docs/scripts/sync-css.js @@ -0,0 +1,149 @@ +const fs = require('fs'); +const path = require('path'); +const { bundle } = require('lightningcss'); +const chokidar = require('chokidar'); + +// Configuration +const config = { + canonPath: '../../packages/canon', + publicPath: '../public', + files: [ + { + source: 'css/styles.css', + destination: 'theme-backstage.css', + name: 'Main Styles', + }, + { + source: '.storybook/themes/spotify.css', + destination: 'theme-spotify.css', + name: 'Spotify Theme', + }, + ], +}; + +class CSSSync { + constructor() { + this.canonPath = path.resolve(__dirname, config.canonPath); + this.publicPath = path.resolve(__dirname, config.publicPath); + this.isWatching = process.argv.includes('--watch'); + } + + async syncFile(fileConfig) { + const sourcePath = path.join(this.canonPath, fileConfig.source); + const destPath = path.join(this.publicPath, fileConfig.destination); + + try { + // Check if source file exists + if (!fs.existsSync(sourcePath)) { + console.warn(`⚠️ Source file not found: ${sourcePath}`); + return false; + } + + // Ensure destination directory exists + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + + // Bundle and optimize CSS + const result = await bundle({ + filename: sourcePath, + minify: true, + }); + + // Write to destination + fs.writeFileSync(destPath, result.code); + + console.log( + `✅ ${fileConfig.name}: ${fileConfig.source} → ${fileConfig.destination}`, + ); + return true; + } catch (error) { + console.error(`❌ Error syncing ${fileConfig.name}:`, error.message); + return false; + } + } + + async syncAll() { + console.log('🔄 Syncing CSS files...\n'); + + let successCount = 0; + for (const fileConfig of config.files) { + if (await this.syncFile(fileConfig)) { + successCount++; + } + } + + console.log( + `\n✨ Synced ${successCount}/${config.files.length} CSS files successfully!`, + ); + + if (successCount > 0) { + console.log('\n📁 Available CSS files in public/:'); + config.files.forEach(file => { + const destPath = path.join(this.publicPath, file.destination); + if (fs.existsSync(destPath)) { + const stats = fs.statSync(destPath); + const size = (stats.size / 1024).toFixed(2); + console.log(` • ${file.destination} (${size} KB)`); + } + }); + } + } + + startWatching() { + console.log('👀 Watching for CSS changes...\n'); + + // Watch all source files + const watchPaths = config.files.map(file => + path.join(this.canonPath, file.source), + ); + + const watcher = chokidar.watch(watchPaths, { + ignored: /node_modules/, + persistent: true, + }); + + watcher.on('change', async filePath => { + console.log( + `\n🔄 Change detected: ${path.relative(this.canonPath, filePath)}`, + ); + + // Find which file changed and sync it + const fileConfig = config.files.find(file => + filePath.endsWith(file.source.replace(/\//g, path.sep)), + ); + + if (fileConfig) { + await this.syncFile(fileConfig); + } + }); + + watcher.on('error', error => console.error('❌ Watch error:', error)); + + // Handle process termination + process.on('SIGINT', () => { + console.log('\n👋 Stopping CSS sync...'); + watcher.close(); + process.exit(0); + }); + } + + async run() { + console.log('🎨 Canon CSS Sync Tool\n'); + console.log(`📂 Canon path: ${this.canonPath}`); + console.log(`📂 Public path: ${this.publicPath}\n`); + + // Initial sync + await this.syncAll(); + + // Watch for changes if requested + if (this.isWatching) { + this.startWatching(); + } + } +} + +// Run the sync tool +const cssSync = new CSSSync(); +cssSync.run().catch(error => { + console.error('❌ CSS Sync failed:', error); + process.exit(1); +}); diff --git a/canon-docs/src/app/(docs)/changelog/page.mdx b/canon-docs/src/app/(docs)/changelog/page.mdx new file mode 100644 index 0000000000..7cc0b2bf3e --- /dev/null +++ b/canon-docs/src/app/(docs)/changelog/page.mdx @@ -0,0 +1,13 @@ +import { Changelog } from '@/components/Changelog'; + +# Changelog + + + +## Version 0.1.0 + +We're excited to share the initial release of Canon 💚 In this first alpha version, +you'll find the foundation of our design system: a set of versatile layout components +and a handful of essential atomic elements to help you get started. While Canon is +still in its early stages, it's ready for exploration and we'd love for you to give +it a try and share your feedback. diff --git a/canon-docs/src/app/(docs)/components/[slug]/page.tsx b/canon-docs/src/app/(docs)/components/[slug]/page.tsx new file mode 100644 index 0000000000..d6f98c8f93 --- /dev/null +++ b/canon-docs/src/app/(docs)/components/[slug]/page.tsx @@ -0,0 +1,25 @@ +import { components, layoutComponents } from '@/utils/data'; + +export default async function Page({ + params, +}: { + params: Promise<{ slug: string }>; +}) { + const { slug } = await params; + + const { default: Component } = await import( + `@/content/components/${slug}.mdx` + ); + + return ; +} + +export function generateStaticParams() { + const list = [...components, ...layoutComponents]; + + return list.map(component => ({ + slug: component.slug, + })); +} + +export const dynamicParams = false; diff --git a/canon-docs/src/app/(docs)/components/avatar/page.mdx b/canon-docs/src/app/(docs)/components/avatar/page.mdx deleted file mode 100644 index b46caf8d62..0000000000 --- a/canon-docs/src/app/(docs)/components/avatar/page.mdx +++ /dev/null @@ -1,75 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { AvatarSnippet } from '@/snippets/stories-snippets'; -import { avatarPropDefs } from './props'; - -# Avatar - -An avatar component with a fallback for initials. - -} - code={``} -/> - - - - Usage - Theming - - - `} - /> - - - 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-AvatarRoot` - - `canon-AvatarRoot[data-size='small']` - - `canon-AvatarRoot[data-size='medium']` - - `canon-AvatarRoot[data-size='large']` - - `canon-AvatarImage` - - `canon-AvatarFallback` - - - -## API reference - - - -## Examples - -### Sizes - -Avatar sizes can be set using the `size` prop. - -} - code={` - - - - `} -/> - -### Fallback - -If the image is not available, the avatar will show the initials of the name. - -} - code={``} -/> diff --git a/canon-docs/src/app/(docs)/components/avatar/props.ts b/canon-docs/src/app/(docs)/components/avatar/props.ts deleted file mode 100644 index e86c67a41e..0000000000 --- a/canon-docs/src/app/(docs)/components/avatar/props.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; -import type { PropDef } from '@/utils/propDefs'; - -export const avatarPropDefs: Record = { - src: { - type: 'string', - }, - name: { - type: 'string', - }, - size: { - type: 'enum', - values: ['small', 'medium', 'large'], - default: 'medium', - responsive: true, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/box/props.ts b/canon-docs/src/app/(docs)/components/box/props.ts deleted file mode 100644 index f3e38984dd..0000000000 --- a/canon-docs/src/app/(docs)/components/box/props.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { - classNamePropDefs, - displayPropDefs, - heightPropDefs, - positionPropDefs, - stylePropDefs, - widthPropDefs, -} from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const boxPropDefs: Record = { - as: { - type: 'enum', - values: ['div', 'span'], - default: 'div', - responsive: true, - }, - ...widthPropDefs, - ...heightPropDefs, - ...positionPropDefs, - ...displayPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/button/page.mdx b/canon-docs/src/app/(docs)/components/button/page.mdx deleted file mode 100644 index 3554ee39f9..0000000000 --- a/canon-docs/src/app/(docs)/components/button/page.mdx +++ /dev/null @@ -1,150 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { ButtonSnippet } from '@/snippets/stories-snippets'; -import { buttonVariants } from '@/snippets/code-snippets'; -import { buttonPropDefs } from './props'; - -# Button - -A button component that can be used to trigger actions. - -} - code={` - - - -`} -/> - - - - Usage - Theming - - - -`} - /> - - - 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. - `} - /> - - - - -## API reference - - - -## Examples - -### Variants - -Here's a view when buttons have different variants. - -} - code={buttonVariants} -/> - -### Sizes - -Here's a view when buttons have different sizes. - -} - code={` - - -`} -/> - -### With Icons - -Here's a view when buttons have icons. - -} - code={` - - - -`} -/> - -### Full width - -Here's a view when buttons are full width. - -} - code={` - -`} -/> - -### Disabled - -Here's a view when buttons are disabled. - -} - code={``} -/> - -### As Link - -Here's a view when buttons are rendered as a link. - -} - code={``} -/> - -### Responsive - -Here's a view when buttons are responsive. - - - Responsive Button - `} -/> diff --git a/canon-docs/src/app/(docs)/components/button/props.ts b/canon-docs/src/app/(docs)/components/button/props.ts deleted file mode 100644 index 36b0f73066..0000000000 --- a/canon-docs/src/app/(docs)/components/button/props.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const buttonPropDefs: Record = { - variant: { - type: 'enum', - values: ['primary', 'secondary'], - default: 'primary', - responsive: true, - }, - size: { - type: 'enum', - values: ['small', 'medium'], - default: 'medium', - responsive: true, - }, - render: { - type: 'enum', - values: ['ReactNode'], - responsive: false, - default: ' - )} /> - - - It's the edge of the world and all of Western civilization - The sun may rise in the East, at least it settled in a final location - It's understood that Hollywood sells Californication - - -`} -/> - - - - Usage - Theming - - - - ( - - )} /> - Your content -`} - /> - - - 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-CollapsibleRoot` - - `canon-CollapsibleTrigger` - - `canon-CollapsiblePanel` - - - -## API reference - -### Collapsible.Root - -Groups all parts of the collapsible. Renders a `
` element. - - - -### Collapsible.Trigger - -The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend -using the `render` prop to render a custom trigger. - - - )} /> - - - It's the edge of the world and all of Western civilization - The sun may rise in the East, at least it settled in a final location - It's understood that Hollywood sells Californication - - -`} -/> diff --git a/canon-docs/src/app/(docs)/components/collapsible/props.ts b/canon-docs/src/app/(docs)/components/collapsible/props.ts deleted file mode 100644 index 02c62dc89e..0000000000 --- a/canon-docs/src/app/(docs)/components/collapsible/props.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { - classNamePropDefs, - stylePropDefs, - renderPropDefs, -} from '@/utils/propDefs'; -import type { PropDef } from '@/utils/propDefs'; - -export const collapsibleRootPropDefs: Record = { - defaultOpen: { - type: 'boolean', - default: 'false', - }, - open: { - type: 'boolean', - }, - onOpenChange: { - type: 'enum', - values: ['(open) => void'], - }, - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const collapsibleTriggerPropDefs: Record = { - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const collapsiblePanelPropDefs: Record = { - hiddenUntilFound: { - type: 'boolean', - default: 'false', - }, - keepMounted: { - type: 'boolean', - default: 'false', - }, - ...renderPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/container/props.ts b/canon-docs/src/app/(docs)/components/container/props.ts deleted file mode 100644 index 4a3410ec12..0000000000 --- a/canon-docs/src/app/(docs)/components/container/props.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { - classNamePropDefs, - stylePropDefs, - gapPropDefs, -} from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const containerPropDefs: Record = { - ...gapPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/flex/page.mdx b/canon-docs/src/app/(docs)/components/flex/page.mdx deleted file mode 100644 index 8150f3e3c5..0000000000 --- a/canon-docs/src/app/(docs)/components/flex/page.mdx +++ /dev/null @@ -1,83 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { - flexFAQ1, - flexSimple, - flexResponsive, - flexAlign, -} from '@/snippets/code-snippets'; -import { Snippet } from '@/components/Snippet'; -import { FlexSnippet } from '@/snippets/stories-snippets'; -import { flexPropDefs } from './props'; -import { spacingPropDefs } from '../../../../utils/propDefs'; - -# Flex - -This is the flex container component. It will help to define the number of -columns that will be used in the grid. You can also define the gap between the -columns. All values are responsive. - -} - code={` - - - -`} -/> - - - - Usage - - - -`} - /> - - - -## API reference - - - -The grid component also accepts all the spacing props from the Box component. - - - -## Common questions - -### Can I stack horizontally? - -The Flex component only allows for stacking elements vertically. If you want -to create a column layout, please use the Grid component. - - - -## Examples - -### Simple - -A simple example of how to use the Flex component. - - - -### Responsive - -The Flex component also supports responsive values, making it easy to create -responsive designs. - - - -### Align - -The Flex component also supports responsive alignment, making it easy to -create responsive designs. - - diff --git a/canon-docs/src/app/(docs)/components/flex/props.ts b/canon-docs/src/app/(docs)/components/flex/props.ts deleted file mode 100644 index 2268a6909d..0000000000 --- a/canon-docs/src/app/(docs)/components/flex/props.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { - PropDef, - childrenPropDefs, - classNamePropDefs, - stylePropDefs, -} from '../../../../utils/propDefs'; - -export const flexPropDefs: Record = { - align: { - type: 'enum', - values: ['start', 'center', 'end', 'baseline', 'stretch'], - responsive: true, - }, - direction: { - type: 'enum', - values: ['row', 'column', 'row-reverse', 'column-reverse'], - responsive: true, - }, - justify: { - type: 'enum', - values: ['start', 'center', 'end', 'between'], - responsive: true, - }, - ...childrenPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/grid/props.ts b/canon-docs/src/app/(docs)/components/grid/props.ts deleted file mode 100644 index a9e8717fa1..0000000000 --- a/canon-docs/src/app/(docs)/components/grid/props.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { - PropDef, - childrenPropDefs, - classNamePropDefs, - stylePropDefs, -} from '../../../../utils/propDefs'; - -const columnsValues = [ - '1', - '2', - '3', - '4', - '5', - '6', - '7', - '8', - '9', - '10', - '11', - '12', -]; - -export const gridPropDefs: Record = { - columns: { - type: 'enum | string', - values: [...columnsValues, 'auto'], - responsive: true, - default: 'auto', - }, - ...childrenPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; - -export const gridItemPropDefs: Record = { - colSpan: { - type: 'enum | string', - values: [...columnsValues, 'full'], - responsive: true, - }, - rowSpan: { - type: 'enum | string', - values: [...columnsValues, 'full'], - responsive: true, - }, - start: { - type: 'enum | string', - values: [...columnsValues, 'auto'], - responsive: true, - }, - end: { - type: 'enum | string', - values: [...columnsValues, 'auto'], - responsive: true, - }, - ...childrenPropDefs, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/heading/page.mdx b/canon-docs/src/app/(docs)/components/heading/page.mdx deleted file mode 100644 index a5eb488411..0000000000 --- a/canon-docs/src/app/(docs)/components/heading/page.mdx +++ /dev/null @@ -1,102 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { HeadingSnippet } from '@/snippets/stories-snippets'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { headingPropDefs } from './props'; - -# Heading - -Headings are used to structure the content of your page. - -} - code={`Hello World!`} -/> - - - - Usage - Theming - - - -`} - /> - - - 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. - `} - /> - - - - -## API reference - - - -## Examples - -### All variants - -The `Heading` component has a `variant` prop that can be used to change the -appearance of the heading. - -} - code={` - Display - Title 1 - Title 2 - Title 3 - Title 4 - Title 5 -`} -/> - -### 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? - `} -/> - -### Custom render - -You can also use the `render` prop to render the heading as a different element. - -} - code={`}>Custom render`} -/> - -### Responsive - -You can also use the `variant` prop to change the appearance of the text based -on the screen size. - - - Responsive heading - `} -/> diff --git a/canon-docs/src/app/(docs)/components/heading/props.ts b/canon-docs/src/app/(docs)/components/heading/props.ts deleted file mode 100644 index 6d62d2dee1..0000000000 --- a/canon-docs/src/app/(docs)/components/heading/props.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const headingPropDefs: Record = { - variant: { - type: 'enum', - values: ['display', 'title1', 'title2', 'title3', 'title4', 'title5'], - responsive: true, - }, - children: { - type: 'enum', - values: ['ReactNode'], - responsive: false, - }, - truncate: { - type: 'boolean', - default: 'false', - }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/icon-button/page.mdx b/canon-docs/src/app/(docs)/components/icon-button/page.mdx deleted file mode 100644 index 60530ec9ea..0000000000 --- a/canon-docs/src/app/(docs)/components/icon-button/page.mdx +++ /dev/null @@ -1,97 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { IconButtonSnippet } from '@/snippets/stories-snippets'; -import { iconButtonVariants } from '@/snippets/code-snippets'; -import { iconButtonPropDefs } from './props'; - -# Icon Button - -A button component with a single icon that can be used to trigger actions. - -} - code={` - - - `} -/> - - - - Usage - Theming - - - -`} - /> - - - 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. - `} - /> - - - - -## API reference - - - -## Examples - -### Variants - -Here's a view when buttons have different variants. - -} - code={iconButtonVariants} -/> - -### Sizes - -Here's a view when buttons have different sizes. - -} - code={` - - -`} -/> - -### Disabled - -Here's a view when buttons are disabled. - -} - code={``} -/> - -### Responsive - -Here's a view when buttons are responsive. - -`} -/> diff --git a/canon-docs/src/app/(docs)/components/icon-button/props.ts b/canon-docs/src/app/(docs)/components/icon-button/props.ts deleted file mode 100644 index bcb1580247..0000000000 --- a/canon-docs/src/app/(docs)/components/icon-button/props.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '../../../../utils/propDefs'; -import type { PropDef } from '../../../../utils/propDefs'; - -export const iconButtonPropDefs: Record = { - variant: { - type: 'enum', - values: ['primary', 'secondary'], - default: 'primary', - responsive: true, - }, - size: { - type: 'enum', - values: ['small', 'medium'], - default: 'medium', - responsive: true, - }, - icon: { type: 'enum', values: 'icon', responsive: false }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/icon/page.mdx b/canon-docs/src/app/(docs)/components/icon/page.mdx deleted file mode 100644 index 66be460d13..0000000000 --- a/canon-docs/src/app/(docs)/components/icon/page.mdx +++ /dev/null @@ -1,44 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { IconSnippet } from '@/snippets/stories-snippets'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { iconPropDefs } from './props'; - -# Icon - -Icons are used to represent an action or a state. - -} - code={``} -/> - - - - Usage - Theming - - - -`} - /> - - - 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. - `} - /> - - - - -## API reference - - diff --git a/canon-docs/src/app/(docs)/components/link/page.mdx b/canon-docs/src/app/(docs)/components/link/page.mdx deleted file mode 100644 index 64a3261590..0000000000 --- a/canon-docs/src/app/(docs)/components/link/page.mdx +++ /dev/null @@ -1,84 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { - MenuSnippet, - ButtonSnippet, - LinkSnippet, -} from '@/snippets/stories-snippets'; -import { linkPropDefs } from './props'; - -# Link - -A link component that renders a `` element. - -} - code={`Sign up for Backstage`} -/> - - - - Usage - Theming - - - Sign up for Backstage`} - /> - - - 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-Link` - - `canon-Link[data-variant='body']` - - `canon-Link[data-variant='subtitle']` - - `canon-Link[data-variant='caption']` - - `canon-Link[data-variant='label']` - - `canon-Link[data-weight='regular']` - - `canon-Link[data-weight='bold']` - - - -## API reference - - - -## Examples - -### Variants - -Here's a view when links have different variants. - -} - code={` - - - - - `} -/> - -### Weights - -Here's a view when links have different weights. - -} - code={` - - - `} -/> diff --git a/canon-docs/src/app/(docs)/components/link/props.ts b/canon-docs/src/app/(docs)/components/link/props.ts deleted file mode 100644 index 83507c5594..0000000000 --- a/canon-docs/src/app/(docs)/components/link/props.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { classNamePropDefs, stylePropDefs } from '@/utils/propDefs'; -import type { PropDef } from '@/utils/propDefs'; - -export const linkPropDefs: Record = { - to: { - type: 'string', - }, - variant: { - type: 'enum', - values: ['subtitle', 'body', 'caption', 'label'], - default: 'body', - responsive: true, - }, - weight: { - type: 'enum', - values: ['regular', 'bold'], - default: 'regular', - responsive: true, - }, - ...classNamePropDefs, - ...stylePropDefs, -}; diff --git a/canon-docs/src/app/(docs)/components/menu/page.mdx b/canon-docs/src/app/(docs)/components/menu/page.mdx deleted file mode 100644 index 524db26cd1..0000000000 --- a/canon-docs/src/app/(docs)/components/menu/page.mdx +++ /dev/null @@ -1,202 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { MenuSnippet } from '@/snippets/stories-snippets'; -import { BaseUI } from '@/components/HeadlessBanners/BaseUI'; -import { - menuRootPropDefs, - menuTriggerPropDefs, - menuPositionerPropDefs, - menuItemPropDefs, -} from './props'; - -# Menu - -A list of actions in a dropdown, enhanced with keyboard navigation. - -} - code={` - ( - - )} - /> - - - - Settings - Invite new members - Download app - Log out - - - - `} -/> - - - - Usage - Theming - - - - - - - - - - - - `} - /> - - - 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-MenuTrigger` - - `canon-MenuBackdrop` - - `canon-MenuPositioner` - - `canon-MenuPopup` - - `canon-MenuArrow` - - `canon-MenuItem` - - `canon-MenuGroup` - - `canon-MenuGroupLabel` - - `canon-MenuRadioGroup` - - `canon-MenuRadioItem` - - `canon-MenuRadioItemIndicator` - - `canon-MenuCheckboxItem` - - `canon-MenuCheckboxItemIndicator` - - `canon-MenuSubmenuTrigger` - - `canon-MenuSeparator` - - - -## API reference - - - -### Menu.Root - -Groups all parts of the menu. Doesn't render its own HTML element. - - - -### Menu.Trigger - -The trigger by default render a simple unstyled button. Because menus can be rendered in different ways, we recommend -using the `render` prop to render a custom trigger. - - - )} - /> - - - - Settings - Invite new members - Download app - Log out - - - - `} -/> - -### 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/select/page.mdx b/canon-docs/src/app/(docs)/components/select/page.mdx deleted file mode 100644 index 4399f62235..0000000000 --- a/canon-docs/src/app/(docs)/components/select/page.mdx +++ /dev/null @@ -1,128 +0,0 @@ -import { PropsTable } from '@/components/PropsTable'; -import { Snippet } from '@/components/Snippet'; -import { Tabs } from '@/components/Tabs'; -import { CodeBlock } from '@/components/CodeBlock'; -import { SelectSnippet } from '@/snippets/stories-snippets'; -import { selectPropDefs } from './props'; - -# Select - -A common form component for choosing a predefined value in a dropdown menu. - -} - code={``} -/> - -### Sizes - -Here's a view when the selects have different sizes. - -} - code={` - - `} -/> - -### Disabled - -Here's a view when the select is disabled. - -} - code={``; + +export const selectDefaultSnippet = ``; + +export const selectIconSnippet = ` + `; + +export const selectResponsiveSnippet = ` { + selectedKey={pageSize?.toString()} + onSelectionChange={value => { table?.setPageSize(Number(value)); }} - className="canon-DataTablePagination--select" + className="bui-DataTablePagination--select" /> )}
-
+
{`${fromCount} - ${toCount} of ${rowCount}`} - table?.previousPage()} - disabled={!table?.getCanPreviousPage()} + isDisabled={!table?.getCanPreviousPage()} icon={} /> - table?.nextPage()} - disabled={!table?.getCanNextPage()} + isDisabled={!table?.getCanNextPage()} icon={} />
diff --git a/packages/canon/src/components/DataTable/Root/DataTableRoot.styles.css b/packages/canon/src/components/DataTable/Root/DataTableRoot.styles.css index 6cdd5fb390..21b9be4eb8 100644 --- a/packages/canon/src/components/DataTable/Root/DataTableRoot.styles.css +++ b/packages/canon/src/components/DataTable/Root/DataTableRoot.styles.css @@ -1,5 +1,5 @@ -.canon-DataTableRoot { +.bui-DataTableRoot { display: flex; flex-direction: column; - gap: var(--canon-space-3); + gap: var(--bui-space-3); } diff --git a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx index 8207fe4412..2e3f7a7c6d 100644 --- a/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx +++ b/packages/canon/src/components/DataTable/Root/DataTableRoot.tsx @@ -38,11 +38,7 @@ const DataTableRoot = forwardRef( return ( -
+
); }, diff --git a/packages/canon/src/components/FieldError/FieldError.stories.tsx b/packages/canon/src/components/FieldError/FieldError.stories.tsx new file mode 100644 index 0000000000..39714d16d2 --- /dev/null +++ b/packages/canon/src/components/FieldError/FieldError.stories.tsx @@ -0,0 +1,87 @@ +/* + * 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 { TextField, Input, Form } from 'react-aria-components'; +import { FieldError } from './FieldError'; + +const meta = { + title: 'Forms/FieldError', + component: FieldError, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +// Show error with server validation using Form component +export const WithServerValidation: Story = { + render: () => ( +
+ + + + +
+ ), +}; + +// Show error using children +export const WithCustomMessage: Story = { + render: () => ( + + + This is a custom error message. + + ), +}; + +// Show error with render prop function +export const WithRenderProp: Story = { + render: () => ( + 'This field is invalid'} + style={{ + display: 'flex', + flexDirection: 'column', + alignItems: 'flex-start', + }} + > + + + {({ validationErrors }) => + validationErrors.length > 0 ? validationErrors[0] : 'Field is invalid' + } + + + ), +}; diff --git a/packages/canon/src/components/FieldError/FieldError.styles.css b/packages/canon/src/components/FieldError/FieldError.styles.css new file mode 100644 index 0000000000..e69e8637d3 --- /dev/null +++ b/packages/canon/src/components/FieldError/FieldError.styles.css @@ -0,0 +1,7 @@ +.bui-FieldError { + display: inline-block; + color: var(--bui-fg-danger); + font-size: var(--bui-font-size-2); + font-weight: var(--bui-font-weight-regular); + margin-top: var(--bui-space-2); +} diff --git a/packages/canon/src/components/Button/Button.props.ts b/packages/canon/src/components/FieldError/FieldError.tsx similarity index 51% rename from packages/canon/src/components/Button/Button.props.ts rename to packages/canon/src/components/FieldError/FieldError.tsx index e363360afa..f216cd6bb8 100644 --- a/packages/canon/src/components/Button/Button.props.ts +++ b/packages/canon/src/components/FieldError/FieldError.tsx @@ -14,28 +14,26 @@ * limitations under the License. */ -import type { PropDef, GetPropDefTypes } from '../../props/prop-def'; +import { forwardRef } from 'react'; +import { + FieldError as AriaFieldError, + type FieldErrorProps, +} from 'react-aria-components'; +import clsx from 'clsx'; /** @public */ -export const buttonPropDefs = { - variant: { - type: 'enum', - values: ['primary', 'secondary'], - className: 'canon-Button--variant', - default: 'primary', - responsive: true, - }, - size: { - type: 'enum', - values: ['small', 'medium'], - className: 'canon-Button--size', - default: 'medium', - responsive: true, - }, -} satisfies { - variant: PropDef<'primary' | 'secondary'>; - size: PropDef<'small' | 'medium'>; -}; +export const FieldError = forwardRef( + (props: FieldErrorProps, ref) => { + const { className, ...rest } = props; -/** @public */ -export type ButtonOwnProps = GetPropDefTypes; + return ( + + ); + }, +); + +FieldError.displayName = 'FieldError'; diff --git a/packages/canon/src/components/FieldError/index.ts b/packages/canon/src/components/FieldError/index.ts new file mode 100644 index 0000000000..5b61f83ace --- /dev/null +++ b/packages/canon/src/components/FieldError/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ + +export * from './FieldError'; diff --git a/packages/canon/src/components/FieldLabel/FieldLabel.stories.tsx b/packages/canon/src/components/FieldLabel/FieldLabel.stories.tsx new file mode 100644 index 0000000000..a82639eca0 --- /dev/null +++ b/packages/canon/src/components/FieldLabel/FieldLabel.stories.tsx @@ -0,0 +1,65 @@ +/* + * 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 { FieldLabel } from './FieldLabel'; + +const meta = { + title: 'Forms/FieldLabel', + component: FieldLabel, + argTypes: { + label: { + control: 'text', + }, + secondaryLabel: { + control: 'text', + }, + description: { + control: 'text', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + label: 'Label', + }, +}; + +export const WithSecondaryLabel: Story = { + args: { + ...Default.args, + secondaryLabel: 'Secondary Label', + }, +}; + +export const WithDescription: Story = { + args: { + ...Default.args, + description: 'Description', + }, +}; + +export const WithAllFields: Story = { + args: { + ...Default.args, + secondaryLabel: 'Secondary Label', + description: 'Description', + }, +}; diff --git a/packages/canon/src/components/FieldLabel/FieldLabel.styles.css b/packages/canon/src/components/FieldLabel/FieldLabel.styles.css new file mode 100644 index 0000000000..2dfc28a49d --- /dev/null +++ b/packages/canon/src/components/FieldLabel/FieldLabel.styles.css @@ -0,0 +1,27 @@ +.bui-FieldLabelWrapper { + display: flex; + flex-direction: column; + margin-bottom: var(--bui-space-3); + gap: var(--bui-space-1); +} + +.bui-FieldLabel { + color: var(--bui-fg-primary); + margin-right: auto; + cursor: pointer; + font-weight: var(--bui-font-weight-regular); + font-size: var(--bui-font-size-2); +} + +.bui-FieldSecondaryLabel { + color: var(--bui-fg-secondary); + font-weight: var(--bui-font-weight-regular); + margin-left: var(--bui-space-1); +} + +.bui-FieldDescription { + font-weight: var(--bui-font-weight-regular); + font-size: var(--bui-font-size-2); + color: var(--bui-fg-secondary); + margin: 0; +} diff --git a/packages/canon/src/components/FieldLabel/FieldLabel.tsx b/packages/canon/src/components/FieldLabel/FieldLabel.tsx new file mode 100644 index 0000000000..df0735389a --- /dev/null +++ b/packages/canon/src/components/FieldLabel/FieldLabel.tsx @@ -0,0 +1,50 @@ +/* + * 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 { Label } from 'react-aria-components'; +import { forwardRef } from 'react'; +import type { FieldLabelProps } from './types'; +import { useStyles } from '../../hooks/useStyles'; + +/** @public */ +export const FieldLabel = forwardRef( + (props: FieldLabelProps, ref) => { + const { label, secondaryLabel, description, htmlFor, id, ...rest } = props; + + const { classNames } = useStyles('FieldLabel'); + + if (!label) return null; + + return ( +
+ {label && ( + + )} + {description && ( +
{description}
+ )} +
+ ); + }, +); + +FieldLabel.displayName = 'FieldLabel'; diff --git a/packages/canon/src/components/FieldLabel/index.ts b/packages/canon/src/components/FieldLabel/index.ts new file mode 100644 index 0000000000..b9cebb1bf7 --- /dev/null +++ b/packages/canon/src/components/FieldLabel/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export * from './FieldLabel'; +export * from './types'; diff --git a/packages/canon/src/components/FieldLabel/types.ts b/packages/canon/src/components/FieldLabel/types.ts new file mode 100644 index 0000000000..dc4e444133 --- /dev/null +++ b/packages/canon/src/components/FieldLabel/types.ts @@ -0,0 +1,43 @@ +/* + * 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. + */ + +/** @public */ +export interface FieldLabelProps { + /** + * The label of the text field + */ + label?: string | null; + + /** + * The secondary label of the text field + */ + secondaryLabel?: string | null; + + /** + * The description of the text field + */ + description?: string | null; + + /** + * The HTML for attribute of the text field + */ + htmlFor?: string; + + /** + * The id of the text field + */ + id?: string; +} diff --git a/packages/canon/src/components/Flex/Flex.props.ts b/packages/canon/src/components/Flex/Flex.props.ts index 97a602b42f..b1d9c019d3 100644 --- a/packages/canon/src/components/Flex/Flex.props.ts +++ b/packages/canon/src/components/Flex/Flex.props.ts @@ -29,19 +29,19 @@ const justifyValues = ['start', 'center', 'end', 'between'] as const; const flexPropDefs = { align: { type: 'enum', - className: 'cu-align', + className: 'bui-align', values: alignValues, responsive: true, }, direction: { type: 'enum', - className: 'cu-fd', + className: 'bui-fd', values: directionValues, responsive: true, }, justify: { type: 'enum', - className: 'cu-jc', + className: 'bui-jc', values: justifyValues, responsive: true, }, diff --git a/packages/canon/src/components/Flex/Flex.tsx b/packages/canon/src/components/Flex/Flex.tsx index a53e0d28f4..ded4fffffa 100644 --- a/packages/canon/src/components/Flex/Flex.tsx +++ b/packages/canon/src/components/Flex/Flex.tsx @@ -21,6 +21,7 @@ import { flexPropDefs } from './Flex.props'; import { extractProps } from '../../utils/extractProps'; import { gapPropDefs } from '../../props/gap-props'; import { spacingPropDefs } from '../../props/spacing.props'; +import { useStyles } from '../../hooks/useStyles'; /** @public */ export const Flex = forwardRef((props, ref) => { @@ -30,11 +31,12 @@ export const Flex = forwardRef((props, ref) => { ...spacingPropDefs, }; + const { classNames } = useStyles('Flex'); const { className, style } = extractProps(props, propDefs); return createElement('div', { ref, - className: clsx('canon-Flex', className), + className: clsx(classNames.root, className), style, children: props.children, }); diff --git a/packages/canon/src/components/Flex/styles.css b/packages/canon/src/components/Flex/styles.css index 61fb53d257..96c94ab58a 100644 --- a/packages/canon/src/components/Flex/styles.css +++ b/packages/canon/src/components/Flex/styles.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.canon-Flex { +.bui-Flex { display: flex; /* This helps when using `truncate` on text inside a flex container */ diff --git a/packages/canon/src/components/Grid/Grid.props.ts b/packages/canon/src/components/Grid/Grid.props.ts index a918af6187..b6997cb01b 100644 --- a/packages/canon/src/components/Grid/Grid.props.ts +++ b/packages/canon/src/components/Grid/Grid.props.ts @@ -36,7 +36,7 @@ const columnsValues = [ const gridPropDefs = { columns: { type: 'enum | string', - className: 'cu-columns', + className: 'bui-columns', customProperties: ['--columns'], values: columnsValues, responsive: true, @@ -50,28 +50,28 @@ const gridPropDefs = { const gridItemPropDefs = { colSpan: { type: 'enum | string', - className: 'cu-col-span', + className: 'bui-col-span', customProperties: ['--col-span'], values: columnsValues, responsive: true, }, colEnd: { type: 'enum | string', - className: 'cu-col-end', + className: 'bui-col-end', customProperties: ['--col-end'], values: columnsValues, responsive: true, }, colStart: { type: 'enum | string', - className: 'cu-col-start', + className: 'bui-col-start', customProperties: ['--col-start'], values: columnsValues, responsive: true, }, rowSpan: { type: 'enum | string', - className: 'cu-row-span', + className: 'bui-row-span', customProperties: ['--row-span'], values: columnsValues, responsive: true, diff --git a/packages/canon/src/components/Grid/Grid.tsx b/packages/canon/src/components/Grid/Grid.tsx index 7bb5486a2d..27257c8972 100644 --- a/packages/canon/src/components/Grid/Grid.tsx +++ b/packages/canon/src/components/Grid/Grid.tsx @@ -21,6 +21,7 @@ import { gridItemPropDefs, gridPropDefs } from './Grid.props'; import clsx from 'clsx'; import type { GridItemProps, GridProps } from './types'; import { spacingPropDefs } from '../../props/spacing.props'; +import { useStyles } from '../../hooks/useStyles'; const GridRoot = forwardRef((props, ref) => { const propDefs = { @@ -29,11 +30,13 @@ const GridRoot = forwardRef((props, ref) => { ...spacingPropDefs, }; + const { classNames } = useStyles('Grid'); + const { className, style } = extractProps(props, propDefs); return createElement('div', { ref, - className: clsx('canon-Grid', className), + className: clsx(classNames.root, className), style, children: props.children, }); @@ -44,11 +47,12 @@ const GridItem = forwardRef((props, ref) => { ...gridItemPropDefs, }; + const { classNames } = useStyles('Grid'); const { className, style } = extractProps(props, propDefs); return createElement('div', { ref, - className: clsx('canon-GridItem', className), + className: clsx(classNames.item, className), style, children: props.children, }); diff --git a/packages/canon/src/components/Grid/styles.css b/packages/canon/src/components/Grid/styles.css index 0bb9267795..e21191ad47 100644 --- a/packages/canon/src/components/Grid/styles.css +++ b/packages/canon/src/components/Grid/styles.css @@ -1,3 +1,3 @@ -.canon-Grid { +.bui-Grid { display: grid; } diff --git a/packages/canon/src/components/Header/Header.stories.tsx b/packages/canon/src/components/Header/Header.stories.tsx new file mode 100644 index 0000000000..da33b1a0f5 --- /dev/null +++ b/packages/canon/src/components/Header/Header.stories.tsx @@ -0,0 +1,338 @@ +/* + * 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, StoryFn } from '@storybook/react'; +import { Header } from './Header'; +import { HeaderBreadcrumb, HeaderMenuItem, HeaderTab } from './types'; +import { Button } from '../Button'; +import { HeaderPage } from '../HeaderPage'; +import { MemoryRouter } from 'react-router-dom'; +import { Container } from '../Container'; +import { Text } from '../Text'; +import { ButtonIcon } from '../ButtonIcon'; +import { + RiHeartLine, + RiEmotionHappyLine, + RiCloudy2Line, +} from '@remixicon/react'; + +const meta = { + title: 'Components/Header', + component: Header, + parameters: { + layout: 'fullscreen', + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const withRouter = (Story: StoryFn) => ( + + + +); + +const tabs: HeaderTab[] = [ + { + id: 'overview', + label: 'Overview', + }, + { + id: 'checks', + label: 'Checks', + }, + { + id: 'tracks', + label: 'Tracks', + }, + { + id: 'campaigns', + label: 'Campaigns', + href: '/campaigns', + }, + { + id: 'integrations', + label: 'Integrations', + href: '/integrations', + }, +]; + +const tabs2: HeaderTab[] = [ + { + id: 'Banana', + label: 'Banana', + }, + { + id: 'Apple', + label: 'Apple', + }, + { + id: 'Orange', + label: 'Orange', + }, +]; + +const breadcrumbs: HeaderBreadcrumb[] = [ + { + label: 'Home', + href: '/', + }, + { + label: 'Dashboard', + href: '/dashboard', + }, + { + label: 'Settings', + href: '/settings', + }, +]; + +const menuItems: HeaderMenuItem[] = [ + { + label: 'Settings', + value: 'settings', + }, + { + label: 'Invite new members', + value: 'invite-new-members', + }, +]; + +// Extract layout decorator as a reusable constant +const layoutDecorator = [ + (Story: StoryFn) => ( + <> +
+
+ + + + Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, + quos. + + +
+
+ + ), + withRouter, +]; + +export const Default: Story = { + args: {}, + decorators: [withRouter], +}; + +export const WithTabs: Story = { + args: { + tabs, + }, + decorators: [withRouter], +}; + +export const WithOptions: Story = { + args: { + menuItems, + }, + decorators: [withRouter], +}; + +export const WithCustomActions: Story = { + args: { + customActions: , + menuItems, + }, + decorators: [withRouter], +}; + +export const WithBreadcrumbs: Story = { + args: { + breadcrumbs, + }, + decorators: [withRouter], +}; + +export const WithAllComponents: Story = { + args: { + menuItems, + tabs, + breadcrumbs, + }, + decorators: [withRouter], +}; + +export const WithLayout: Story = { + args: { + menuItems, + breadcrumbs, + }, + decorators: layoutDecorator, + render: args => ( + <> +
+ Custom action} + /> + + ), +}; + +export const WithLayoutNoTabs: Story = { + args: { + menuItems, + breadcrumbs, + }, + decorators: layoutDecorator, + render: args => ( + <> +
+ + + ), +}; + +export const WithEverything: Story = { + args: { + menuItems, + breadcrumbs, + tabs, + }, + decorators: layoutDecorator, + render: args => ( + <> +
+ } /> + } /> + } /> + + } + /> + + + + + } + /> + + ), +}; + +export const WithMockedURLCampaigns: Story = { + args: { + tabs, + }, + render: args => ( + +
+ + + Current URL is mocked to be: /campaigns + + + Notice how the "Campaigns" tab is selected (highlighted) because it + matches the current path. + + + + ), +}; + +export const WithMockedURLIntegrations: Story = { + args: { + tabs, + }, + render: args => ( + +
+ + + Current URL is mocked to be: /integrations + + + Notice how the "Integrations" tab is selected (highlighted) because it + matches the current path. + + + + ), +}; + +export const WithMockedURLNoMatch: Story = { + args: { + tabs, + }, + render: args => ( + +
+ + + Current URL is mocked to be: /some-other-page + + + No tab is selected because the current path doesn't match any tab's + href. + + + Tabs without href (like "Overview", "Checks", "Tracks") fall back to + React Aria's internal state. + + + + ), +}; diff --git a/packages/canon/src/components/Header/Header.styles.css b/packages/canon/src/components/Header/Header.styles.css new file mode 100644 index 0000000000..b978033561 --- /dev/null +++ b/packages/canon/src/components/Header/Header.styles.css @@ -0,0 +1,129 @@ +/* + * 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. + */ + +.bui-HeaderToolbar { + position: sticky; + top: 0; + z-index: 10; + margin-bottom: var(--bui-space-6); + + &::before { + content: ''; + position: absolute; + top: 0; + left: 0px; + right: 0px; + height: 16px; + background-color: var(--bui-bg); + z-index: 0; + } + + &[data-has-tabs='true'] { + margin-bottom: 0; + } +} + +.bui-HeaderToolbarWrapper { + position: relative; + z-index: 1; + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + background-color: var(--bui-bg-surface-1); + padding-inline: var(--bui-space-5); + border-bottom: 1px solid var(--bui-border); + color: var(--bui-fg-primary); + height: 52px; +} + +.bui-HeaderToolbarContent { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); +} + +.bui-HeaderToolbarName { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); + font-size: var(--bui-font-size-3); + font-weight: var(--bui-font-weight-regular); + flex-shrink: 0; +} + +.bui-HeaderToolbarIcon { + width: 16px; + height: 16px; + color: var(--bui-fg-primary); + + & svg { + width: 100%; + height: 100%; + } +} + +.bui-HeaderToolbarControls { + position: absolute; + right: var(--bui-space-5); + top: 50%; + transform: translateY(-50%); + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); +} + +.bui-HeaderBreadcrumbs { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); + font-size: var(--bui-font-size-3); + font-weight: var(--bui-font-weight-regular); +} + +.bui-HeaderBreadcrumb { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); +} + +.bui-HeaderBreadcrumbLink { + color: var(--bui-fg-secondary); + text-decoration: none; + cursor: pointer; + + &[data-active='true'] { + color: var(--bui-fg-primary); + } +} + +.bui-HeaderBreadcrumbSeparator { + width: 16px; + height: 16px; + color: var(--bui-fg-secondary); +} + +.bui-HeaderTabsWrapper { + margin-bottom: var(--bui-space-4); + padding-inline: var(--bui-space-3); + border-bottom: 1px solid var(--bui-border); + background-color: var(--bui-bg-surface-1); +} diff --git a/packages/canon/src/components/Header/Header.tsx b/packages/canon/src/components/Header/Header.tsx new file mode 100644 index 0000000000..e6aae08eeb --- /dev/null +++ b/packages/canon/src/components/Header/Header.tsx @@ -0,0 +1,66 @@ +/* + * 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 { HeaderProps } from './types'; +import { HeaderToolbar } from './HeaderToolbar'; +import { Tabs, TabList, Tab } from '../Tabs'; +import { useStyles } from '../../hooks/useStyles'; +import { type NavigateOptions } from 'react-router-dom'; + +declare module 'react-aria-components' { + interface RouterConfig { + routerOptions: NavigateOptions; + } +} + +/** + * A component that renders a toolbar. + * + * @public + */ +export const Header = (props: HeaderProps) => { + const { tabs, icon, title, menuItems, breadcrumbs, customActions } = props; + + const { classNames } = useStyles('Header'); + + const hasTabs = tabs && tabs.length > 0; + + return ( + <> + + {tabs && ( +
+ + + {tabs?.map(tab => ( + + {tab.label} + + ))} + + +
+ )} + + ); +}; diff --git a/packages/canon/src/components/Header/HeaderToolbar.tsx b/packages/canon/src/components/Header/HeaderToolbar.tsx new file mode 100644 index 0000000000..e03b70055d --- /dev/null +++ b/packages/canon/src/components/Header/HeaderToolbar.tsx @@ -0,0 +1,166 @@ +/* + * 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 { Link } from 'react-aria-components'; +import { useStyles } from '../../hooks/useStyles'; +import { useRef, useState, useEffect } from 'react'; +import { RiArrowRightSLine, RiMore2Line, RiShapesLine } from '@remixicon/react'; +import type { HeaderToolbarProps } from './types'; +import { ButtonIcon } from '../ButtonIcon'; +import { Menu } from '../Menu'; +import { Text } from '../Text'; +import { motion, useScroll, useTransform } from 'motion/react'; + +/** + * A component that renders a toolbar. + * + * @internal + */ +export const HeaderToolbar = (props: HeaderToolbarProps) => { + const { icon, title, menuItems, breadcrumbs, customActions, hasTabs } = props; + const { classNames } = useStyles('Header'); + + const { scrollY } = useScroll(); + const breadcrumbOpacity = useTransform(scrollY, [80, 120], [0, 1]); + + // Refs for collision detection + const toolbarWrapperRef = useRef(null); + const toolbarContentRef = useRef(null); + const toolbarControlsRef = useRef(null); + + // State for breadcrumb visibility + const [showBreadcrumbs, setShowBreadcrumbs] = useState(true); + + // Set up resize observer + useEffect(() => { + const wrapper = toolbarWrapperRef.current; + if (!wrapper) return; + + const resizeObserver = new ResizeObserver(() => { + const wrapper = toolbarWrapperRef.current; + const content = toolbarContentRef.current; + const options = toolbarControlsRef.current; + + if (!wrapper || !content) return; + + // Get dimensions + const wrapperRect = wrapper.getBoundingClientRect(); + const wrapperWidth = wrapperRect.width; + const wrapperPadding = 24; // 12px on each side (var(--bui-space-3)) + const availableWidth = wrapperWidth - wrapperPadding; + + // Calculate required width for content + const contentRect = content.getBoundingClientRect(); + const contentWidth = contentRect?.width || 0; + + // Calculate options width (if exists) + const optionsRect = options?.getBoundingClientRect(); + const optionsWidth = optionsRect?.width || 0; + + // Check if we need to hide breadcrumbs + const shouldShowBreadcrumbs = + contentWidth + optionsWidth + 32 <= availableWidth; + + // Only update state if the value actually changed to prevent flickering + setShowBreadcrumbs(prev => + prev !== shouldShowBreadcrumbs ? shouldShowBreadcrumbs : prev, + ); + }); + + resizeObserver.observe(wrapper); + + return () => { + resizeObserver.disconnect(); + }; + }, []); + + return ( +
+
+
+
+
+ {icon || } +
+ {title || 'Your plugin'} +
+ {breadcrumbs && ( + + + {breadcrumbs.map((breadcrumb, index) => ( +
+ + {breadcrumb.label} + + {index < breadcrumbs.length - 1 && ( + + )} +
+ ))} +
+ )} +
+
+ {customActions} + {menuItems && ( + + ( + } + variant="tertiary" + {...props} + /> + )} + /> + + + + {menuItems.map(option => ( + option.onClick?.()} + > + {option.label} + + ))} + + + + + )} +
+
+
+ ); +}; diff --git a/packages/canon/src/components/IconButton/index.tsx b/packages/canon/src/components/Header/index.tsx similarity index 74% rename from packages/canon/src/components/IconButton/index.tsx rename to packages/canon/src/components/Header/index.tsx index 5f9943557e..c90dbb3abb 100644 --- a/packages/canon/src/components/IconButton/index.tsx +++ b/packages/canon/src/components/Header/index.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -export { IconButton } from './IconButton'; -export type { IconButtonProps } from './types'; -export { iconButtonPropDefs } from './IconButton.props'; -export type { IconButtonOwnProps } from './IconButton.props'; +export { Header } from './Header'; +export type { + HeaderProps, + HeaderTab, + HeaderMenuItem, + HeaderBreadcrumb, +} from './types'; diff --git a/packages/canon/src/components/Header/types.ts b/packages/canon/src/components/Header/types.ts new file mode 100644 index 0000000000..e1156c9a55 --- /dev/null +++ b/packages/canon/src/components/Header/types.ts @@ -0,0 +1,75 @@ +/* + * 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. + */ + +/** + * Props for the main Header component. + * + * @public + */ +export interface HeaderProps { + icon?: React.ReactNode; + title?: string; + breadcrumbs?: HeaderBreadcrumb[]; + customActions?: React.ReactNode; + menuItems?: HeaderMenuItem[]; + tabs?: HeaderTab[]; +} + +/** + * Represents a tab item in the header navigation. + * + * @public + */ +export interface HeaderTab { + id: string; + label: string; + href?: string; +} + +/** + * Represents an option item in the header dropdown menu. + * + * @public + */ +export interface HeaderMenuItem { + label: string; + value: string; + onClick?: () => void; +} + +/** + * Represents a breadcrumb item in the header. + * + * @public + */ +export interface HeaderBreadcrumb { + label: string; + href: string; +} + +/** + * Props for the HeaderToolbar component. + * + * @internal + */ +export interface HeaderToolbarProps { + icon?: HeaderProps['icon']; + title?: HeaderProps['title']; + breadcrumbs?: HeaderProps['breadcrumbs']; + customActions?: HeaderProps['customActions']; + menuItems?: HeaderProps['menuItems']; + hasTabs?: boolean; +} diff --git a/packages/canon/src/components/HeaderPage/HeaderPage.stories.tsx b/packages/canon/src/components/HeaderPage/HeaderPage.stories.tsx new file mode 100644 index 0000000000..6a4e7bef6f --- /dev/null +++ b/packages/canon/src/components/HeaderPage/HeaderPage.stories.tsx @@ -0,0 +1,162 @@ +/* + * 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, StoryFn } from '@storybook/react'; +import { HeaderPage } from './HeaderPage'; +import type { HeaderTab, HeaderMenuItem } from '../Header/types'; +import { MemoryRouter } from 'react-router-dom'; +import { Button } from '../Button'; + +const meta = { + title: 'Components/HeaderPage', + component: HeaderPage, + parameters: { + layout: 'fullscreen', + }, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +const tabs: HeaderTab[] = [ + { + id: 'overview', + label: 'Overview', + }, + { + id: 'checks', + label: 'Checks', + }, + { + id: 'tracks', + label: 'Tracks', + }, + { + id: 'campaigns', + label: 'Campaigns', + }, + { + id: 'integrations', + label: 'Integrations', + }, +]; + +const menuItems: HeaderMenuItem[] = [ + { + label: 'Settings', + value: 'settings', + }, + { + label: 'Invite new members', + value: 'invite-new-members', + }, +]; + +// Extract layout decorator as a reusable constant +const layoutDecorator = [ + (Story: StoryFn) => ( + <> +
+
+ +
+
+ + ), +]; + +export const Default: Story = { + args: { + title: 'Header Page', + }, +}; + +export const WithTabs: Story = { + args: { + ...Default.args, + tabs, + }, +}; + +export const WithMenuItems: Story = { + args: { + ...Default.args, + menuItems, + }, +}; + +export const WithCustomActions: Story = { + render: () => ( + Custom action} + /> + ), +}; + +export const WithEverything: Story = { + render: () => ( + Custom action} + /> + ), +}; + +export const WithLayout: Story = { + args: { + ...WithEverything.args, + }, + decorators: layoutDecorator, +}; diff --git a/packages/canon/src/components/HeaderPage/HeaderPage.styles.css b/packages/canon/src/components/HeaderPage/HeaderPage.styles.css new file mode 100644 index 0000000000..8001ab4f37 --- /dev/null +++ b/packages/canon/src/components/HeaderPage/HeaderPage.styles.css @@ -0,0 +1,41 @@ +/* + * 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. + */ + +.bui-HeaderPage { + display: flex; + flex-direction: column; + gap: var(--bui-space-1); + padding-inline: var(--bui-space-5); + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); +} + +.bui-HeaderPageContent { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.bui-HeaderPageTabsWrapper { + margin-left: -8px; +} + +.bui-HeaderPageControls { + display: flex; + flex-direction: row; + align-items: center; + gap: var(--bui-space-2); +} diff --git a/packages/canon/src/components/HeaderPage/HeaderPage.tsx b/packages/canon/src/components/HeaderPage/HeaderPage.tsx new file mode 100644 index 0000000000..eae6144056 --- /dev/null +++ b/packages/canon/src/components/HeaderPage/HeaderPage.tsx @@ -0,0 +1,85 @@ +/* + * 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 type { HeaderPageProps } from './types'; +import { Heading } from '../Heading'; +import { Menu } from '../Menu'; +import { ButtonIcon } from '../ButtonIcon'; +import { RiMore2Line } from '@remixicon/react'; +import { Tabs, TabList, Tab } from '../Tabs'; +import { useStyles } from '../../hooks/useStyles'; + +/** + * A component that renders a header page. + * + * @public + */ +export const HeaderPage = (props: HeaderPageProps) => { + const { title, menuItems, tabs, customActions } = props; + const { classNames } = useStyles('HeaderPage'); + + return ( +
+
+ {title} +
+ {customActions} + {menuItems && ( + + ( + } + variant="tertiary" + /> + )} + /> + + + + {menuItems.map(menuItem => ( + menuItem.onClick?.()} + > + {menuItem.label} + + ))} + + + + + )} +
+
+ {tabs && ( +
+ + + {tabs.map(tab => ( + + {tab.label} + + ))} + + +
+ )} +
+ ); +}; diff --git a/packages/canon/src/components/HeaderPage/index.tsx b/packages/canon/src/components/HeaderPage/index.tsx new file mode 100644 index 0000000000..49ec9b51bb --- /dev/null +++ b/packages/canon/src/components/HeaderPage/index.tsx @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { HeaderPage } from './HeaderPage'; +export type { HeaderPageProps } from './types'; diff --git a/packages/canon/src/components/HeaderPage/types.ts b/packages/canon/src/components/HeaderPage/types.ts new file mode 100644 index 0000000000..0a55a9456a --- /dev/null +++ b/packages/canon/src/components/HeaderPage/types.ts @@ -0,0 +1,29 @@ +/* + * 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 { HeaderTab, HeaderMenuItem } from '../Header/types'; + +/** + * Props for the main HeaderPage component. + * + * @public + */ +export interface HeaderPageProps { + title?: string; + customActions?: React.ReactNode; + menuItems?: HeaderMenuItem[]; + tabs?: HeaderTab[]; +} diff --git a/packages/canon/src/components/Heading/Heading.stories.tsx b/packages/canon/src/components/Heading/Heading.stories.tsx index 956118ddeb..2e608c09a3 100644 --- a/packages/canon/src/components/Heading/Heading.stories.tsx +++ b/packages/canon/src/components/Heading/Heading.stories.tsx @@ -96,7 +96,7 @@ export const WrappedInLink: Story = { export const CustomRender: Story = { args: { ...Default.args, - render:

, + as: 'h4', }, }; diff --git a/packages/canon/src/components/Heading/Heading.tsx b/packages/canon/src/components/Heading/Heading.tsx index 6ea2fe006b..4ea8bdcfe6 100644 --- a/packages/canon/src/components/Heading/Heading.tsx +++ b/packages/canon/src/components/Heading/Heading.tsx @@ -14,42 +14,51 @@ * limitations under the License. */ -import { forwardRef, useRef } from 'react'; +import { forwardRef } from 'react'; import clsx from 'clsx'; -import { useResponsiveValue } from '../../hooks/useResponsiveValue'; -import { useRender } from '@base-ui-components/react/use-render'; +import type { ElementType } from 'react'; import type { HeadingProps } from './types'; +import { useStyles } from '../../hooks/useStyles'; + +function HeadingComponent( + { + as, + variant = 'title1', + color = 'primary', + truncate, + className, + style, + ...restProps + }: HeadingProps, + ref: React.Ref, +) { + const Component = as || 'h1'; + + const { classNames, dataAttributes } = useStyles('Heading', { + variant, + color, + }); + + return ( + + ); +} + +HeadingComponent.displayName = 'Heading'; /** @public */ -export const Heading = forwardRef( - (props, ref) => { - const { - variant = 'title1', - color = 'primary', - truncate, - className, - render =

, - ...restProps - } = props; - - const responsiveVariant = useResponsiveValue(variant); - const responsiveColor = useResponsiveValue(color); - const internalRef = useRef(null); - - const { renderElement } = useRender({ - render, - props: { - className: clsx('canon-Heading', className), - ['data-variant']: responsiveVariant, - ['data-color']: responsiveColor, - ['data-truncate']: truncate, - ...restProps, - }, - refs: [ref, internalRef], - }); - - return renderElement(); - }, -); +export const Heading = forwardRef(HeadingComponent) as { + ( + props: HeadingProps & { ref?: React.ComponentPropsWithRef['ref'] }, + ): React.ReactElement, T>; + displayName: string; +}; Heading.displayName = 'Heading'; diff --git a/packages/canon/src/components/Heading/index.ts b/packages/canon/src/components/Heading/index.ts index 69b7c3c1a0..ca9845b122 100644 --- a/packages/canon/src/components/Heading/index.ts +++ b/packages/canon/src/components/Heading/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { Heading } from './Heading'; -export type { HeadingProps } from './types'; +export * from './Heading'; +export * from './types'; diff --git a/packages/canon/src/components/Heading/styles.css b/packages/canon/src/components/Heading/styles.css index ce3da94f7f..15d7eea798 100644 --- a/packages/canon/src/components/Heading/styles.css +++ b/packages/canon/src/components/Heading/styles.css @@ -14,53 +14,53 @@ * limitations under the License. */ -.canon-Heading { - font-family: var(--canon-font-regular); - color: var(--canon-fg-primary); +.bui-Heading { + font-family: var(--bui-font-regular); + color: var(--bui-fg-primary); line-height: 100%; padding: 0; margin: 0; } -.canon-Heading[data-variant='display'] { - font-size: var(--canon-font-size-10); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='display'] { + font-size: var(--bui-font-size-10); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-variant='title1'] { - font-size: var(--canon-font-size-9); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='title1'] { + font-size: var(--bui-font-size-9); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-variant='title2'] { - font-size: var(--canon-font-size-8); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='title2'] { + font-size: var(--bui-font-size-8); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-variant='title3'] { - font-size: var(--canon-font-size-7); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='title3'] { + font-size: var(--bui-font-size-7); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-variant='title4'] { - font-size: var(--canon-font-size-6); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='title4'] { + font-size: var(--bui-font-size-6); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-variant='title5'] { - font-size: var(--canon-font-size-5); - font-weight: var(--canon-font-weight-bold); +.bui-Heading[data-variant='title5'] { + font-size: var(--bui-font-size-5); + font-weight: var(--bui-font-weight-bold); } -.canon-Heading[data-color='primary'] { - color: var(--canon-fg-primary); +.bui-Heading[data-color='primary'] { + color: var(--bui-fg-primary); } -.canon-Heading[data-color='secondary'] { - color: var(--canon-fg-secondary); +.bui-Heading[data-color='secondary'] { + color: var(--bui-fg-secondary); } -.canon-Heading[data-truncate] { +.bui-Heading[data-truncate] { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; diff --git a/packages/canon/src/components/Heading/types.ts b/packages/canon/src/components/Heading/types.ts index 7a94bdf2a4..f2317462c2 100644 --- a/packages/canon/src/components/Heading/types.ts +++ b/packages/canon/src/components/Heading/types.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { Breakpoint } from '../../types'; -import type { useRender } from '@base-ui-components/react/use-render'; +import type { ElementType, ComponentPropsWithRef } from 'react'; +import type { Breakpoint } from '../../types'; /** @public */ -export interface HeadingProps - extends Omit, 'color'> { +export type HeadingOwnProps = { + as?: 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; variant?: | 'display' | 'title1' @@ -40,4 +40,8 @@ export interface HeadingProps truncate?: boolean; className?: string; style?: React.CSSProperties; -} +}; + +/** @public */ +export type HeadingProps = HeadingOwnProps & + Omit, keyof HeadingOwnProps>; diff --git a/packages/canon/src/components/Icon/Icon.tsx b/packages/canon/src/components/Icon/Icon.tsx index 8877420a84..6f1e3d9c72 100644 --- a/packages/canon/src/components/Icon/Icon.tsx +++ b/packages/canon/src/components/Icon/Icon.tsx @@ -18,22 +18,25 @@ import { ComponentType } from 'react'; import { useIcons } from './context'; import type { IconProps } from './types'; import clsx from 'clsx'; +import { useStyles } from '../../hooks/useStyles'; /** @public */ export const Icon = (props: IconProps) => { const { name, size, className, style, ...restProps } = props; const { icons } = useIcons(); - const CanonIcon = icons[name] as ComponentType>; + const BckstageIcon = icons[name] as ComponentType>; - if (!CanonIcon) { + if (!BckstageIcon) { console.error(`Icon "${name}" not found or is not a valid component.`); return null; } + const { classNames } = useStyles('Icon'); + return ( - ( + + + + ), + ], } satisfies Meta; export default meta; @@ -33,21 +40,55 @@ type Story = StoryObj; export const Default: Story = { args: { - to: 'https://canon.backstage.io', + href: '/home', children: 'Sign up for Backstage', }, }; +export const ExternalLink: Story = { + args: { + href: 'https://backstage.io', + children: 'Sign up for Backstage', + target: '_blank', + }, +}; + export const AllVariants: Story = { args: { ...Default.args, }, render: args => ( - - - - + + + + + + + + + + ), +}; + +export const AllColors: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + ), }; @@ -58,8 +99,102 @@ export const AllWeights: Story = { }, render: args => ( - - + + + + + + + + + + + + + + + + ), }; @@ -68,43 +203,32 @@ export const Responsive: Story = { args: { ...Default.args, variant: { - xs: 'label', - md: 'body', + xs: 'title-x-small', + md: 'body-x-small', }, }, }; -export const CustomRender: Story = { - render: () => { - return ( - - }>Go to Catalog - - ); - }, - decorators: [ - Story => ( - - - - ), - ], -}; - export const Playground: Story = { args: { ...Default.args, }, render: args => ( - Subtitle - - Body - - Caption - - Label - + Title X Small + + Body X Small + + Body Small + + Body Medium + + Body Large + + Title Small + + Title Medium + ), }; diff --git a/packages/canon/src/components/Link/Link.tsx b/packages/canon/src/components/Link/Link.tsx index 8609517bc0..5fefdccd42 100644 --- a/packages/canon/src/components/Link/Link.tsx +++ b/packages/canon/src/components/Link/Link.tsx @@ -14,39 +14,80 @@ * limitations under the License. */ -import { useRef, forwardRef } from 'react'; -import { useRender } from '@base-ui-components/react/use-render'; -import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import { forwardRef } from 'react'; +import { Link as AriaLink, RouterProvider } from 'react-aria-components'; import clsx from 'clsx'; - +import { useStyles } from '../../hooks/useStyles'; import type { LinkProps } from './types'; +import { useNavigate, useHref } from 'react-router-dom'; + +// Helper function to determine if a link is external +function isExternalLink(href?: string): boolean { + if (!href) return false; + + // Check if it's an absolute URL with protocol + if (href.startsWith('http://') || href.startsWith('https://')) { + return true; + } + + // Check if it's a protocol-relative URL + if (href.startsWith('//')) { + return true; + } + + // Check if it's a mailto: or tel: link + if (href.startsWith('mailto:') || href.startsWith('tel:')) { + return true; + } + + return false; +} /** @public */ -export const Link = forwardRef((props, ref) => { +export const Link = forwardRef((props, ref) => { + const navigate = useNavigate(); const { className, variant = 'body', weight = 'regular', - render = , + color = 'primary', + href, ...restProps } = props; - const responsiveVariant = useResponsiveValue(variant); - const responsiveWeight = useResponsiveValue(weight); - const internalRef = useRef(null); - - const { renderElement } = useRender({ - render, - props: { - className: clsx('canon-Link', className), - ['data-variant']: responsiveVariant, - ['data-weight']: responsiveWeight, - ...restProps, - }, - refs: [ref, internalRef], + const { classNames, dataAttributes } = useStyles('Link', { + variant, + weight, + color, }); - return renderElement(); + const isExternal = isExternalLink(href); + + // If it's an external link, render AriaLink without RouterProvider + if (isExternal) { + return ( + + ); + } + + // For internal links, use RouterProvider + return ( + + + + ); }); Link.displayName = 'Link'; diff --git a/packages/canon/src/components/Link/styles.css b/packages/canon/src/components/Link/styles.css index 8112ae8231..da06003289 100644 --- a/packages/canon/src/components/Link/styles.css +++ b/packages/canon/src/components/Link/styles.css @@ -14,52 +14,78 @@ * limitations under the License. */ -.canon-Link { - font-family: var(--canon-font-regular); - color: var(--canon-fg-link); +.bui-Link { + font-family: var(--bui-font-regular); padding: 0; margin: 0; cursor: pointer; text-decoration-line: none; &:hover { - color: var(--canon-fg-link-hover); text-decoration-line: underline; text-decoration-style: solid; text-decoration-thickness: min(2px, max(1px, 0.05em)); text-underline-offset: calc(0.025em + 2px); text-decoration-color: color-mix( in srgb, - var(--canon-fg-link-hover) 30%, + var(--bui-fg-link-hover) 30%, transparent ); } } -.canon-Link[data-variant='body'] { - font-size: var(--canon-font-size-3); +.bui-Link[data-variant='title-large'] { + font-size: var(--bui-font-size-8); line-height: 140%; } -.canon-Link[data-variant='subtitle'] { - font-size: var(--canon-font-size-4); +.bui-Link[data-variant='title-medium'] { + font-size: var(--bui-font-size-7); line-height: 140%; } -.canon-Link[data-variant='caption'] { - font-size: var(--canon-font-size-2); +.bui-Link[data-variant='title-small'] { + font-size: var(--bui-font-size-6); line-height: 140%; } -.canon-Link[data-variant='label'] { - font-size: var(--canon-font-size-1); +.bui-Link[data-variant='title-x-small'] { + font-size: var(--bui-font-size-5); line-height: 140%; } -.canon-Link[data-weight='regular'] { - font-weight: var(--canon-font-weight-regular); +.bui-Link[data-variant='body-large'] { + font-size: var(--bui-font-size-4); + line-height: 140%; } -.canon-Link[data-weight='bold'] { - font-weight: var(--canon-font-weight-bold); +.bui-Link[data-variant='body-medium'] { + font-size: var(--bui-font-size-3); + line-height: 140%; +} + +.bui-Link[data-variant='body-small'] { + font-size: var(--bui-font-size-2); + line-height: 140%; +} + +.bui-Link[data-variant='body-x-small'] { + font-size: var(--bui-font-size-1); + line-height: 140%; +} + +.bui-Link[data-weight='regular'] { + font-weight: var(--bui-font-weight-regular); +} + +.bui-Link[data-weight='bold'] { + font-weight: var(--bui-font-weight-bold); +} + +.bui-Link[data-color='primary'] { + color: var(--bui-fg-primary); +} + +.bui-Link[data-color='secondary'] { + color: var(--bui-fg-secondary); } diff --git a/packages/canon/src/components/Link/types.ts b/packages/canon/src/components/Link/types.ts index 20fa4a2f31..4f8cc99149 100644 --- a/packages/canon/src/components/Link/types.ts +++ b/packages/canon/src/components/Link/types.ts @@ -14,21 +14,36 @@ * limitations under the License. */ -import type { CSSProperties, ReactNode } from 'react'; import type { Breakpoint } from '../../types'; -import type { useRender } from '@base-ui-components/react/use-render'; +import type { LinkProps as AriaLinkProps } from 'react-aria-components'; /** @public */ -export interface LinkProps extends useRender.ComponentProps<'a'> { - children: ReactNode; - to?: string; +export interface LinkProps extends AriaLinkProps { variant?: - | 'subtitle' - | 'body' - | 'caption' - | 'label' - | Partial>; + | 'title-large' + | 'title-medium' + | 'title-small' + | 'title-x-small' + | 'body-large' + | 'body-medium' + | 'body-small' + | 'body-x-small' + | Partial< + Record< + Breakpoint, + | 'title-large' + | 'title-medium' + | 'title-small' + | 'title-x-small' + | 'body-large' + | 'body-medium' + | 'body-small' + | 'body-x-small' + > + >; weight?: 'regular' | 'bold' | Partial>; - className?: string; - style?: CSSProperties; + color?: + | 'primary' + | 'secondary' + | Partial>; } diff --git a/packages/canon/src/components/Menu/Combobox.tsx b/packages/canon/src/components/Menu/Combobox.tsx index c9593ed2a4..6acaf201db 100644 --- a/packages/canon/src/components/Menu/Combobox.tsx +++ b/packages/canon/src/components/Menu/Combobox.tsx @@ -27,7 +27,7 @@ import { } from 'react'; import clsx from 'clsx'; import { MenuComboboxOption, MenuComboboxProps } from './types'; -import { Icon } from '@backstage/canon'; +import { Icon } from '../../'; const getListboxItemId = (listboxId: string, optionValue: string): string => `${listboxId}-option-${optionValue}`; @@ -66,7 +66,7 @@ function ComboboxItem({ return (
!option.disabled && onItemActive(optionIndex)} onClick={() => !option.disabled && onItemSelect(option.value)} > -
+
{isSelected &&
-
{option.label}
+
{option.label}
); } @@ -194,11 +194,11 @@ export const Combobox = forwardRef(
( id={listboxId} tabIndex={-1} aria-multiselectable={multiselect ? true : undefined} - className="canon-SubmenuComboboxItems" + className="bui-SubmenuComboboxItems" > {filteredOptions.length === 0 ? ( -
- No results found -
+
No results found
) : ( filteredOptions.map((option, index) => ( , React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuTrigger.displayName = MenuPrimitive.Trigger.displayName; const MenuBackdrop = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuBackdrop.displayName = MenuPrimitive.Backdrop.displayName; const MenuPositioner = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuPositioner.displayName = MenuPrimitive.Positioner.displayName; const MenuPopup = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuPopup.displayName = MenuPrimitive.Popup.displayName; const MenuArrow = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuArrow.displayName = MenuPrimitive.Arrow.displayName; const MenuItem = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuItem.displayName = MenuPrimitive.Item.displayName; const MenuGroup = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuGroup.displayName = MenuPrimitive.Group.displayName; const MenuGroupLabel = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuGroupLabel.displayName = MenuPrimitive.GroupLabel.displayName; const MenuRadioGroup = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuRadioGroup.displayName = MenuPrimitive.RadioGroup.displayName; const MenuRadioItem = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuRadioItem.displayName = MenuPrimitive.RadioItem.displayName; const MenuRadioItemIndicator = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuRadioItemIndicator.displayName = MenuPrimitive.RadioItemIndicator.displayName; const MenuCheckboxItem = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuCheckboxItem.displayName = MenuPrimitive.CheckboxItem.displayName; const MenuCheckboxItemIndicator = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuCheckboxItemIndicator.displayName = MenuPrimitive.CheckboxItemIndicator.displayName; const MenuSubmenuTrigger = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - -
{children}
- -
-)); +>(({ className, children, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + +
{children}
+ +
+ ); +}); MenuSubmenuTrigger.displayName = MenuPrimitive.SubmenuTrigger.displayName; const MenuSeparator = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Menu'); + + return ( + + ); +}); MenuSeparator.displayName = MenuPrimitive.Separator.displayName; /** @public */ diff --git a/packages/canon/src/components/Popover/Popover.styles.css b/packages/canon/src/components/Popover/Popover.styles.css new file mode 100644 index 0000000000..23ae5c1019 --- /dev/null +++ b/packages/canon/src/components/Popover/Popover.styles.css @@ -0,0 +1,25 @@ +/* + * 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. + */ + +.bui-Popover { + margin-right: 12px; + overflow: scroll; + background-color: var(--bui-bg-surface-1); + border: 1px solid var(--bui-border); + border-radius: var(--bui-radius-3); + padding-block: var(--bui-space-1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); +} diff --git a/packages/canon/src/components/RadioGroup/RadioGroup.stories.tsx b/packages/canon/src/components/RadioGroup/RadioGroup.stories.tsx new file mode 100644 index 0000000000..4f28e04bfc --- /dev/null +++ b/packages/canon/src/components/RadioGroup/RadioGroup.stories.tsx @@ -0,0 +1,147 @@ +/* + * 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 { RadioGroup, Radio } from './RadioGroup'; + +const meta = { + title: 'Forms/RadioGroup', + component: RadioGroup, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + label: 'What is your favorite pokemon?', + }, + render: args => ( + + Bulbasaur + Charmander + Squirtle + + ), +}; + +export const Horizontal: Story = { + args: { + ...Default.args, + orientation: 'horizontal', + }, + render: args => ( + + Bulbasaur + Charmander + Squirtle + + ), +}; + +export const Disabled: Story = { + args: { + ...Default.args, + isDisabled: true, + }, + render: args => ( + + Bulbasaur + Charmander + Squirtle + + ), +}; + +export const DisabledSingle: Story = { + args: { + ...Default.args, + }, + render: args => ( + + Bulbasaur + + Charmander + + Squirtle + + ), +}; + +export const DisabledAndSelected: Story = { + args: { + ...Default.args, + value: 'charmander', + }, + render: args => ( + + Bulbasaur + + Charmander + + Squirtle + + ), +}; + +export const Invalid: Story = { + args: { + ...Default.args, + name: 'pokemon', + isInvalid: true, + }, + render: args => ( + + Bulbasaur + + Charmander + + Squirtle + + ), +}; + +export const Validation: Story = { + args: { + ...Default.args, + name: 'pokemon', + defaultValue: 'charmander', + validationBehavior: 'aria', + validate: value => (value === 'charmander' ? 'Nice try!' : null), + }, + render: args => ( + + Bulbasaur + Charmander + Squirtle + + ), +}; + +export const ReadOnly: Story = { + args: { + ...Default.args, + isReadOnly: true, + defaultValue: 'charmander', + }, + render: args => ( + + Bulbasaur + Charmander + Squirtle + + ), +}; diff --git a/packages/canon/src/components/RadioGroup/RadioGroup.styles.css b/packages/canon/src/components/RadioGroup/RadioGroup.styles.css new file mode 100644 index 0000000000..f12d5571e0 --- /dev/null +++ b/packages/canon/src/components/RadioGroup/RadioGroup.styles.css @@ -0,0 +1,95 @@ +.bui-RadioGroup { + display: flex; + flex-direction: column; + color: var(--bui-fg-primary); +} + +.bui-RadioGroup[data-orientation='horizontal'] .bui-RadioGroupContent { + flex-direction: row; + gap: var(--bui-space-4); +} + +.bui-RadioGroupContent { + display: flex; + flex-direction: column; + gap: var(--bui-space-2); +} + +.bui-Radio { + display: flex; + /* This is needed so the HiddenInput is positioned correctly */ + position: relative; + align-items: center; + gap: var(--bui-space-2); + font-size: var(--bui-font-size-2); + color: var(--bui-fg-primary); + forced-color-adjust: none; + + &:before { + content: ''; + display: block; + width: 1rem; + height: 1rem; + box-sizing: border-box; + border: 0.125rem solid var(--bui-border); + background: var(--bui-gray-1); + border-radius: var(--bui-radius-full); + transition: all 200ms; + } + + &[data-pressed]:before { + border-color: var(--bui-border); + } + + &[data-selected] { + &:before { + border-color: var(--bui-bg-solid); + border-width: 0.25rem; + } + + &[data-pressed]:before { + border-color: var(--bui-bg-solid); + } + } + + &[data-focus-visible]:before { + outline: 2px solid var(--bui-ring); + outline-offset: 2px; + } + + &[data-disabled] { + cursor: not-allowed; + color: var(--bui-fg-disabled); + + &:before { + border-color: var(--bui-border-disabled); + background: var(--bui-bg-disabled); + } + + &[data-selected]:before { + border-color: var(--bui-border-disabled); + } + } + + &[data-invalid]:before { + border-color: var(--bui-border-danger); + } + + &[data-invalid][data-selected]:before { + border-color: var(--bui-border-danger); + } + + /* Ensure disabled state prevails over invalid state */ + &[data-disabled][data-invalid] { + color: var(--bui-fg-disabled); + + &:before { + border-color: var(--bui-border-disabled); + background: var(--bui-bg-disabled); + } + + &[data-selected]:before { + border-color: var(--bui-border-disabled); + } + } +} diff --git a/packages/canon/src/components/RadioGroup/RadioGroup.tsx b/packages/canon/src/components/RadioGroup/RadioGroup.tsx new file mode 100644 index 0000000000..4363477c1a --- /dev/null +++ b/packages/canon/src/components/RadioGroup/RadioGroup.tsx @@ -0,0 +1,95 @@ +/* + * 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, useEffect } from 'react'; +import { + RadioGroup as AriaRadioGroup, + Radio as AriaRadio, +} from 'react-aria-components'; +import clsx from 'clsx'; +import { FieldLabel } from '../FieldLabel'; +import { FieldError } from '../FieldError'; +import { useStyles } from '../../hooks/useStyles'; + +import type { RadioGroupProps, RadioProps } from './types'; + +/** @public */ +export const RadioGroup = forwardRef( + (props, ref) => { + const { + className, + label, + secondaryLabel, + description, + isRequired, + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, + children, + ...rest + } = props; + + const { classNames } = useStyles('RadioGroup'); + + useEffect(() => { + if (!label && !ariaLabel && !ariaLabelledBy) { + console.warn( + 'RadioGroup requires either a visible label, aria-label, or aria-labelledby for accessibility', + ); + } + }, [label, ariaLabel, ariaLabelledBy]); + + // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. + const secondaryLabelText = + secondaryLabel || (isRequired ? 'Required' : null); + + return ( + + +
{children}
+ +
+ ); + }, +); + +RadioGroup.displayName = 'RadioGroup'; + +/** @public */ +export const Radio = forwardRef((props, ref) => { + const { className, ...rest } = props; + + const { classNames } = useStyles('RadioGroup'); + + return ( + + ); +}); + +RadioGroup.displayName = 'RadioGroup'; diff --git a/packages/canon/src/components/RadioGroup/index.ts b/packages/canon/src/components/RadioGroup/index.ts new file mode 100644 index 0000000000..fd9572f667 --- /dev/null +++ b/packages/canon/src/components/RadioGroup/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export * from './RadioGroup'; +export * from './types'; diff --git a/packages/canon/src/components/RadioGroup/types.ts b/packages/canon/src/components/RadioGroup/types.ts new file mode 100644 index 0000000000..029ef4baf9 --- /dev/null +++ b/packages/canon/src/components/RadioGroup/types.ts @@ -0,0 +1,32 @@ +/* + * 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 type { + RadioGroupProps as AriaRadioGroupProps, + RadioProps as AriaRadioProps, +} from 'react-aria-components'; +import type { FieldLabelProps } from '../FieldLabel/types'; +import { ReactNode } from 'react'; + +/** @public */ +export interface RadioGroupProps + extends Omit, + Omit { + children?: ReactNode; +} + +/** @public */ +export interface RadioProps extends AriaRadioProps {} diff --git a/packages/canon/src/components/ScrollArea/ScrollArea.stories.tsx b/packages/canon/src/components/ScrollArea/ScrollArea.stories.tsx index 25a3d5f3d8..867ce29b07 100644 --- a/packages/canon/src/components/ScrollArea/ScrollArea.stories.tsx +++ b/packages/canon/src/components/ScrollArea/ScrollArea.stories.tsx @@ -30,7 +30,7 @@ export const Default: Story = { render: () => (
diff --git a/packages/canon/src/components/ScrollArea/ScrollArea.styles.css b/packages/canon/src/components/ScrollArea/ScrollArea.styles.css index 1d1c25e9d0..209168c329 100644 --- a/packages/canon/src/components/ScrollArea/ScrollArea.styles.css +++ b/packages/canon/src/components/ScrollArea/ScrollArea.styles.css @@ -14,19 +14,17 @@ * limitations under the License. */ -.canon-ScrollAreaRoot { +.bui-ScrollAreaRoot { box-sizing: border-box; - width: 24rem; - height: 8.5rem; - max-width: calc(100vw - 8rem); + width: 100%; } -.canon-ScrollAreaViewport { +.bui-ScrollAreaViewport { height: 100%; overscroll-behavior: contain; } -.canon-ScrollAreaContent { +.bui-ScrollAreaContent { display: flex; flex-direction: column; gap: 1rem; @@ -35,10 +33,10 @@ padding-right: 1.5rem; } -.canon-ScrollAreaScrollbar { +.bui-ScrollAreaScrollbar { display: flex; justify-content: center; - background-color: var(--canon-scrollbar); + background-color: var(--bui-scrollbar); width: 0.25rem; border-radius: 0.375rem; margin: 0.5rem; @@ -60,8 +58,8 @@ } } -.canon-ScrollAreaThumb { +.bui-ScrollAreaThumb { width: 100%; border-radius: inherit; - background-color: var(--canon-scrollbar-thumb); + background-color: var(--bui-scrollbar-thumb); } diff --git a/packages/canon/src/components/ScrollArea/ScrollArea.tsx b/packages/canon/src/components/ScrollArea/ScrollArea.tsx index e96e76c29b..44722fbd12 100644 --- a/packages/canon/src/components/ScrollArea/ScrollArea.tsx +++ b/packages/canon/src/components/ScrollArea/ScrollArea.tsx @@ -17,53 +17,70 @@ import { forwardRef } from 'react'; import { ScrollArea as ScrollAreaPrimitive } from '@base-ui-components/react/scroll-area'; import clsx from 'clsx'; +import { useStyles } from '../../hooks/useStyles'; const ScrollAreaRoot = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('ScrollArea'); + + return ( + + ); +}); ScrollAreaRoot.displayName = ScrollAreaPrimitive.Root.displayName; const ScrollAreaViewport = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('ScrollArea'); + + return ( + + ); +}); ScrollAreaViewport.displayName = ScrollAreaPrimitive.Viewport.displayName; const ScrollAreaScrollbar = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('ScrollArea'); + + return ( + + ); +}); ScrollAreaScrollbar.displayName = ScrollAreaPrimitive.Scrollbar.displayName; const ScrollAreaThumb = forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('ScrollArea'); + + return ( + + ); +}); ScrollAreaThumb.displayName = ScrollAreaPrimitive.Thumb.displayName; /** @public */ diff --git a/packages/canon/src/components/SearchField/SearchField.stories.tsx b/packages/canon/src/components/SearchField/SearchField.stories.tsx new file mode 100644 index 0000000000..5b267f585d --- /dev/null +++ b/packages/canon/src/components/SearchField/SearchField.stories.tsx @@ -0,0 +1,154 @@ +/* + * 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 { SearchField } from './SearchField'; +import { Form } from 'react-aria-components'; +import { Icon } from '../Icon'; +import { Flex } from '../Flex'; +import { FieldLabel } from '../FieldLabel'; + +const meta = { + title: 'Forms/SearchField', + component: SearchField, + argTypes: { + isRequired: { + control: 'boolean', + }, + icon: { + control: 'object', + }, + placeholder: { + control: 'text', + }, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + name: 'url', + style: { + maxWidth: '300px', + }, + }, +}; + +export const Sizes: Story = { + args: { + ...Default.args, + }, + render: args => ( + + + + + ), +}; + +export const DefaultValue: Story = { + args: { + ...Default.args, + defaultValue: 'https://example.com', + }, +}; + +export const WithLabel: Story = { + args: { + ...Default.args, + label: 'Label', + }, +}; + +export const WithDescription: Story = { + args: { + ...WithLabel.args, + description: 'Description', + }, +}; + +export const Required: Story = { + args: { + ...WithLabel.args, + isRequired: true, + }, +}; + +export const Disabled: Story = { + args: { + ...Default.args, + isDisabled: true, + }, +}; + +export const WithIcon: Story = { + args: { + ...Default.args, + }, + render: args => ( + } + /> + ), +}; + +export const DisabledWithIcon: Story = { + args: { + ...WithIcon.args, + isDisabled: true, + }, +}; + +export const ShowError: Story = { + args: { + ...WithLabel.args, + }, + render: args => ( +
+ + + ), +}; + +export const Validation: Story = { + args: { + ...WithLabel.args, + validate: value => (value === 'admin' ? 'Nice try!' : null), + }, +}; + +export const CustomField: Story = { + render: () => ( + <> + + + + ), +}; diff --git a/packages/canon/src/components/SearchField/SearchField.styles.css b/packages/canon/src/components/SearchField/SearchField.styles.css new file mode 100644 index 0000000000..f2f808c5e0 --- /dev/null +++ b/packages/canon/src/components/SearchField/SearchField.styles.css @@ -0,0 +1,55 @@ +/* + * 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. + */ + +.bui-SearchField[data-empty] .bui-InputClear { + display: none; +} + +.bui-InputClear { + position: absolute; + right: 0; + top: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: transparent; + border: none; + padding: 0; + margin: 0; + cursor: pointer; + color: var(--bui-fg-secondary); + transition: color 0.2s ease-in-out; +} + +.bui-InputClear:hover { + color: var(--bui-fg-primary); +} + +.bui-InputClear[data-size='small'] { + width: 2rem; + height: 2rem; +} + +.bui-InputClear[data-size='medium'] { + width: 2.5rem; + height: 2.5rem; +} + +.bui-InputClear svg { + width: 1rem; + height: 1rem; +} diff --git a/packages/canon/src/components/SearchField/SearchField.tsx b/packages/canon/src/components/SearchField/SearchField.tsx new file mode 100644 index 0000000000..ff54439309 --- /dev/null +++ b/packages/canon/src/components/SearchField/SearchField.tsx @@ -0,0 +1,120 @@ +/* + * 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, useEffect } from 'react'; +import { + Input, + SearchField as AriaSearchField, + Button, +} from 'react-aria-components'; +import clsx from 'clsx'; +import { FieldLabel } from '../FieldLabel'; +import { FieldError } from '../FieldError'; +import { RiSearch2Line, RiCloseCircleLine } from '@remixicon/react'; +import { useStyles } from '../../hooks/useStyles'; + +import type { SearchFieldProps } from './types'; + +/** @public */ +export const SearchField = forwardRef( + (props, ref) => { + const { + className, + icon, + size = 'small', + label, + secondaryLabel, + description, + isRequired, + placeholder = 'Search', + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, + ...rest + } = props; + + useEffect(() => { + if (!label && !ariaLabel && !ariaLabelledBy) { + console.warn( + 'SearchField requires either a visible label, aria-label, or aria-labelledby for accessibility', + ); + } + }, [label, ariaLabel, ariaLabelledBy]); + + const { classNames: textFieldClassNames, dataAttributes } = useStyles( + 'TextField', + { + size, + }, + ); + + const { classNames: searchFieldClassNames } = useStyles('SearchField', { + size, + }); + + // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. + const secondaryLabelText = + secondaryLabel || (isRequired ? 'Required' : null); + + return ( + + +
+ {icon !== false && ( + + )} + + +
+ +
+ ); + }, +); + +SearchField.displayName = 'searchField'; diff --git a/packages/canon/src/components/SearchField/index.ts b/packages/canon/src/components/SearchField/index.ts new file mode 100644 index 0000000000..288c3fe44a --- /dev/null +++ b/packages/canon/src/components/SearchField/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export * from './SearchField'; +export * from './types'; diff --git a/packages/canon/src/components/SearchField/types.ts b/packages/canon/src/components/SearchField/types.ts new file mode 100644 index 0000000000..b561292684 --- /dev/null +++ b/packages/canon/src/components/SearchField/types.ts @@ -0,0 +1,41 @@ +/* + * 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 type { SearchFieldProps as AriaSearchFieldProps } from 'react-aria-components'; +import { ReactNode } from 'react'; +import type { Breakpoint } from '../../types'; +import type { FieldLabelProps } from '../FieldLabel/types'; + +/** @public */ +export interface SearchFieldProps + extends AriaSearchFieldProps, + Omit { + /** + * An icon to render before the input + */ + icon?: ReactNode | false; + + /** + * The size of the text field + * @defaultValue 'medium' + */ + size?: 'small' | 'medium' | Partial>; + + /** + * The placeholder text for the input + */ + placeholder?: string; +} diff --git a/packages/canon/src/components/Select/Select.stories.tsx b/packages/canon/src/components/Select/Select.stories.tsx index 000ba180dc..ecefc6ec09 100644 --- a/packages/canon/src/components/Select/Select.stories.tsx +++ b/packages/canon/src/components/Select/Select.stories.tsx @@ -16,9 +16,11 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Select } from './Select'; import { Flex } from '../Flex'; +import { Form } from 'react-aria-components'; +import { RiCloudLine } from '@remixicon/react'; const meta = { - title: 'Components/Select', + title: 'Forms/Select', component: Select, } satisfies Meta; @@ -49,21 +51,35 @@ export const Preview: Story = { }, }; -export const WithDescription: Story = { +export const WithLabel: Story = { args: { - ...Preview.args, + ...Default.args, + label: 'Font Family', + }, +}; + +export const WithLabelAndDescription: Story = { + args: { + ...WithLabel.args, description: 'Choose a font family for your document', }, }; +export const WithIcon: Story = { + args: { + ...WithLabel.args, + }, + render: args => - } /> + + + ), }; -export const WithLongOptionNames: Story = { +export const WithLongNames: Story = { args: { label: 'Document Template', options: [ @@ -286,6 +304,19 @@ export const WithLongOptionNames: Story = { placeholder: 'Select a document template', name: 'template', style: { maxWidth: 400 }, - value: 'annual-report-2024', + defaultSelectedKey: 'annual-report-2024', }, }; + +export const WithLongNamesAndPadding: Story = { + args: { + ...WithLongNames.args, + }, + decorators: [ + (Story, { args }) => ( +
+ +
+ ), + ], +}; diff --git a/packages/canon/src/components/Select/Select.styles.css b/packages/canon/src/components/Select/Select.styles.css index 34cb242c7b..da700d67d1 100644 --- a/packages/canon/src/components/Select/Select.styles.css +++ b/packages/canon/src/components/Select/Select.styles.css @@ -14,194 +14,160 @@ * limitations under the License. */ -.canon-Select { - display: flex; - flex-direction: column; - font-family: var(--canon-font-regular); - width: 100%; +.bui-Select[data-invalid] { + & .bui-SelectTrigger { + border-color: var(--bui-fg-danger); + } } -.canon-SelectLabel { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-primary); - margin-bottom: var(--canon-space-1_5); - cursor: pointer; -} -.canon-SelectLabel[data-disabled] { - cursor: default; -} - -.canon-SelectDescription { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-secondary); - margin: 0; - padding-top: var(--canon-space-1_5); -} - -.canon-SelectError { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-danger); - margin: 0; - padding-top: var(--canon-space-1_5); -} - -.canon-SelectTrigger { +.bui-SelectTrigger { box-sizing: border-box; - border-radius: var(--canon-radius-3); - border: 1px solid var(--canon-border); - padding: 0 var(--canon-space-4); - background-color: var(--canon-bg-surface-1); - font-size: var(--canon-font-size-3); - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-primary); - width: 100%; + border-radius: var(--bui-radius-3); + border: 1px solid var(--bui-border); + background-color: var(--bui-bg-surface-1); display: flex; justify-content: space-between; align-items: center; transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out; cursor: pointer; - gap: var(--canon-space-2); + gap: var(--bui-space-2); + max-width: 100%; + + & svg { + flex-shrink: 0; + } + + &[data-size='small'] { + height: 2rem; + padding-inline: var(--bui-space-3); + } + + &[data-size='medium'] { + height: 3rem; + padding-inline: var(--bui-space-4); + } + + &[data-size='small'] svg { + width: 1rem; + height: 1rem; + } + + &[data-size='medium'] svg { + width: 1.25rem; + height: 1.25rem; + } + + &::placeholder { + color: var(--bui-fg-secondary); + } + + &:hover { + border-color: var(--bui-border-hover); + } + + &:focus-visible { + border-color: var(--bui-border-pressed); + outline: 0; + } + + &[data-invalid] { + border-color: var(--bui-fg-danger); + } + &[data-invalid]:hover { + border-width: 2px; + } + + &[data-invalid]:focus-visible { + border-width: 2px; + } + + &[disabled] { + cursor: not-allowed; + border-color: var(--bui-border-disabled); + color: var(--bui-fg-disabled); + } + + &[disabled] .bui-SelectValue { + color: var(--bui-fg-disabled); + } + + &[data-popup-open] .bui-SelectIcon { + transform: rotate(180deg); + } } -.canon-SelectTrigger::placeholder { - color: var(--canon-fg-secondary); +.bui-SelectValue { + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; + width: 100%; + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-primary); + + & .bui-SelectItemIndicator { + display: none; + } + + &[disabled] { + color: var(--bui-fg-disabled); + } } -.canon-SelectTrigger:hover { - border-color: var(--canon-border-hover); -} - -.canon-SelectTrigger:focus-visible { - border-color: var(--canon-border-pressed); - outline: 0; -} - -.canon-SelectTrigger[data-invalid] { - border-color: var(--canon-fg-danger); -} -.canon-SelectTrigger[data-invalid]:hover { - border-width: 2px; -} - -.canon-SelectTrigger[data-invalid]:focus-visible { - border-width: 2px; -} - -.canon-SelectTrigger[data-disabled] { - cursor: not-allowed; - border-color: var(--canon-border-disabled); - color: var(--canon-fg-disabled); -} - -.canon-SelectTrigger[data-size='small'] { - height: 2rem; -} - -.canon-SelectTrigger[data-size='medium'] { - height: 3rem; -} - -.canon-SelectIcon { - margin-left: var(--canon-space-5); - transition: transform 0.2s ease; -} - -.canon-SelectTrigger[data-popup-open] .canon-SelectIcon { - transform: rotate(180deg); -} - -.canon-SelectPopup { - box-sizing: border-box; - max-height: var(--available-height); - overflow-y: auto; - background-color: var(--canon-bg-surface-1); - border: 1px solid var(--canon-border); - border-radius: var(--canon-radius-3); - padding-block: var(--canon-space-1); - z-index: 1; - outline: 0; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); - transform-origin: var(--transform-origin); - transition: transform 150ms, opacity 150ms; -} - -.canon-SelectPopup[data-starting-style], -.canon-SelectPopup[data-ending-style] { - opacity: 0; - transform: scale(0.9); -} - -.canon-SelectItem { +.bui-SelectItem { position: relative; width: var(--anchor-width); display: grid; grid-template-areas: 'icon text'; grid-template-columns: 1rem 1fr; align-items: center; - padding-block: var(--canon-space-2); - padding-inline: var(--canon-space-4); - color: var(--canon-fg-primary); - border-radius: var(--canon-radius-3); + padding-block: var(--bui-space-2); + padding-inline: var(--bui-space-4); + color: var(--bui-fg-primary); + border-radius: var(--bui-radius-3); cursor: pointer; user-select: none; - font-size: var(--canon-font-size-3); - gap: var(--canon-space-2); + font-size: var(--bui-font-size-3); + gap: var(--bui-space-2); outline: none; + + &[data-focused] { + z-index: 0; + position: relative; + color: var(--bui-fg-primary); + } + + &[data-focused]::before { + content: ''; + z-index: -1; + position: absolute; + inset-block: 0; + inset-inline: 0.25rem; + border-radius: 0.25rem; + background-color: var(--bui-bg-tint-hover); + } + + &[data-disabled] { + cursor: not-allowed; + color: var(--bui-fg-disabled); + } + + &[data-selected] .bui-SelectItemIndicator { + opacity: 1; + } } -.canon-SelectItem[data-highlighted] { - z-index: 0; - position: relative; - color: var(--canon-fg-primary); -} - -.canon-SelectItem[data-highlighted]::before { - content: ''; - z-index: -1; - position: absolute; - inset-block: 0; - inset-inline: 0.25rem; - border-radius: 0.25rem; - background-color: var(--canon-bg-tint-hover); -} - -.canon-SelectItem[data-disabled] { - cursor: not-allowed; - color: var(--canon-fg-disabled); -} - -.canon-SelectItemIndicator { +.bui-SelectItemIndicator { grid-area: icon; display: flex; align-items: center; justify-content: center; + opacity: 0; + transition: opacity 0.2s ease-in-out; } -.canon-SelectItemText { +.bui-SelectItemLabel { flex: 1; grid-area: text; } - -.canon-SelectRequired { - color: var(--canon-fg-secondary); - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - margin-left: var(--canon-space-1); -} - -.canon-SelectIcon { - display: flex; - align-items: center; - justify-content: center; -} - -.canon-SelectValue { - text-overflow: ellipsis; - overflow: hidden; - white-space: nowrap; - width: 100%; -} diff --git a/packages/canon/src/components/Select/Select.tsx b/packages/canon/src/components/Select/Select.tsx index 3f25ac26de..ce3e094b9d 100644 --- a/packages/canon/src/components/Select/Select.tsx +++ b/packages/canon/src/components/Select/Select.tsx @@ -14,13 +14,23 @@ * limitations under the License. */ -import { forwardRef, useCallback, useId, useRef, MouseEvent } from 'react'; -import { Select as SelectPrimitive } from '@base-ui-components/react/select'; -import { Icon } from '../Icon'; +import { forwardRef, useEffect } from 'react'; +import { + Select as AriaSelect, + SelectValue, + Button, + Popover, + ListBox, + ListBoxItem, + Text, +} from 'react-aria-components'; import clsx from 'clsx'; import './Select.styles.css'; import { SelectProps } from './types'; -import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import { useStyles } from '../../hooks/useStyles'; +import { FieldLabel } from '../FieldLabel'; +import { Icon } from '../Icon'; +import { FieldError } from '../FieldError'; /** @public */ export const Select = forwardRef((props, ref) => { @@ -30,100 +40,72 @@ export const Select = forwardRef((props, ref) => { description, options, placeholder = 'Select an option', - size = 'medium', - required, - error, + size = 'small', + icon, + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, + isRequired, + secondaryLabel, style, ...rest } = props; - // Get the responsive value for the variant - const responsiveSize = useResponsiveValue(size); + const { classNames: popoverClassNames } = useStyles('Popover'); + const { classNames, dataAttributes } = useStyles('Select', { + size, + }); - // Generate unique IDs for accessibility - const selectId = useId(); - const descriptionId = useId(); - const errorId = useId(); + useEffect(() => { + if (!label && !ariaLabel && !ariaLabelledBy) { + console.warn( + 'TextField requires either a visible label, aria-label, or aria-labelledby for accessibility', + ); + } + }, [label, ariaLabel, ariaLabelledBy]); - const triggerRef = useRef(null); - - const handleLabelClick = useCallback( - (e: MouseEvent) => { - if (!props.disabled && triggerRef.current) { - e.preventDefault(); - triggerRef.current.focus(); - } - }, - [props.disabled], - ); + // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. + const secondaryLabelText = secondaryLabel || (isRequired ? 'Required' : null); return ( -
- {label && ( - - )} - - - - - - - - - - - - {options?.map(option => ( - - - - - - {option.label} - - - ))} - - - - - {description && ( -

- {description} -

- )} - {error && ( - - )} -
+ + + + + + + {options?.map(option => ( + +
+ +
+ + {option.label} + +
+ ))} +
+
+
); }); diff --git a/packages/canon/src/components/Select/types.ts b/packages/canon/src/components/Select/types.ts index 955eb14c5e..991344aa5b 100644 --- a/packages/canon/src/components/Select/types.ts +++ b/packages/canon/src/components/Select/types.ts @@ -14,15 +14,22 @@ * limitations under the License. */ -import { Breakpoint } from '@backstage/canon'; -import { ChangeEvent, FocusEvent } from 'react'; +import { Breakpoint } from '../../'; +import { ReactNode } from 'react'; +import type { SelectProps as AriaSelectProps } from 'react-aria-components'; +import type { FieldLabelProps } from '../FieldLabel/types'; /** @public */ -export interface SelectProps { +export interface SelectProps + extends AriaSelectProps<{ + name: string; + value: string; + }>, + Omit { /** - * The class name of the select field + * An icon to render before the input */ - className?: string; + icon?: ReactNode; /** * The size of the select field @@ -30,81 +37,8 @@ export interface SelectProps { */ size?: 'small' | 'medium' | Partial>; - /** - * The label of the select field - */ - label?: string; - - /** - * The description of the select field - */ - description?: string; - - /** - * The name of the select field - */ - name: string; - - /** - * Whether the select field should ignore user input - * @defaultValue false - */ - disabled?: boolean; - - /** - * Whether the select field is required - * @defaultValue false - */ - required?: boolean; - /** * The options of the select field */ options?: Array<{ value: string; label: string; disabled?: boolean }>; - - /** - * The current value of the select field - */ - value?: string; - - /** - * The default value of the select field, if nothing has been selected yet - */ - defaultValue?: string; - - /** - * A placeholder text to show if nothing has been selected and there's no default value - * @defaultValue 'Select an option' - */ - placeholder?: string; - - /** - * Callback that is called when the value of the select field changes - */ - onValueChange?: (value: string) => void; - - /** - * Callback that is called when the select field is opened or closed - */ - onOpenChange?: (open: boolean) => void; - - /** - * The style of the select field - */ - style?: React.CSSProperties; - - /** - * The error message of the select field - */ - error?: string; - - /** - * onChange handler for form integration - */ - onChange?: (event: ChangeEvent) => void; - - /** - * onBlur handler for form integration - */ - onBlur?: (event: FocusEvent) => void; } diff --git a/packages/canon/src/components/Skeleton/Skeleton.stories.tsx b/packages/canon/src/components/Skeleton/Skeleton.stories.tsx new file mode 100644 index 0000000000..3fe95c53bc --- /dev/null +++ b/packages/canon/src/components/Skeleton/Skeleton.stories.tsx @@ -0,0 +1,82 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { Skeleton } from './Skeleton'; +import { Flex } from '../Flex'; + +const meta = { + title: 'Components/Skeleton', + component: Skeleton, + argTypes: { + rounded: { + control: 'boolean', + }, + width: { + control: 'number', + }, + height: { + control: 'number', + }, + }, + args: { + width: 80, + height: 24, + rounded: false, + }, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: {}, +}; + +export const Rounded: Story = { + args: { + rounded: true, + width: 48, + height: 48, + }, +}; + +export const Demo1: Story = { + render: () => ( + + + + + + + + + + + + + ), +}; + +export const Demo2: Story = { + render: () => ( + + + + + + ), +}; diff --git a/packages/canon/src/components/Skeleton/Skeleton.styles.css b/packages/canon/src/components/Skeleton/Skeleton.styles.css new file mode 100644 index 0000000000..bbbb6deaeb --- /dev/null +++ b/packages/canon/src/components/Skeleton/Skeleton.styles.css @@ -0,0 +1,25 @@ +/* + * 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. + */ + +.bui-Skeleton { + animation: var(--bui-animate-pulse); + background-color: var(--bui-bg-surface-2); + border-radius: var(--bui-radius-2); +} + +.bui-Skeleton[data-rounded='true'] { + border-radius: var(--bui-radius-full); +} diff --git a/packages/canon/src/components/Skeleton/Skeleton.tsx b/packages/canon/src/components/Skeleton/Skeleton.tsx new file mode 100644 index 0000000000..772a69bc28 --- /dev/null +++ b/packages/canon/src/components/Skeleton/Skeleton.tsx @@ -0,0 +1,36 @@ +/* + * 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 { useStyles } from '../../hooks/useStyles'; +import { SkeletonProps } from './types'; + +/** @public */ +export const Skeleton = (props: SkeletonProps) => { + const { width = 80, height = 24, rounded = false, ...rest } = props; + const { classNames } = useStyles('Skeleton'); + + return ( +
+ ); +}; diff --git a/packages/canon/src/components/Skeleton/index.tsx b/packages/canon/src/components/Skeleton/index.tsx new file mode 100644 index 0000000000..f1e76b1851 --- /dev/null +++ b/packages/canon/src/components/Skeleton/index.tsx @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { Skeleton } from './Skeleton'; +export type { SkeletonProps } from './types'; diff --git a/packages/canon/src/components/Skeleton/types.ts b/packages/canon/src/components/Skeleton/types.ts new file mode 100644 index 0000000000..136873ed27 --- /dev/null +++ b/packages/canon/src/components/Skeleton/types.ts @@ -0,0 +1,24 @@ +/* + * 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 { ComponentProps } from 'react'; + +/** @public */ +export interface SkeletonProps extends ComponentProps<'div'> { + width?: number | string; + height?: number | string; + rounded?: boolean; +} diff --git a/packages/canon/src/components/Switch/Switch.stories.tsx b/packages/canon/src/components/Switch/Switch.stories.tsx new file mode 100644 index 0000000000..165fefc97b --- /dev/null +++ b/packages/canon/src/components/Switch/Switch.stories.tsx @@ -0,0 +1,39 @@ +/* + * 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 type { Meta, StoryObj } from '@storybook/react'; +import { Switch } from './Switch'; + +const meta = { + title: 'Components/Switch', + component: Switch, +} satisfies Meta; + +export default meta; +type Story = StoryObj; + +export const Default: Story = { + args: { + label: 'Switch', + }, +}; + +export const Disabled: Story = { + args: { + ...Default.args, + isDisabled: true, + }, +}; diff --git a/packages/canon/src/components/Switch/Switch.styles.css b/packages/canon/src/components/Switch/Switch.styles.css new file mode 100644 index 0000000000..ee2a24d013 --- /dev/null +++ b/packages/canon/src/components/Switch/Switch.styles.css @@ -0,0 +1,74 @@ +/* + * 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. + */ + +.bui-Switch { + display: flex; + /* This is needed so the HiddenInput is positioned correctly */ + position: relative; + align-items: center; + gap: var(--bui-space-3); + font-size: var(--bui-font-size-3); + color: var(--bui-fg-primary); + cursor: pointer; + + &[data-pressed] .bui-SwitchIndicator { + &:before { + background: var(--bui-fg-solid); + } + } + + &[data-selected] { + .bui-SwitchIndicator { + background: var(--bui-bg-solid); + + &:before { + background: var(--bui-fg-solid); + transform: translateX(100%); + } + } + + &[data-pressed] { + .indicator { + background: var(--bui-gray-3); + } + } + } + + &[data-focus-visible] .bui-SwitchIndicator { + outline: 2px solid; + outline-offset: 2px; + } +} + +.bui-SwitchIndicator { + width: 2rem; + height: 1.143rem; + border: 2px; + background: var(--bui-gray-3); + border-radius: 1.143rem; + transition: all 200ms; + + &:before { + content: ''; + display: block; + margin: 0.143rem; + width: 0.857rem; + height: 0.857rem; + background: var(--bui-fg-solid); + border-radius: 16px; + transition: all 200ms; + } +} diff --git a/packages/canon/src/components/IconButton/IconButton.props.ts b/packages/canon/src/components/Switch/Switch.tsx similarity index 51% rename from packages/canon/src/components/IconButton/IconButton.props.ts rename to packages/canon/src/components/Switch/Switch.tsx index e584997ef5..70890ca3a0 100644 --- a/packages/canon/src/components/IconButton/IconButton.props.ts +++ b/packages/canon/src/components/Switch/Switch.tsx @@ -14,28 +14,23 @@ * limitations under the License. */ -import type { PropDef, GetPropDefTypes } from '../../props/prop-def'; +import { forwardRef } from 'react'; +import { Switch as AriaSwitch } from 'react-aria-components'; +import type { SwitchProps } from './types'; +import { useStyles } from '../../hooks/useStyles'; /** @public */ -export const iconButtonPropDefs = { - variant: { - type: 'enum', - values: ['primary', 'secondary'], - className: 'canon-Button--variant', - default: 'primary', - responsive: true, - }, - size: { - type: 'enum', - values: ['small', 'medium'], - className: 'canon-Button--size', - default: 'medium', - responsive: true, - }, -} satisfies { - variant: PropDef<'primary' | 'secondary'>; - size: PropDef<'small' | 'medium'>; -}; +export const Switch = forwardRef( + ({ label, ...props }, ref) => { + const { classNames } = useStyles('Switch'); -/** @public */ -export type IconButtonOwnProps = GetPropDefTypes; + return ( + +
+ {label} + + ); + }, +); + +Switch.displayName = 'Switch'; diff --git a/packages/canon/src/components/Switch/index.tsx b/packages/canon/src/components/Switch/index.tsx new file mode 100644 index 0000000000..171969809d --- /dev/null +++ b/packages/canon/src/components/Switch/index.tsx @@ -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 * from './Switch'; +export * from './types'; diff --git a/packages/canon/src/components/Switch/types.ts b/packages/canon/src/components/Switch/types.ts new file mode 100644 index 0000000000..725671edd5 --- /dev/null +++ b/packages/canon/src/components/Switch/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 type { SwitchProps as AriaSwitchProps } from 'react-aria-components'; + +/** @public */ +export interface SwitchProps extends AriaSwitchProps { + /** + * The label of the switch + */ + label?: string; +} diff --git a/packages/canon/src/components/Table/Table.tsx b/packages/canon/src/components/Table/Table.tsx index abc0ce03c8..ca89cae0c0 100644 --- a/packages/canon/src/components/Table/Table.tsx +++ b/packages/canon/src/components/Table/Table.tsx @@ -20,63 +20,88 @@ import { TableCell } from './TableCell/TableCell'; import { TableCellText } from './TableCellText/TableCellText'; import { TableCellLink } from './TableCellLink/TableCellLink'; import { TableCellProfile } from './TableCellProfile/TableCellProfile'; +import { useStyles } from '../../hooks/useStyles'; const TableRoot = forwardRef< HTMLTableElement, React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( +
+ ); +}); TableRoot.displayName = 'TableRoot'; const TableHeader = forwardRef< HTMLTableSectionElement, React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + ); +}); TableHeader.displayName = 'TableHeader'; const TableBody = forwardRef< HTMLTableSectionElement, React.HTMLAttributes ->(({ className, ...props }, ref) => ( - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + ); +}); TableBody.displayName = 'TableBody'; const TableRow = forwardRef< HTMLTableRowElement, React.HTMLAttributes ->(({ className, ...props }, ref) => ( - - {props.children} - -)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + {props.children} + + ); +}); TableRow.displayName = 'TableRow'; const TableHead = forwardRef< HTMLTableCellElement, React.ThHTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + ); +}); TableHead.displayName = 'TableHead'; const TableCaption = forwardRef< HTMLTableCaptionElement, React.HTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + ); +}); TableCaption.displayName = 'TableCaption'; /** diff --git a/packages/canon/src/components/Table/TableCell/TableCell.styles.css b/packages/canon/src/components/Table/TableCell/TableCell.styles.css index 26d0868b49..dd3857d6f8 100644 --- a/packages/canon/src/components/Table/TableCell/TableCell.styles.css +++ b/packages/canon/src/components/Table/TableCell/TableCell.styles.css @@ -14,7 +14,7 @@ * limitations under the License. */ -.canon-TableCell { - padding: var(--canon-space-3); - font-size: var(--canon-font-size-3); +.bui-TableCell { + padding: var(--bui-space-3); + font-size: var(--bui-font-size-3); } diff --git a/packages/canon/src/components/Table/TableCell/TableCell.tsx b/packages/canon/src/components/Table/TableCell/TableCell.tsx index a8a87ac038..cbd025178c 100644 --- a/packages/canon/src/components/Table/TableCell/TableCell.tsx +++ b/packages/canon/src/components/Table/TableCell/TableCell.tsx @@ -16,14 +16,19 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; +import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCell = forwardRef< HTMLTableCellElement, React.TdHTMLAttributes ->(({ className, ...props }, ref) => ( -
-)); +>(({ className, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( + + ); +}); TableCell.displayName = 'TableCell'; export { TableCell }; diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx b/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx index 861f2643b2..bc9b66f18a 100644 --- a/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.stories.tsx @@ -14,12 +14,20 @@ * limitations under the License. */ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; import { TableCellLink } from './TableCellLink'; +import { MemoryRouter } from 'react-router-dom'; const meta = { title: 'Components/Table/TableCellLink', component: TableCellLink, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], } satisfies Meta; export default meta; @@ -28,7 +36,7 @@ type Story = StoryObj; export const Default: Story = { args: { title: 'I am a link', - href: 'https://canon.backstage.io', + href: 'https://ui.backstage.io', }, }; diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css b/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css index 3064bbb791..d4e10631fe 100644 --- a/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.styles.css @@ -14,8 +14,8 @@ * limitations under the License. */ -.canon-TableCellLink { +.bui-TableCellLink { display: flex; flex-direction: column; - gap: var(--canon-space-0_5); + gap: var(--bui-space-0_5); } diff --git a/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx b/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx index cb9d3e69a8..b7cd8c9580 100644 --- a/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx +++ b/packages/canon/src/components/Table/TableCellLink/TableCellLink.tsx @@ -19,27 +19,28 @@ import clsx from 'clsx'; import { TableCellLinkProps } from './types'; import { Text } from '../../Text/Text'; import { Link } from '../../Link/Link'; +import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCellLink = forwardRef( - ({ className, title, description, href, render, ...props }, ref) => ( -
- {title && ( - - {title} - - )} - {description && ( - - {description} - - )} -
- ), + ({ className, title, description, href, render, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( +
+ {title && {title}} + {description && ( + + {description} + + )} +
+ ); + }, ); TableCellLink.displayName = 'TableCellLink'; diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx index 5ee482f4fc..230fc0e3d1 100644 --- a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.stories.tsx @@ -14,12 +14,20 @@ * limitations under the License. */ -import type { Meta, StoryObj } from '@storybook/react'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; import { TableCellProfile } from './TableCellProfile'; +import { MemoryRouter } from 'react-router-dom'; const meta = { title: 'Components/Table/TableCellProfile', component: TableCellProfile, + decorators: [ + (Story: StoryFn) => ( + + + + ), + ], } satisfies Meta; export default meta; diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css index 30ab1fe683..4ec0f74899 100644 --- a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.styles.css @@ -14,14 +14,14 @@ * limitations under the License. */ -.canon-TableCellProfile { +.bui-TableCellProfile { display: flex; flex-direction: row; - gap: var(--canon-space-2); + gap: var(--bui-space-2); align-items: center; } -.canon-TableCellProfileAvatar { +.bui-TableCellProfileAvatar { display: inline-flex; justify-content: center; align-items: center; @@ -29,8 +29,8 @@ border-radius: 100%; user-select: none; font-weight: 500; - color: var(--canon-fg-primary); - background-color: var(--canon-bg-surface-2); + color: var(--bui-fg-primary); + background-color: var(--bui-bg-surface-2); font-size: 1rem; line-height: 1; overflow: hidden; @@ -38,20 +38,20 @@ width: 1.25rem; } -.canon-TableCellProfileAvatarImage { +.bui-TableCellProfileAvatarImage { object-fit: cover; height: 100%; width: 100%; } -.canon-TableCellProfileAvatarFallback { +.bui-TableCellProfileAvatarFallback { align-items: center; display: flex; justify-content: center; height: 100%; width: 100%; - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - box-shadow: inset 0 0 0 1px var(--canon-border); - border-radius: var(--canon-radius-full); + font-size: var(--bui-font-size-2); + font-weight: var(--bui-font-weight-regular); + box-shadow: inset 0 0 0 1px var(--bui-border); + border-radius: var(--bui-radius-full); } diff --git a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx index 78d82f59aa..7392baec68 100644 --- a/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx +++ b/packages/canon/src/components/Table/TableCellProfile/TableCellProfile.tsx @@ -20,40 +20,45 @@ import { TableCellProfileProps } from './types'; import { Text } from '../../Text/Text'; import { Link } from '../../Link/Link'; import { Avatar } from '@base-ui-components/react/avatar'; +import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCellProfile = forwardRef( - ({ className, src, name, to, withImage = true, ...rest }, ref) => ( -
- {withImage && ( - - - - {(name || '') - .split(' ') - .map(word => word[0]) - .join('') - .toLocaleUpperCase('en-US') - .slice(0, 1)} - - - )} - {name && to ? ( - {name} - ) : ( - {name} - )} -
- ), + ({ className, src, name, to, withImage = true, ...rest }, ref) => { + const { classNames } = useStyles('Table'); + + return ( +
+ {withImage && ( + + + + {(name || '') + .split(' ') + .map(word => word[0]) + .join('') + .toLocaleUpperCase('en-US') + .slice(0, 1)} + + + )} + {name && to ? ( + {name} + ) : ( + {name} + )} +
+ ); + }, ); TableCellProfile.displayName = 'TableCellProfile'; diff --git a/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css b/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css index 7a645a1fcb..f38f8b3689 100644 --- a/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css +++ b/packages/canon/src/components/Table/TableCellText/TableCellText.styles.css @@ -14,8 +14,8 @@ * limitations under the License. */ -.canon-TableCellText { +.bui-TableCellText { display: flex; flex-direction: column; - gap: var(--canon-space-0_5); + gap: var(--bui-space-0_5); } diff --git a/packages/canon/src/components/Table/TableCellText/TableCellText.tsx b/packages/canon/src/components/Table/TableCellText/TableCellText.tsx index d5496d799b..d707de18c7 100644 --- a/packages/canon/src/components/Table/TableCellText/TableCellText.tsx +++ b/packages/canon/src/components/Table/TableCellText/TableCellText.tsx @@ -18,23 +18,28 @@ import { forwardRef } from 'react'; import clsx from 'clsx'; import { TableCellTextProps } from './types'; import { Text } from '../../Text/Text'; +import { useStyles } from '../../../hooks/useStyles'; /** @public */ const TableCellText = forwardRef( - ({ className, title, description, ...props }, ref) => ( -
- {title && {title}} - {description && ( - - {description} - - )} -
- ), + ({ className, title, description, ...props }, ref) => { + const { classNames } = useStyles('Table'); + + return ( +
+ {title && {title}} + {description && ( + + {description} + + )} +
+ ); + }, ); TableCellText.displayName = 'TableCellText'; diff --git a/packages/canon/src/components/Table/styles.css b/packages/canon/src/components/Table/styles.css index 723287babe..63502eaa51 100644 --- a/packages/canon/src/components/Table/styles.css +++ b/packages/canon/src/components/Table/styles.css @@ -1,25 +1,25 @@ -.canon-TableRoot { +.bui-TableRoot { width: 100%; caption-side: bottom; border-collapse: collapse; } -.canon-TableHead { +.bui-TableHead { text-align: left; - padding: var(--canon-space-3); - font-size: var(--canon-font-size-3); - color: var(--canon-fg-primary); + padding: var(--bui-space-3); + font-size: var(--bui-font-size-3); + color: var(--bui-fg-primary); } -.canon-TableBody { - color: var(--canon-fg-primary); +.bui-TableBody { + color: var(--bui-fg-primary); } -.canon-TableRow { - border-bottom: 1px solid var(--canon-border); +.bui-TableRow { + border-bottom: 1px solid var(--bui-border); transition: color 0.2s ease-in-out; } -.canon-TableBody .canon-TableRow:hover { - background-color: var(--canon-gray-2); +.bui-TableBody .bui-TableRow:hover { + background-color: var(--bui-gray-2); } diff --git a/packages/canon/src/components/Tabs/Tabs.stories.tsx b/packages/canon/src/components/Tabs/Tabs.stories.tsx index 31945b1e02..6cfcd5570f 100644 --- a/packages/canon/src/components/Tabs/Tabs.stories.tsx +++ b/packages/canon/src/components/Tabs/Tabs.stories.tsx @@ -14,35 +14,163 @@ * limitations under the License. */ -import type { Meta, StoryObj } from '@storybook/react'; -import { Tabs } from './Tabs'; +import type { Meta, StoryFn, StoryObj } from '@storybook/react'; +import { Tabs, TabList, Tab, TabPanel } from './Tabs'; +import { MemoryRouter } from 'react-router-dom'; +import { Box } from '../Box'; +import { Text } from '../Text'; const meta = { title: 'Components/Tabs', - component: Tabs.Root, -} satisfies Meta; + component: Tabs, +} satisfies Meta; export default meta; type Story = StoryObj; -const panelStyles = { - padding: 'var(--canon-space-3)', - fontSize: 'var(--canon-font-size-2)', -}; +const withRouter = (Story: StoryFn) => ( + + + +); 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 - - ), + children: '', }, + decorators: [withRouter], + render: () => ( + + + Tab 1 + Tab 2 + Tab 3 With long title + + + ), +}; + +export const WithTabPanels: Story = { + args: { + children: '', + }, + decorators: [withRouter], + render: () => ( + + + Settings + Profile + Preferences + + + Settings panel content goes here + + + Profile panel content goes here + + + Preferences panel content goes here + + + ), +}; + +export const WithMockedURLTab2: Story = { + args: { + children: '', + }, + render: () => ( + + + + + Tab 1 + + + Tab 2 + + + Tab 3 With long title + + + + + + Current URL is mocked to be: /tab2 + + + Notice how the "Tab 2" tab is selected (highlighted) because it + matches the current path. + + + + ), +}; + +export const WithMockedURLTab3: Story = { + args: { + children: '', + }, + render: () => ( + + + + + Tab 1 + + + Tab 2 + + + Tab 3 With long title + + + + + + Current URL is mocked to be: /tab3 + + + Notice how the "Tab 3 With long title" tab is selected (highlighted) + because it matches the current path. + + + + ), +}; + +export const WithMockedURLNoMatch: Story = { + args: { + children: '', + }, + render: () => ( + + + + + Tab 1 + + + Tab 2 + + + Tab 3 With long title + + + + + + Current URL is mocked to be: /some-other-page + + + No tab is selected because the current path doesn't match any tab's + href. + + + Tabs without href (like "Tab 1", "Tab 2", "Tab 3 With long title") + fall back to React Aria's internal state. + + + + ), }; diff --git a/packages/canon/src/components/Tabs/Tabs.styles.css b/packages/canon/src/components/Tabs/Tabs.styles.css index 84a4d378c1..819fe458ed 100644 --- a/packages/canon/src/components/Tabs/Tabs.styles.css +++ b/packages/canon/src/components/Tabs/Tabs.styles.css @@ -1,76 +1,82 @@ -.canon-TabsRoot { - border: 1px solid var(--color-gray-200); - border-radius: 0.375rem; +.bui-Tabs { + /* Initialize CSS variables */ + --active-tab-left: 0px; + --active-tab-right: 0px; + --active-tab-top: 0px; + --active-tab-bottom: 0px; + --active-tab-width: 0px; + --active-tab-height: 0px; + --active-transition-duration: 0s; + + --hovered-tab-left: 0px; + --hovered-tab-right: 0px; + --hovered-tab-top: 0px; + --hovered-tab-bottom: 0px; + --hovered-tab-width: 0px; + --hovered-tab-height: 0px; + --hovered-tab-opacity: 0; + --hovered-transition-duration: 0s; } -.canon-TabsList { +.bui-TabList { display: flex; - position: relative; - z-index: 0; + flex-direction: row; } -.canon-TabsTab { +.bui-TabListWrapper { + position: relative; +} + +.bui-Tab { + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-secondary); + height: 36px; 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; + position: relative; + z-index: 2; + padding-inline: var(--bui-space-2); - &[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; - } + &[data-selected='true'] { + color: var(--bui-fg-primary); } } -.canon-TabsIndicator { +.bui-TabActive { + content: ''; position: absolute; - z-index: -1; - left: 0; - bottom: 0; - translate: var(--active-tab-left) -50%; - width: var(--active-tab-width); + left: calc(var(--active-tab-left) + var(--bui-space-2)); + bottom: -1px; + width: calc(var(--active-tab-width) - var(--bui-space-4)); height: 1px; - background-color: var(--canon-bg-solid); - transition-property: translate, width; - transition-duration: 200ms; - transition-timing-function: ease-in-out; + background-color: var(--bui-fg-primary); + border-radius: 4px; + transition: left var(--active-transition-duration) ease-out, + opacity 0.15s ease-out, width var(--active-transition-duration) ease-out; + opacity: 1; } -.canon-TabsPanel { - &[hidden] { - display: none; - } +.bui-TabHovered { + content: ''; + position: absolute; + left: var(--hovered-tab-left); + top: calc(var(--hovered-tab-top) + 4px); + width: var(--hovered-tab-width); + height: calc(var(--hovered-tab-height) - 8px); + background-color: var(--bui-gray-2); + border-radius: 4px; + opacity: var(--hovered-tab-opacity); + transition: left var(--hovered-transition-duration) ease-out, + top var(--hovered-transition-duration) ease-out, + width var(--hovered-transition-duration) ease-out, + height var(--hovered-transition-duration) ease-out, opacity 0.15s ease-out; +} + +.bui-TabPanel { + padding-inline: var(--bui-space-2); + padding-top: var(--bui-space-4); } diff --git a/packages/canon/src/components/Tabs/Tabs.tsx b/packages/canon/src/components/Tabs/Tabs.tsx index b4f93a5b31..f77338d34e 100644 --- a/packages/canon/src/components/Tabs/Tabs.tsx +++ b/packages/canon/src/components/Tabs/Tabs.tsx @@ -14,66 +14,195 @@ * 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'; +import { + useRef, + useState, + Children, + cloneElement, + isValidElement, + ReactNode, + createContext, + useContext, +} from 'react'; +import type { + TabsProps, + TabListProps, + TabPanelProps, + TabsContextValue, +} from './types'; +import { useLocation, useNavigate, useHref } from 'react-router-dom'; +import { TabsIndicators } from './TabsIndicators'; +import { + Tabs as AriaTabs, + TabList as AriaTabList, + Tab as AriaTab, + TabPanel as AriaTabPanel, + RouterProvider, + TabProps as AriaTabProps, +} from 'react-aria-components'; -const TabsRoot = forwardRef< - React.ElementRef, - TabsRootWithoutOrientation ->(({ className, ...props }, ref) => ( - -)); -TabsRoot.displayName = TabsPrimitive.Root.displayName; +import { useStyles } from '../../hooks/useStyles'; -const TabsList = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - {children} - - -)); -TabsList.displayName = TabsPrimitive.List.displayName; +const TabsContext = createContext(undefined); -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, +const useTabsContext = () => { + const context = useContext(TabsContext); + if (!context) { + throw new Error('Tab components must be used within a Tabs component'); + } + return context; +}; + +/** + * A component that renders a list of tabs. + * + * @public + */ +export const Tabs = (props: TabsProps) => { + const { children, ...rest } = props; + const { classNames } = useStyles('Tabs'); + const tabsRef = useRef(null); + const tabRefs = useRef>(new Map()); + const [hoveredKey, setHoveredKey] = useState(null); + const prevHoveredKey = useRef(null); + let navigate = useNavigate(); + const location = useLocation(); + + const setTabRef = (key: string, element: HTMLDivElement | null) => { + if (element) { + tabRefs.current.set(key, element); + } else { + tabRefs.current.delete(key); + } + }; + + // If selectedKey is not provided, try to determine it from the current route + const computedSelectedKey = (() => { + const childrenArray = Children.toArray(children as ReactNode); + for (const child of childrenArray) { + if (isValidElement(child) && child.type === TabList) { + const tabListChildren = Children.toArray(child.props.children); + for (const tabChild of tabListChildren) { + if ( + isValidElement(tabChild) && + tabChild.props.href === location.pathname + ) { + return tabChild.props.id; + } + } + } + } + return undefined; + })(); + + if (!children) return null; + + const contextValue: TabsContextValue = { + tabsRef, + tabRefs, + hoveredKey, + prevHoveredKey, + setHoveredKey, + setTabRef, + }; + + return ( + + + + {children as ReactNode} + + + + ); +}; + +/** + * A component that renders a list of tabs. + * + * @public + */ +export const TabList = (props: TabListProps) => { + const { children, ...rest } = props; + const { classNames } = useStyles('Tabs'); + const { setHoveredKey, tabRefs, tabsRef, hoveredKey, prevHoveredKey } = + useTabsContext(); + + const handleHover = (key: string | null) => { + setHoveredKey(key); + }; + + // Clone children with additional props for hover and ref management + const enhancedChildren = Children.map(children as ReactNode, child => { + if (isValidElement(child)) { + return cloneElement(child, { + onHoverStart: () => handleHover(child.props.id as string), + onHoverEnd: () => handleHover(null), + } as Partial); + } + return child; + }); + + return ( +
+ + {enhancedChildren} + + +
+ ); +}; + +/** + * A component that renders a tab. + * + * @public + */ +export const Tab = (props: AriaTabProps) => { + const { href, children, id, ...rest } = props; + const { classNames } = useStyles('Tabs'); + const { setTabRef } = useTabsContext(); + + return ( + setTabRef(id as string, el as HTMLDivElement)} + href={href} + {...rest} + > + {children} + + ); +}; + +/** + * A component that renders the content of a tab. + * + * @public + */ +export const TabPanel = (props: TabPanelProps) => { + const { children, ...rest } = props; + const { classNames } = useStyles('Tabs'); + + return ( + + {children} + + ); }; diff --git a/packages/canon/src/components/Tabs/TabsIndicators.tsx b/packages/canon/src/components/Tabs/TabsIndicators.tsx new file mode 100644 index 0000000000..fa2e29300d --- /dev/null +++ b/packages/canon/src/components/Tabs/TabsIndicators.tsx @@ -0,0 +1,191 @@ +/* + * 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 { TabListStateContext } from 'react-aria-components'; +import { useStyles } from '../../hooks/useStyles'; +import { useContext, useEffect, useCallback, useRef } from 'react'; +import type { TabsIndicatorsProps } from './types'; + +/** + * A component that renders the indicators for the toolbar. + * + * @internal + */ +export const TabsIndicators = (props: TabsIndicatorsProps) => { + const { tabRefs, tabsRef, hoveredKey, prevHoveredKey } = props; + const { classNames } = useStyles('Tabs'); + const state = useContext(TabListStateContext); + const prevSelectedKey = useRef(null); + + const updateCSSVariables = useCallback(() => { + if (!tabsRef.current) return; + + const tabsRect = tabsRef.current.getBoundingClientRect(); + + // Set active tab variables + if (state?.selectedKey) { + const activeTab = tabRefs.current.get(state.selectedKey.toString()); + + if (activeTab) { + const activeRect = activeTab.getBoundingClientRect(); + const relativeLeft = activeRect.left - tabsRect.left; + const relativeTop = activeRect.top - tabsRect.top; + + // Control transition timing based on whether this is the first time setting active tab + const isFirstActiveTab = prevSelectedKey.current === null; + + if (isFirstActiveTab) { + // First time setting active tab: no transitions for position + tabsRef.current.style.setProperty( + '--active-transition-duration', + '0s', + ); + // Enable transitions on next frame for future tab switches + requestAnimationFrame(() => { + if (tabsRef.current) { + tabsRef.current.style.setProperty( + '--active-transition-duration', + '0.25s', + ); + } + }); + } else { + // Switching between tabs: full transitions + tabsRef.current.style.setProperty( + '--active-transition-duration', + '0.25s', + ); + } + + // Update previous selected key for next time + prevSelectedKey.current = state.selectedKey.toString(); + + tabsRef.current.style.setProperty( + '--active-tab-left', + `${relativeLeft}px`, + ); + tabsRef.current.style.setProperty( + '--active-tab-right', + `${relativeLeft + activeRect.width}px`, + ); + tabsRef.current.style.setProperty( + '--active-tab-top', + `${relativeTop}px`, + ); + tabsRef.current.style.setProperty( + '--active-tab-bottom', + `${relativeTop + activeRect.height}px`, + ); + tabsRef.current.style.setProperty( + '--active-tab-width', + `${activeRect.width}px`, + ); + tabsRef.current.style.setProperty( + '--active-tab-height', + `${activeRect.height}px`, + ); + } + } + + // Set hovered tab variables + if (hoveredKey) { + const hoveredTab = tabRefs.current.get(hoveredKey); + if (hoveredTab) { + const hoveredRect = hoveredTab.getBoundingClientRect(); + const relativeLeft = hoveredRect.left - tabsRect.left; + const relativeTop = hoveredRect.top - tabsRect.top; + + tabsRef.current.style.setProperty( + '--hovered-tab-left', + `${relativeLeft}px`, + ); + tabsRef.current.style.setProperty( + '--hovered-tab-right', + `${relativeLeft + hoveredRect.width}px`, + ); + tabsRef.current.style.setProperty( + '--hovered-tab-top', + `${relativeTop}px`, + ); + tabsRef.current.style.setProperty( + '--hovered-tab-bottom', + `${relativeTop + hoveredRect.height}px`, + ); + tabsRef.current.style.setProperty( + '--hovered-tab-width', + `${hoveredRect.width}px`, + ); + tabsRef.current.style.setProperty( + '--hovered-tab-height', + `${hoveredRect.height}px`, + ); + // Control transition timing based on whether this is a new hover session + const isNewHoverSession = prevHoveredKey.current === null; + + if (isNewHoverSession) { + // Starting new hover session: no transitions for position + tabsRef.current.style.setProperty( + '--hovered-transition-duration', + '0s', + ); + // Enable transitions on next frame for future tab switches + requestAnimationFrame(() => { + if (tabsRef.current) { + tabsRef.current.style.setProperty( + '--hovered-transition-duration', + '0.2s', + ); + } + }); + } else { + // Moving between tabs in same session: full transitions + tabsRef.current.style.setProperty( + '--hovered-transition-duration', + '0.2s', + ); + } + + // Update previous hover key for next time + prevHoveredKey.current = hoveredKey; + + tabsRef.current.style.setProperty('--hovered-tab-opacity', '1'); + } + } else { + // When not hovering, hide with opacity and reset for next hover session + tabsRef.current.style.setProperty('--hovered-tab-opacity', '0'); + + // Reset previous hover key so next hover is treated as new session + prevHoveredKey.current = null; + } + }, [state?.selectedKey, hoveredKey, tabRefs.current]); + + useEffect(() => { + updateCSSVariables(); + }, [updateCSSVariables, tabRefs.current.size]); + + useEffect(() => { + const handleResize = () => updateCSSVariables(); + window.addEventListener('resize', handleResize); + return () => window.removeEventListener('resize', handleResize); + }, [updateCSSVariables]); + + return ( + <> +
+
+ + ); +}; diff --git a/packages/canon/src/components/Tabs/index.ts b/packages/canon/src/components/Tabs/index.ts index ba6ca91de7..ac65c77479 100644 --- a/packages/canon/src/components/Tabs/index.ts +++ b/packages/canon/src/components/Tabs/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { Tabs } from './Tabs'; -export type { TabsRootWithoutOrientation } from './types'; +export { Tabs, TabList, Tab, TabPanel } from './Tabs'; +export type { TabsProps, TabListProps, TabPanelProps } from './types'; diff --git a/packages/canon/src/components/Tabs/types.ts b/packages/canon/src/components/Tabs/types.ts index f3b6342761..38eaa65b74 100644 --- a/packages/canon/src/components/Tabs/types.ts +++ b/packages/canon/src/components/Tabs/types.ts @@ -13,13 +13,70 @@ * 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 +import type { + TabsProps as AriaTabsProps, + TabListProps as AriaTabListProps, + TabPanelProps as AriaTabPanelProps, +} from 'react-aria-components'; +import { MutableRefObject } from 'react'; -/** @public */ -export interface TabsRootWithoutOrientation - extends Omit< - React.ComponentPropsWithoutRef, - 'orientation' - > {} +/** + * Props for the Tabs component. + * + * @public + */ +export interface TabsProps extends AriaTabsProps {} + +/** + * Props for the TabList component. + * + * @public + */ +export interface TabListProps extends Omit, 'items'> {} + +/** Context for sharing refs between Tabs and TabList + * + * @internal + */ +export interface TabsContextValue { + tabsRef: React.RefObject; + tabRefs: React.MutableRefObject>; + hoveredKey: string | null; + prevHoveredKey: React.MutableRefObject; + setHoveredKey: (key: string | null) => void; + setTabRef: (key: string, element: HTMLDivElement | null) => void; +} + +/** + * Props for the TabPanel component. + * + * @public + */ +export interface TabPanelProps extends AriaTabPanelProps {} + +/** + * Props for the TabsIndicators component. + * + * @internal + */ +export interface TabsIndicatorsProps { + tabRefs: MutableRefObject>; + tabsRef: MutableRefObject; + hoveredKey: string | null; + prevHoveredKey: MutableRefObject; +} + +/** + * Context value for sharing refs and state between Tabs and TabList components. + * + * @internal + */ +export interface TabsContextValue { + tabsRef: React.RefObject; + tabRefs: React.MutableRefObject>; + hoveredKey: string | null; + prevHoveredKey: React.MutableRefObject; + setHoveredKey: (key: string | null) => void; + setTabRef: (key: string, element: HTMLDivElement | null) => void; +} diff --git a/packages/canon/src/components/Text/Text.stories.tsx b/packages/canon/src/components/Text/Text.stories.tsx index 5fe0c12cb5..5bb918f52e 100644 --- a/packages/canon/src/components/Text/Text.stories.tsx +++ b/packages/canon/src/components/Text/Text.stories.tsx @@ -111,7 +111,7 @@ export const WrappedInLink: Story = { export const CustomRender: Story = { args: { ...Default.args, - render: , + as: 'span', }, }; diff --git a/packages/canon/src/components/Text/Text.tsx b/packages/canon/src/components/Text/Text.tsx index 7e4dc74720..275765c42d 100644 --- a/packages/canon/src/components/Text/Text.tsx +++ b/packages/canon/src/components/Text/Text.tsx @@ -14,47 +14,53 @@ * limitations under the License. */ -import { forwardRef, useRef } from 'react'; -import { useResponsiveValue } from '../../hooks/useResponsiveValue'; -import { useRender } from '@base-ui-components/react/use-render'; +import { forwardRef } from 'react'; import clsx from 'clsx'; - +import type { ElementType } from 'react'; import type { TextProps } from './types'; +import { useStyles } from '../../hooks/useStyles'; + +function TextComponent( + { + as, + variant = 'body', + weight = 'regular', + color = 'primary', + className, + truncate, + style, + ...restProps + }: TextProps, + ref: React.Ref, +) { + const Component = as || 'p'; + + const { classNames, dataAttributes } = useStyles('Text', { + variant, + weight, + color, + }); + + return ( + + ); +} + +TextComponent.displayName = 'Text'; /** @public */ -export const Text = forwardRef( - (props, ref) => { - const { - variant = 'body', - weight = 'regular', - color = 'primary', - className, - truncate, - render =

, - ...restProps - } = props; - - // Get the responsive values for the variant and weight - const responsiveVariant = useResponsiveValue(variant); - const responsiveWeight = useResponsiveValue(weight); - const responsiveColor = useResponsiveValue(color); - const internalRef = useRef(null); - - 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(); - }, -); +export const Text = forwardRef(TextComponent) as { + ( + props: TextProps & { ref?: React.ComponentPropsWithRef['ref'] }, + ): React.ReactElement, T>; + displayName: string; +}; Text.displayName = 'Text'; diff --git a/packages/canon/src/components/Text/index.ts b/packages/canon/src/components/Text/index.ts index ae1f532b2c..423288a8a4 100644 --- a/packages/canon/src/components/Text/index.ts +++ b/packages/canon/src/components/Text/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { Text } from './Text'; -export type { TextProps } from './types'; +export * from './Text'; +export * from './types'; diff --git a/packages/canon/src/components/Text/styles.css b/packages/canon/src/components/Text/styles.css index c0cd781007..a940fcf9fa 100644 --- a/packages/canon/src/components/Text/styles.css +++ b/packages/canon/src/components/Text/styles.css @@ -14,61 +14,61 @@ * limitations under the License. */ -.canon-Text { - font-family: var(--canon-font-regular); +.bui-Text { + font-family: var(--bui-font-regular); padding: 0; margin: 0; } -.canon-Text[data-variant='body'] { - font-size: var(--canon-font-size-3); +.bui-Text[data-variant='body'] { + font-size: var(--bui-font-size-3); line-height: 140%; } -.canon-Text[data-variant='subtitle'] { - font-size: var(--canon-font-size-4); +.bui-Text[data-variant='subtitle'] { + font-size: var(--bui-font-size-4); line-height: 140%; } -.canon-Text[data-variant='caption'] { - font-size: var(--canon-font-size-2); +.bui-Text[data-variant='caption'] { + font-size: var(--bui-font-size-2); line-height: 140%; } -.canon-Text[data-variant='label'] { - font-size: var(--canon-font-size-1); +.bui-Text[data-variant='label'] { + font-size: var(--bui-font-size-1); line-height: 140%; } -.canon-Text[data-weight='regular'] { - font-weight: var(--canon-font-weight-regular); +.bui-Text[data-weight='regular'] { + font-weight: var(--bui-font-weight-regular); } -.canon-Text[data-weight='bold'] { - font-weight: var(--canon-font-weight-bold); +.bui-Text[data-weight='bold'] { + font-weight: var(--bui-font-weight-bold); } -.canon-Text[data-color='primary'] { - color: var(--canon-fg-primary); +.bui-Text[data-color='primary'] { + color: var(--bui-fg-primary); } -.canon-Text[data-color='secondary'] { - color: var(--canon-fg-secondary); +.bui-Text[data-color='secondary'] { + color: var(--bui-fg-secondary); } -.canon-Text[data-color='danger'] { - color: var(--canon-fg-danger); +.bui-Text[data-color='danger'] { + color: var(--bui-fg-danger); } -.canon-Text[data-color='warning'] { - color: var(--canon-fg-warning); +.bui-Text[data-color='warning'] { + color: var(--bui-fg-warning); } -.canon-Text[data-color='success'] { - color: var(--canon-fg-success); +.bui-Text[data-color='success'] { + color: var(--bui-fg-success); } -.canon-Text[data-truncate] { +.bui-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 f9c3c0aaca..ed01956248 100644 --- a/packages/canon/src/components/Text/types.ts +++ b/packages/canon/src/components/Text/types.ts @@ -14,13 +14,12 @@ * limitations under the License. */ -import type { CSSProperties } from 'react'; +import type { ElementType, ComponentPropsWithRef } from 'react'; import type { Breakpoint } from '../../types'; -import type { useRender } from '@base-ui-components/react/use-render'; /** @public */ -export interface TextProps - extends Omit, 'color'> { +export type TextOwnProps = { + as?: 'p' | 'span' | 'label'; variant?: | 'subtitle' | 'body' @@ -42,5 +41,9 @@ export interface TextProps >; truncate?: boolean; className?: string; - style?: CSSProperties; -} + style?: React.CSSProperties; +}; + +/** @public */ +export type TextProps = TextOwnProps & + Omit, keyof TextOwnProps>; diff --git a/packages/canon/src/components/TextField/TextField.stories.tsx b/packages/canon/src/components/TextField/TextField.stories.tsx index 7e625dad24..7adc84e909 100644 --- a/packages/canon/src/components/TextField/TextField.stories.tsx +++ b/packages/canon/src/components/TextField/TextField.stories.tsx @@ -16,12 +16,22 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TextField } from './TextField'; -import { Flex } from '../Flex'; +import { Form } from 'react-aria-components'; import { Icon } from '../Icon'; +import { Flex } from '../Flex'; +import { FieldLabel } from '../FieldLabel'; const meta = { - title: 'Components/TextField', + title: 'Forms/TextField', component: TextField, + argTypes: { + isRequired: { + control: 'boolean', + }, + icon: { + control: 'object', + }, + }, } satisfies Meta; export default meta; @@ -37,7 +47,19 @@ export const Default: Story = { }, }; -export const Filled: Story = { +export const Sizes: Story = { + args: { + ...Default.args, + }, + render: args => ( + + } /> + } /> + + ), +}; + +export const DefaultValue: Story = { args: { ...Default.args, defaultValue: 'https://example.com', @@ -61,84 +83,70 @@ export const WithDescription: Story = { export const Required: Story = { args: { ...WithLabel.args, - required: true, + isRequired: true, }, }; export const Disabled: Story = { - args: { - ...WithLabel.args, - disabled: true, - }, -}; - -export const Sizes: Story = { args: { ...Default.args, - label: 'Label', - description: 'Description', - }, - render: args => ( - - } /> - } /> - - ), -}; - -export const Responsive: Story = { - args: { - ...WithLabel.args, - size: { - initial: 'small', - sm: 'medium', - }, - }, -}; - -export const WithError: Story = { - args: { - ...WithLabel.args, - error: 'Invalid URL', - }, -}; - -export const WithErrorAndDescription: Story = { - args: { - ...WithLabel.args, - error: 'Invalid URL', - description: 'Description', + isDisabled: true, }, }; export const WithIcon: Story = { args: { - ...WithLabel.args, - placeholder: 'Search...', - icon: , + ...Default.args, }, + render: args => ( + } + /> + ), }; export const DisabledWithIcon: Story = { args: { ...WithIcon.args, - disabled: true, + isDisabled: true, }, }; -export const WithOnClear: Story = { +export const ShowError: Story = { args: { ...WithLabel.args, - placeholder: 'Search...', - type: 'search', - onClear: () => console.log('Cleared!'), + }, + render: args => ( +

+ + + ), +}; + +export const Validation: Story = { + args: { + ...WithLabel.args, + validate: value => (value === 'admin' ? 'Nice try!' : null), }, }; -export const DisabledWithOnClear: Story = { - args: { - ...WithOnClear.args, - defaultValue: 'Testing', - disabled: true, - }, +export const CustomField: Story = { + render: () => ( + <> + + + + ), }; diff --git a/packages/canon/src/components/TextField/TextField.styles.css b/packages/canon/src/components/TextField/TextField.styles.css index be7ec3379c..abdce20294 100644 --- a/packages/canon/src/components/TextField/TextField.styles.css +++ b/packages/canon/src/components/TextField/TextField.styles.css @@ -14,152 +14,101 @@ * limitations under the License. */ -.canon-TextField { +.bui-TextField { display: flex; flex-direction: column; - font-family: var(--canon-font-regular); + font-family: var(--bui-font-regular); width: 100%; } -.canon-TextFieldLabel { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-primary); - margin-bottom: var(--canon-space-1_5); - margin-right: auto; - cursor: pointer; +.bui-InputWrapper { + position: relative; + + &[data-size='small'] .bui-Input { + height: 2rem; + } + + &[data-size='medium'] .bui-Input { + height: 2.5rem; + } + + &[data-size='small'] .bui-Input[data-icon] { + padding-left: var(--bui-space-8); + } + + &[data-size='medium'] .bui-Input[data-icon] { + padding-left: var(--bui-space-9); + } } -.canon-TextFieldLabel[data-disabled] { - cursor: default; +.bui-InputIcon { + position: absolute; + left: var(--bui-space-3); + top: 50%; + transform: translateY(-50%); + margin-right: var(--bui-space-1); + color: var(--bui-fg-primary); + flex-shrink: 0; + + &[data-size='small'], + &[data-size='small'] svg { + width: 1rem; + height: 1rem; + } + + &[data-size='medium'], + &[data-size='medium'] svg { + width: 1.25rem; + height: 1.25rem; + } } -.canon-TextFieldDescription { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-secondary); - margin: 0; - padding-top: var(--canon-space-1_5); -} - -.canon-TextFieldError { - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-danger); - margin: 0; - padding-top: var(--canon-space-1_5); -} - -.canon-TextFieldInputWrapper { +.bui-Input { display: flex; align-items: center; - padding: 0 var(--canon-space-3); - border-radius: var(--canon-radius-3); - border: 1px solid var(--canon-border); - background-color: var(--canon-bg-surface-1); -} - -.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; - font-size: var(--canon-font-size-3); - font-family: var(--canon-font-regular); - font-weight: var(--canon-font-weight-regular); - color: var(--canon-fg-primary); + padding: 0 var(--bui-space-3); + border-radius: var(--bui-radius-3); + border: 1px solid var(--bui-border); + background-color: var(--bui-bg-surface-1); + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + font-weight: var(--bui-font-weight-regular); + color: var(--bui-fg-primary); transition: border-color 0.2s ease-in-out, outline-color 0.2s ease-in-out; width: 100%; height: 100%; cursor: inherit; - padding: 0; -} -.canon-TextFieldInput:not([data-filled]):has(+ .canon-TextFieldClearButton) { - padding-right: 1.25rem; -} + &::-webkit-search-cancel-button, + &::-webkit-search-decoration { + -webkit-appearance: none; + } -.canon-TextFieldInput[type='search']::-webkit-search-cancel-button, -.canon-TextFieldInput[type='search']::-webkit-search-decoration { - appearance: none; -} + &::placeholder { + color: var(--bui-fg-secondary); + } -.canon-TextFieldClearButton { - display: none; - padding: 0; - margin-left: var(--canon-space-1); - background: none; - border: none; - vertical-align: middle; - color: var(--canon-fg-primary); -} + &[data-focused] { + outline-color: var(--bui-border-pressed); + outline-width: 0px; + } -.canon-TextFieldInput[data-filled] + .canon-TextFieldClearButton { - display: inline-block; -} + &[data-hovered] { + border-color: var(--bui-border-hover); + } -.canon-TextFieldClearButtonIcon { - display: block; -} + &[data-focused] { + border-color: var(--bui-border-pressed); + outline-width: 0px; + } -.canon-TextFieldInput::placeholder { - color: var(--canon-fg-secondary); -} + &[data-invalid] { + border-color: var(--bui-fg-danger); + } -.canon-TextFieldInput[data-focused] { - outline-color: var(--canon-border-pressed); - outline-width: 0px; -} - -.canon-TextFieldInputWrapper:has(> .canon-TextFieldInput:hover) { - border-color: var(--canon-border-hover); -} - -.canon-TextField[data-focused] .canon-TextFieldInputWrapper { - border-color: var(--canon-border-pressed); -} - -.canon-TextField[data-invalid] .canon-TextFieldInputWrapper { - border-color: var(--canon-fg-danger); -} - -.canon-TextField[data-disabled] .canon-TextFieldInputWrapper { - opacity: 0.5; - cursor: not-allowed; - border: 1px solid var(--canon-border-disabled); -} - -.canon-TextField[data-disabled] .canon-TextFieldClearButton { - cursor: inherit; -} - -.canon-TextFieldInputWrapper[data-size='small'] { - height: 2rem; -} - -.canon-TextFieldInputWrapper[data-size='medium'] { - height: 2.5rem; -} - -.canon-TextFieldRequired { - color: var(--canon-fg-secondary); - font-size: var(--canon-font-size-2); - font-weight: var(--canon-font-weight-regular); - margin-left: var(--canon-space-1); + &[data-disabled] { + opacity: 0.5; + cursor: not-allowed; + border: 1px solid var(--bui-border-disabled); + } } diff --git a/packages/canon/src/components/TextField/TextField.tsx b/packages/canon/src/components/TextField/TextField.tsx index e4cf43a711..2756c3c85f 100644 --- a/packages/canon/src/components/TextField/TextField.tsx +++ b/packages/canon/src/components/TextField/TextField.tsx @@ -14,88 +14,83 @@ * limitations under the License. */ -import { Field } from '@base-ui-components/react/field'; -import { forwardRef } from 'react'; -import { useResponsiveValue } from '../../hooks/useResponsiveValue'; +import { forwardRef, useEffect } from 'react'; +import { Input, TextField as AriaTextField } from 'react-aria-components'; import clsx from 'clsx'; +import { FieldLabel } from '../FieldLabel'; +import { FieldError } from '../FieldError'; import type { TextFieldProps } from './types'; -import { Icon } from '../Icon'; +import { useStyles } from '../../hooks/useStyles'; /** @public */ export const TextField = forwardRef( - (props: TextFieldProps, ref) => { + (props, ref) => { const { className, + icon, size = 'small', label, + secondaryLabel, description, - error, - required, - style, - disabled, - icon, - onClear, + isRequired, + 'aria-label': ariaLabel, + 'aria-labelledby': ariaLabelledBy, + placeholder, ...rest } = props; - // Get the responsive value for the variant - const responsiveSize = useResponsiveValue(size); + useEffect(() => { + if (!label && !ariaLabel && !ariaLabelledBy) { + console.warn( + 'TextField requires either a visible label, aria-label, or aria-labelledby for accessibility', + ); + } + }, [label, ariaLabel, ariaLabelledBy]); + + const { classNames, dataAttributes } = useStyles('TextField', { + size, + }); + + // If a secondary label is provided, use it. Otherwise, use 'Required' if the field is required. + const secondaryLabelText = + secondaryLabel || (isRequired ? 'Required' : null); return ( - - {label && ( - - {label} - {required && ( - - )} - - )} -
+ +
{icon && ( )} - - {onClear && ( - - )}
- {description && ( - - {description} - - )} - {error && ( - - {error} - - )} - + + ); }, ); diff --git a/packages/canon/src/components/TextField/index.ts b/packages/canon/src/components/TextField/index.ts index 29df00ccaf..95a838fb46 100644 --- a/packages/canon/src/components/TextField/index.ts +++ b/packages/canon/src/components/TextField/index.ts @@ -15,4 +15,4 @@ */ export * from './TextField'; -export type { TextFieldProps } from './types'; +export * from './types'; diff --git a/packages/canon/src/components/TextField/types.ts b/packages/canon/src/components/TextField/types.ts index 8a37a23efe..75a5c68aaa 100644 --- a/packages/canon/src/components/TextField/types.ts +++ b/packages/canon/src/components/TextField/types.ts @@ -14,15 +14,19 @@ * limitations under the License. */ +import type { TextFieldProps as AriaTextFieldProps } from 'react-aria-components'; +import { ReactNode } from 'react'; import type { Breakpoint } from '../../types'; -import type { ReactNode, MouseEventHandler } from 'react'; +import type { FieldLabelProps } from '../FieldLabel/types'; + /** @public */ export interface TextFieldProps - extends Omit, 'size'> { + extends AriaTextFieldProps, + Omit { /** - * The class name of the text field + * An icon to render before the input */ - className?: string; + icon?: ReactNode; /** * The size of the text field @@ -31,32 +35,7 @@ export interface TextFieldProps size?: 'small' | 'medium' | Partial>; /** - * The label of the text field + * Text to display in the input when it has no value */ - label?: string; - - /** - * The description of the text field - */ - description?: string; - - /** - * The name of the text field - */ - name: string; - - /** - * The error message of the text field - */ - error?: string | null; - - /** - * An icon to render before the input - */ - icon?: ReactNode; - - /** - * Handler to call when the clear button is pressed - */ - onClear?: MouseEventHandler; + placeholder?: string; } diff --git a/packages/canon/src/components/Tooltip/Tooltip.stories.tsx b/packages/canon/src/components/Tooltip/Tooltip.stories.tsx index e0ae6f0be8..d016bdbf04 100644 --- a/packages/canon/src/components/Tooltip/Tooltip.stories.tsx +++ b/packages/canon/src/components/Tooltip/Tooltip.stories.tsx @@ -15,66 +15,110 @@ */ import type { Meta, StoryObj } from '@storybook/react'; -import { Tooltip } from './Tooltip'; +import { TooltipTrigger, Tooltip } from './Tooltip'; import { Button } from '../Button/Button'; const meta = { title: 'Components/Tooltip', - component: Tooltip.Root, -} satisfies Meta; + component: TooltipTrigger, +} satisfies Meta; export default meta; type Story = StoryObj; export const Default: Story = { - args: { - children: ( - <> - ( - - )} - /> - - - Nice! - - - - ), - }, + args: { children: '' }, + render: () => ( + + + I am a tooltip + + ), }; -export const Open: Story = { +export const IsOpen: Story = { args: { ...Default.args, - open: true, }, + render: () => ( + + + I am a tooltip + + ), }; -export const WithArrow: Story = { +export const IsDisabled: Story = { args: { - open: true, - children: ( - <> - ( - - )} - /> - - - - - Nice! - - - - - ), + ...Default.args, }, + render: () => ( + + + I am a tooltip + + ), +}; + +export const PlacementTop: Story = { + args: { + ...Default.args, + }, + render: () => ( + + + I am a tooltip + + ), +}; + +export const PlacementRight: Story = { + args: { + ...Default.args, + }, + render: () => ( + + + I am a tooltip + + ), +}; + +export const PlacementLeft: Story = { + args: { + ...Default.args, + }, + render: () => ( + + + I am a tooltip + + ), +}; + +export const PlacementBottom: Story = { + args: { + ...Default.args, + }, + render: () => ( + + + I am a tooltip + + ), +}; + +export const WithLongText: Story = { + args: { + ...Default.args, + }, + render: () => ( + + + + I am a tooltip with a very long text. I am a tooltip with a very long + text. + + + ), }; diff --git a/packages/canon/src/components/Tooltip/Tooltip.styles.css b/packages/canon/src/components/Tooltip/Tooltip.styles.css index d6850c0d26..b86a93b038 100644 --- a/packages/canon/src/components/Tooltip/Tooltip.styles.css +++ b/packages/canon/src/components/Tooltip/Tooltip.styles.css @@ -14,70 +14,61 @@ * limitations under the License. */ -.canon-TooltipPopup { - box-sizing: border-box; - font-size: 0.875rem; - line-height: 1.25rem; - display: flex; - flex-direction: column; - padding: 0.25rem 0.5rem; - border-radius: 0.375rem; - background-color: canvas; - transform-origin: var(--transform-origin); - transition: transform 150ms, opacity 150ms; - background-color: var(--canon-bg-surface-1); - color: var(--canon-fg-primary); - outline: 1px solid var(--canon-border); - box-shadow: 0 10px 15px -3px var(--canon-border), - 0 4px 6px -4px var(--canon-border); +.bui-Tooltip { + box-shadow: 0 8px 20px rgba(0 0 0 / 0.1); + border-radius: 4px; + background: var(--bui-bg-surface-2); + forced-color-adjust: none; + outline: none; + padding: var(--bui-space-2) var(--bui-space-3); + max-width: 240px; + /* fixes FF gap */ + transform: translate3d(0, 0, 0); + transition: transform 200ms, opacity 200ms; + font-size: var(--bui-font-size-3); + font-family: var(--bui-font-regular); + color: var(--bui-fg-primary); - &[data-starting-style], - &[data-ending-style] { + &[data-entering], + &[data-exiting] { + transform: var(--origin); opacity: 0; - transform: scale(0.9); } - &[data-instant] { - transition-duration: 0ms; + &[data-placement='top'] { + margin-bottom: var(--bui-space-2); + --origin: translateY(4px); + } + + &[data-placement='bottom'] { + margin-top: var(--bui-space-2); + --origin: translateY(-4px); + + & .bui-TooltipArrow svg { + transform: rotate(180deg); + } + } + + &[data-placement='right'] { + margin-left: var(--bui-space-2); + --origin: translateX(-4px); + + & .bui-TooltipArrow svg { + transform: rotate(90deg); + } + } + + &[data-placement='left'] { + margin-right: var(--bui-space-2); + --origin: translateX(4px); + + & .bui-TooltipArrow svg { + transform: rotate(-90deg); + } } } -.canon-TooltipArrow { - display: flex; - - &[data-side='top'] { - bottom: -8px; - rotate: 180deg; - } - - &[data-side='bottom'] { - top: -8px; - rotate: 0deg; - } - - &[data-side='left'] { - right: -13px; - rotate: 90deg; - } - - &[data-side='right'] { - left: -13px; - rotate: -90deg; - } -} - -.canon-TooltipArrow-fill { - fill: var(--canon-bg-surface-1); -} - -.canon-TooltipArrow-outer-stroke { - @media (prefers-color-scheme: light) { - fill: var(--canon-border); - } -} - -.canon-TooltipArrow-inner-stroke { - @media (prefers-color-scheme: dark) { - /* fill: var(--canon-border); */ - } +.bui-TooltipArrow svg { + display: block; + fill: var(--bui-bg-surface-1); } diff --git a/packages/canon/src/components/Tooltip/Tooltip.tsx b/packages/canon/src/components/Tooltip/Tooltip.tsx index 7d78d2d3ba..f22e068cab 100644 --- a/packages/canon/src/components/Tooltip/Tooltip.tsx +++ b/packages/canon/src/components/Tooltip/Tooltip.tsx @@ -15,79 +15,43 @@ */ import { forwardRef } from 'react'; -import { Tooltip as TooltipPrimitive } from '@base-ui-components/react/tooltip'; +import { + OverlayArrow, + Tooltip as AriaTooltip, + TooltipTrigger as AriaTooltipTrigger, + TooltipTriggerComponentProps, +} from 'react-aria-components'; import clsx from 'clsx'; - -const TooltipTrigger = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TooltipTrigger.displayName = TooltipPrimitive.Trigger.displayName; - -const TooltipPositioner = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TooltipPositioner.displayName = TooltipPrimitive.Positioner.displayName; - -const TooltipPopup = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -TooltipPopup.displayName = TooltipPrimitive.Popup.displayName; - -const TooltipArrow = forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - - - - - -)); -TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName; +import { TooltipProps } from './types'; +import { useStyles } from '../../hooks/useStyles'; /** @public */ -export const Tooltip = { - Provider: TooltipPrimitive.Provider, - Root: TooltipPrimitive.Root, - Trigger: TooltipTrigger, - Portal: TooltipPrimitive.Portal, - Positioner: TooltipPositioner, - Popup: TooltipPopup, - Arrow: TooltipArrow, +export const TooltipTrigger = (props: TooltipTriggerComponentProps) => { + const { delay = 600 } = props; + + return ; }; + +/** @public */ +export const Tooltip = forwardRef( + ({ className, children, ...rest }, ref) => { + const { classNames } = useStyles('Tooltip'); + + return ( + + + + + + + {children} + + ); + }, +); + +Tooltip.displayName = Tooltip.displayName; diff --git a/packages/canon/src/components/Tooltip/index.ts b/packages/canon/src/components/Tooltip/index.ts index cf97688854..613631091b 100644 --- a/packages/canon/src/components/Tooltip/index.ts +++ b/packages/canon/src/components/Tooltip/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ -export * from './Tooltip'; +export { TooltipTrigger, Tooltip } from './Tooltip'; +export type { TooltipProps } from './types'; diff --git a/packages/canon/src/components/Tooltip/types.ts b/packages/canon/src/components/Tooltip/types.ts new file mode 100644 index 0000000000..2ef44fbbf4 --- /dev/null +++ b/packages/canon/src/components/Tooltip/types.ts @@ -0,0 +1,22 @@ +/* + * 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 { TooltipProps as AriaTooltipProps } from 'react-aria-components'; + +/** @public */ +export interface TooltipProps extends Omit { + children: React.ReactNode; +} diff --git a/packages/canon/src/css/components.css b/packages/canon/src/css/components.css index 7cee946e3b..c3f3196bb0 100644 --- a/packages/canon/src/css/components.css +++ b/packages/canon/src/css/components.css @@ -17,14 +17,25 @@ @import '../components/Avatar/Avatar.styles.css'; @import '../components/Box/styles.css'; @import '../components/Button/styles.css'; +@import '../components/ButtonIcon/styles.css'; +@import '../components/Card/Card.styles.css'; +@import '../components/Checkbox/styles.css'; @import '../components/Collapsible/Collapsible.styles.css'; +@import '../components/Container/styles.css'; @import '../components/DataTable/Root/DataTableRoot.styles.css'; @import '../components/DataTable/Pagination/DataTablePagination.styles.css'; +@import '../components/FieldError/FieldError.styles.css'; +@import '../components/FieldLabel/FieldLabel.styles.css'; @import '../components/Flex/styles.css'; @import '../components/Grid/styles.css'; -@import '../components/Container/styles.css'; +@import '../components/Header/Header.styles.css'; +@import '../components/HeaderPage/HeaderPage.styles.css'; +@import '../components/Heading/styles.css'; @import '../components/Icon/styles.css'; -@import '../components/Checkbox/styles.css'; +@import '../components/Link/styles.css'; +@import '../components/Menu/Menu.styles.css'; +@import '../components/Popover/Popover.styles.css'; +@import '../components/RadioGroup/RadioGroup.styles.css'; @import '../components/Table/styles.css'; @import '../components/Table/TableCell/TableCell.styles.css'; @import '../components/Table/TableCellText/TableCellText.styles.css'; @@ -32,11 +43,10 @@ @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'; @import '../components/TextField/TextField.styles.css'; -@import '../components/Menu/Menu.styles.css'; -@import '../components/Link/styles.css'; +@import '../components/SearchField/SearchField.styles.css'; +@import '../components/Skeleton/Skeleton.styles.css'; @import '../components/Tooltip/Tooltip.styles.css'; @import '../components/ScrollArea/ScrollArea.styles.css'; @import '../components/Select/Select.styles.css'; +@import '../components/Switch/Switch.styles.css'; diff --git a/packages/canon/src/css/core.css b/packages/canon/src/css/core.css index 260140af70..e41629073f 100644 --- a/packages/canon/src/css/core.css +++ b/packages/canon/src/css/core.css @@ -24,169 +24,177 @@ text-decoration: none; } +@keyframes pulse { + 50% { + opacity: 0.5; + } +} + /* Light theme tokens */ :root { /* Font families */ - --canon-font-regular: system-ui; - --canon-font-monospace: ui-monospace, 'Menlo', 'Monaco', 'Consolas', + --bui-font-regular: system-ui; + --bui-font-monospace: ui-monospace, 'Menlo', 'Monaco', 'Consolas', 'Liberation Mono', 'Courier New', monospace; /* Font weights */ - --canon-font-weight-regular: 400; - --canon-font-weight-bold: 600; + --bui-font-weight-regular: 400; + --bui-font-weight-bold: 600; /* Font sizes */ - --canon-font-size-1: 0.625rem; /* 10px */ - --canon-font-size-2: 0.75rem; /* 12px */ - --canon-font-size-3: 0.875rem; /* 14px */ - --canon-font-size-4: 1rem; /* 16px */ - --canon-font-size-5: 1.25rem; /* 20px */ - --canon-font-size-6: 1.5rem; /* 24px */ - --canon-font-size-7: 2rem; /* 32px */ - --canon-font-size-8: 3rem; /* 48px */ - --canon-font-size-9: 4rem; /* 64px */ - --canon-font-size-10: 5.75rem; /* 92px */ + --bui-font-size-1: 0.625rem; /* 10px */ + --bui-font-size-2: 0.75rem; /* 12px */ + --bui-font-size-3: 0.875rem; /* 14px */ + --bui-font-size-4: 1rem; /* 16px */ + --bui-font-size-5: 1.25rem; /* 20px */ + --bui-font-size-6: 1.5rem; /* 24px */ + --bui-font-size-7: 2rem; /* 32px */ + --bui-font-size-8: 3rem; /* 48px */ + --bui-font-size-9: 4rem; /* 64px */ + --bui-font-size-10: 5.75rem; /* 92px */ /* Spacing */ - --canon-space: 0.25rem; /* This is the spacing multiplier */ - --canon-space-0_5: calc(var(--canon-space) * 0.5); /* 2px */ - --canon-space-1: var(--canon-space); /* 4px */ - --canon-space-1_5: calc(var(--canon-space) * 1.5); /* 6px */ - --canon-space-2: calc(var(--canon-space) * 2); /* 8px */ - --canon-space-3: calc(var(--canon-space) * 3); /* 12px */ - --canon-space-4: calc(var(--canon-space) * 4); /* 16px */ - --canon-space-5: calc(var(--canon-space) * 5); /* 20px */ - --canon-space-6: calc(var(--canon-space) * 6); /* 24px */ - --canon-space-7: calc(var(--canon-space) * 7); /* 28px */ - --canon-space-8: calc(var(--canon-space) * 8); /* 32px */ - --canon-space-9: calc(var(--canon-space) * 9); /* 36px */ - --canon-space-10: calc(var(--canon-space) * 10); /* 40px */ - --canon-space-11: calc(var(--canon-space) * 11); /* 44px */ - --canon-space-12: calc(var(--canon-space) * 12); /* 48px */ - --canon-space-13: calc(var(--canon-space) * 13); /* 52px */ - --canon-space-14: calc(var(--canon-space) * 14); /* 56px */ + --bui-space: 0.25rem; /* This is the spacing multiplier */ + --bui-space-0_5: calc(var(--bui-space) * 0.5); /* 2px */ + --bui-space-1: var(--bui-space); /* 4px */ + --bui-space-1_5: calc(var(--bui-space) * 1.5); /* 6px */ + --bui-space-2: calc(var(--bui-space) * 2); /* 8px */ + --bui-space-3: calc(var(--bui-space) * 3); /* 12px */ + --bui-space-4: calc(var(--bui-space) * 4); /* 16px */ + --bui-space-5: calc(var(--bui-space) * 5); /* 20px */ + --bui-space-6: calc(var(--bui-space) * 6); /* 24px */ + --bui-space-7: calc(var(--bui-space) * 7); /* 28px */ + --bui-space-8: calc(var(--bui-space) * 8); /* 32px */ + --bui-space-9: calc(var(--bui-space) * 9); /* 36px */ + --bui-space-10: calc(var(--bui-space) * 10); /* 40px */ + --bui-space-11: calc(var(--bui-space) * 11); /* 44px */ + --bui-space-12: calc(var(--bui-space) * 12); /* 48px */ + --bui-space-13: calc(var(--bui-space) * 13); /* 52px */ + --bui-space-14: calc(var(--bui-space) * 14); /* 56px */ /* Radius */ - --canon-radius-1: calc(0.125rem); - --canon-radius-2: calc(0.25rem); - --canon-radius-3: calc(0.5rem); - --canon-radius-4: calc(0.75rem); - --canon-radius-5: calc(1rem); - --canon-radius-6: calc(1.25rem); - --canon-radius-full: 9999px; + --bui-radius-1: calc(0.125rem); + --bui-radius-2: calc(0.25rem); + --bui-radius-3: calc(0.5rem); + --bui-radius-4: calc(0.75rem); + --bui-radius-5: calc(1rem); + --bui-radius-6: calc(1.25rem); + --bui-radius-full: 9999px; /* Base Colors */ - --canon-black: #000000; - --canon-white: #ffffff; + --bui-black: #000000; + --bui-white: #ffffff; /* Gray scale */ - --canon-gray-1: #f8f8f8; - --canon-gray-2: #ececec; - --canon-gray-3: #d9d9d9; - --canon-gray-4: #c1c1c1; - --canon-gray-5: #9e9e9e; - --canon-gray-6: #8c8c8c; - --canon-gray-7: #757575; - --canon-gray-8: #595959; + --bui-gray-1: #f8f8f8; + --bui-gray-2: #ececec; + --bui-gray-3: #d9d9d9; + --bui-gray-4: #c1c1c1; + --bui-gray-5: #9e9e9e; + --bui-gray-6: #8c8c8c; + --bui-gray-7: #757575; + --bui-gray-8: #595959; /* Background Colors */ - --canon-bg: var(--canon-gray-1); - --canon-bg-surface-1: var(--canon-white); - --canon-bg-surface-2: var(--canon-gray-2); - --canon-bg-solid: #1f5493; - --canon-bg-solid-hover: #163a66; - --canon-bg-solid-pressed: #0f2b4e; - --canon-bg-solid-disabled: #ebebeb; - --canon-bg-tint: transparent; - --canon-bg-tint-hover: rgba(31, 84, 147, 0.4); - --canon-bg-tint-pressed: rgba(31, 84, 147, 0.6); - --canon-bg-tint-disabled: #ebebeb; - --canon-bg-danger: #feebe7; - --canon-bg-warning: #fff2b2; - --canon-bg-success: #e6f6eb; + --bui-bg: var(--bui-gray-1); + --bui-bg-surface-1: var(--bui-white); + --bui-bg-surface-2: var(--bui-gray-2); + --bui-bg-solid: #1f5493; + --bui-bg-solid-hover: #163a66; + --bui-bg-solid-pressed: #0f2b4e; + --bui-bg-solid-disabled: #ebebeb; + --bui-bg-tint: transparent; + --bui-bg-tint-hover: rgba(31, 84, 147, 0.4); + --bui-bg-tint-pressed: rgba(31, 84, 147, 0.6); + --bui-bg-tint-disabled: #ebebeb; + --bui-bg-danger: #feebe7; + --bui-bg-warning: #fff2b2; + --bui-bg-success: #e6f6eb; /* Foreground Colors */ - --canon-fg-primary: var(--canon-black); - --canon-fg-secondary: var(--canon-gray-7); - --canon-fg-link: #1f5493; - --canon-fg-link-hover: #1f2d5c; - --canon-fg-disabled: #9e9e9e; - --canon-fg-solid: var(--canon-white); - --canon-fg-solid-disabled: #9c9c9c; - --canon-fg-tint: #1f5493; - --canon-fg-tint-disabled: var(--canon-gray-5); - --canon-fg-danger: #e22b2b; - --canon-fg-warning: #e36d05; - --canon-fg-success: #1db954; + --bui-fg-primary: var(--bui-black); + --bui-fg-secondary: var(--bui-gray-7); + --bui-fg-link: #1f5493; + --bui-fg-link-hover: #1f2d5c; + --bui-fg-disabled: #9e9e9e; + --bui-fg-solid: var(--bui-white); + --bui-fg-solid-disabled: #9c9c9c; + --bui-fg-tint: #1f5493; + --bui-fg-tint-disabled: var(--bui-gray-5); + --bui-fg-danger: #e22b2b; + --bui-fg-warning: #e36d05; + --bui-fg-success: #1db954; /* Border Colors */ - --canon-border: rgba(0, 0, 0, 0.1); - --canon-border-hover: rgba(0, 0, 0, 0.2); - --canon-border-pressed: rgba(0, 0, 0, 0.4); - --canon-border-disabled: rgba(0, 0, 0, 0.1); - --canon-border-danger: #f87a7a; - --canon-border-warning: #e36d05; - --canon-border-success: #53db83; + --bui-border: rgba(0, 0, 0, 0.1); + --bui-border-hover: rgba(0, 0, 0, 0.2); + --bui-border-pressed: rgba(0, 0, 0, 0.4); + --bui-border-disabled: rgba(0, 0, 0, 0.1); + --bui-border-danger: #f87a7a; + --bui-border-warning: #e36d05; + --bui-border-success: #53db83; /* Special Colors */ - --canon-ring: #1f5493; - --canon-scrollbar: #a0a0a03b; - --canon-scrollbar-thumb: #a0a0a0; + --bui-ring: #1f5493; + --bui-scrollbar: #a0a0a03b; + --bui-scrollbar-thumb: #a0a0a0; + + --bui-animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } /* Dark theme tokens */ [data-theme='dark'] { /* Gray scale */ - --canon-gray-1: #191919; - --canon-gray-2: #242424; - --canon-gray-3: #373737; - --canon-gray-4: #464646; - --canon-gray-5: #575757; - --canon-gray-6: #7b7b7b; - --canon-gray-7: #9e9e9e; - --canon-gray-8: #b4b4b4; + --bui-gray-1: #191919; + --bui-gray-2: #242424; + --bui-gray-3: #373737; + --bui-gray-4: #464646; + --bui-gray-5: #575757; + --bui-gray-6: #7b7b7b; + --bui-gray-7: #9e9e9e; + --bui-gray-8: #b4b4b4; /* Background Colors */ - --canon-bg: var(--canon-black); - --canon-bg-surface-1: var(--canon-gray-1); - --canon-bg-surface-2: var(--canon-gray-2); - --canon-bg-solid: #9cc9ff; - --canon-bg-solid-hover: #83b9fd; - --canon-bg-solid-pressed: #83b9fd; - --canon-bg-solid-disabled: #222222; - --canon-bg-tint: transparent; - --canon-bg-tint-hover: rgba(156, 201, 255, 0.12); - --canon-bg-tint-pressed: rgba(156, 201, 255, 0.16); - --canon-bg-tint-disabled: transparent; - --canon-bg-danger: #3b1219; - --canon-bg-warning: #302008; - --canon-bg-success: #132d21; + --bui-bg: var(--bui-black); + --bui-bg-surface-1: var(--bui-gray-1); + --bui-bg-surface-2: var(--bui-gray-2); + --bui-bg-solid: #9cc9ff; + --bui-bg-solid-hover: #83b9fd; + --bui-bg-solid-pressed: #83b9fd; + --bui-bg-solid-disabled: #222222; + --bui-bg-tint: transparent; + --bui-bg-tint-hover: rgba(156, 201, 255, 0.12); + --bui-bg-tint-pressed: rgba(156, 201, 255, 0.16); + --bui-bg-tint-disabled: transparent; + --bui-bg-danger: #3b1219; + --bui-bg-warning: #302008; + --bui-bg-success: #132d21; /* Foreground Colors */ - --canon-fg-primary: var(--canon-white); - --canon-fg-secondary: var(--canon-gray-7); - --canon-fg-link: #9cc9ff; - --canon-fg-link-hover: #7eb5f7; - --canon-fg-disabled: var(--canon-gray-7); - --canon-fg-solid: #101821; - --canon-fg-solid-disabled: var(--canon-gray-5); - --canon-fg-tint: #9cc9ff; - --canon-fg-tint-disabled: var(--canon-gray-5); - --canon-fg-danger: #e22b2b; - --canon-fg-warning: #e36d05; - --canon-fg-success: #1db954; + --bui-fg-primary: var(--bui-white); + --bui-fg-secondary: var(--bui-gray-7); + --bui-fg-link: #9cc9ff; + --bui-fg-link-hover: #7eb5f7; + --bui-fg-disabled: var(--bui-gray-7); + --bui-fg-solid: #101821; + --bui-fg-solid-disabled: var(--bui-gray-5); + --bui-fg-tint: #9cc9ff; + --bui-fg-tint-disabled: var(--bui-gray-5); + --bui-fg-danger: #e22b2b; + --bui-fg-warning: #e36d05; + --bui-fg-success: #1db954; /* Border Colors */ - --canon-border: rgba(255, 255, 255, 0.2); - --canon-border-hover: rgba(255, 255, 255, 0.4); - --canon-border-pressed: rgba(255, 255, 255, 0.5); - --canon-border-disabled: rgba(255, 255, 255, 0.2); - --canon-border-danger: #f87a7a; - --canon-border-warning: #e36d05; - --canon-border-success: #53db83; + --bui-border: rgba(255, 255, 255, 0.2); + --bui-border-hover: rgba(255, 255, 255, 0.4); + --bui-border-pressed: rgba(255, 255, 255, 0.5); + --bui-border-disabled: rgba(255, 255, 255, 0.2); + --bui-border-danger: #f87a7a; + --bui-border-warning: #e36d05; + --bui-border-success: #53db83; /* Special Colors */ - --canon-ring: #1f5493; - --canon-scrollbar: #3636363a; - --canon-scrollbar-thumb: #575757; + --bui-ring: #1f5493; + --bui-scrollbar: #3636363a; + --bui-scrollbar-thumb: #575757; } diff --git a/packages/canon/src/css/stories.module.css b/packages/canon/src/css/stories.module.css new file mode 100644 index 0000000000..10ed37bb7a --- /dev/null +++ b/packages/canon/src/css/stories.module.css @@ -0,0 +1,16 @@ +.fakeBox { + background: #eaf2fd; + border-radius: 4px; + box-shadow: 0 0 0 1px #2563eb; + height: 48px; + transition-property: background-color, box-shadow, background-image; + transition-duration: 0.2s; + transition-timing-function: ease-in-out; + background-image: url('data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%232563eb%22%20fill-opacity%3D%220.3%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E'); +} + +[data-theme='dark'] .fakeBox { + background: #1a1a1a; + box-shadow: 0 0 0 1px #4765ff; + background-image: url('data:image/svg+xml,%3Csvg%20width%3D%226%22%20height%3D%226%22%20viewBox%3D%220%200%206%206%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cg%20fill%3D%22%234765FF%22%20fill-opacity%3D%221%22%20fill-rule%3D%22evenodd%22%3E%3Cpath%20d%3D%22M5%200h1L0%206V5zM6%205v1H5z%22/%3E%3C/g%3E%3C/svg%3E'); +} diff --git a/packages/canon/src/css/storybook.css b/packages/canon/src/css/storybook.css new file mode 100644 index 0000000000..ac843a0d75 --- /dev/null +++ b/packages/canon/src/css/storybook.css @@ -0,0 +1,32 @@ +:root { + --sb-panel-radius: 0; + --sb-panel-top: 0; + --sb-panel-bottom: 0; + --sb-panel-left: 0; + --sb-panel-right: 0; + --sb-sidebar-border: none; + --sb-sidebar-border-right: 1px solid var(--bui-border); + --sb-options-border: none; + --sb-options-border-left: 1px solid var(--bui-border); + --sb-content-padding-inline: 250px; +} + +[data-theme-name='spotify'] { + --sb-panel-radius: var(--bui-radius-3); + --sb-panel-top: 8px; + --sb-panel-bottom: 8px; + --sb-panel-left: 8px; + --sb-panel-right: 8px; + --sb-sidebar-border: none; + --sb-sidebar-border-right: none; + --sb-options-border: none; + --sb-options-border-left: none; + --sb-content-padding-inline: 258px; +} + +[data-theme-name='spotify'][data-theme='light'] { + --sb-sidebar-border: 1px solid var(--bui-border); + --sb-sidebar-border-right: 1px solid var(--bui-border); + --sb-options-border: 1px solid var(--bui-border); + --sb-options-border-left: 1px solid var(--bui-border); +} diff --git a/packages/canon/src/css/utilities/display.css b/packages/canon/src/css/utilities/display.css index 8c353730ca..179d80f860 100644 --- a/packages/canon/src/css/utilities/display.css +++ b/packages/canon/src/css/utilities/display.css @@ -1,110 +1,110 @@ -.cu-display-none { +.bui-display-none { display: none; } -.cu-display-inline { +.bui-display-inline { display: inline; } -.cu-display-inline-block { +.bui-display-inline-block { display: inline-block; } -.cu-display-block { +.bui-display-block { display: block; } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-display-none { + .xs\:bui-display-none { display: none; } - .xs\:cu-display-inline { + .xs\:bui-display-inline { display: inline; } - .xs\:cu-display-inline-block { + .xs\:bui-display-inline-block { display: inline-block; } - .xs\:cu-display-block { + .xs\:bui-display-block { display: block; } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-display-none { + .sm\:bui-display-none { display: none; } - .sm\:cu-display-inline { + .sm\:bui-display-inline { display: inline; } - .sm\:cu-display-inline-block { + .sm\:bui-display-inline-block { display: inline-block; } - .sm\:cu-display-block { + .sm\:bui-display-block { display: block; } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-display-none { + .md\:bui-display-none { display: none; } - .md\:cu-display-inline { + .md\:bui-display-inline { display: inline; } - .md\:cu-display-inline-block { + .md\:bui-display-inline-block { display: inline-block; } - .md\:cu-display-block { + .md\:bui-display-block { display: block; } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-display-none { + .lg\:bui-display-none { display: none; } - .lg\:cu-display-inline { + .lg\:bui-display-inline { display: inline; } - .lg\:cu-display-inline-block { + .lg\:bui-display-inline-block { display: inline-block; } - .lg\:cu-display-block { + .lg\:bui-display-block { display: block; } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-display-none { + .xl\:bui-display-none { display: none; } - .xl\:cu-display-inline { + .xl\:bui-display-inline { display: inline; } - .xl\:cu-display-inline-block { + .xl\:bui-display-inline-block { display: inline-block; } - .xl\:cu-display-block { + .xl\:bui-display-block { display: block; } } diff --git a/packages/canon/src/css/utilities/flex.css b/packages/canon/src/css/utilities/flex.css index 5311aac3e8..d68c63556f 100644 --- a/packages/canon/src/css/utilities/flex.css +++ b/packages/canon/src/css/utilities/flex.css @@ -1,302 +1,302 @@ -.cu-align-start { +.bui-align-start { align-items: start; } -.cu-align-center { +.bui-align-center { align-items: center; } -.cu-align-end { +.bui-align-end { align-items: end; } -.cu-align-stretch { +.bui-align-stretch { align-items: stretch; } -.cu-jc-start { +.bui-jc-start { justify-content: start; } -.cu-jc-center { +.bui-jc-center { justify-content: center; } -.cu-jc-end { +.bui-jc-end { justify-content: end; } -.cu-jc-between { +.bui-jc-between { justify-content: space-between; } -.cu-fd-row { +.bui-fd-row { flex-direction: row; } -.cu-fd-column { +.bui-fd-column { flex-direction: column; } -.cu-fd-row-reverse { +.bui-fd-row-reverse { flex-direction: row-reverse; } -.cu-fd-column-reverse { +.bui-fd-column-reverse { flex-direction: column-reverse; } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-align-start { + .xs\:bui-align-start { align-items: start; } - .xs\:cu-align-center { + .xs\:bui-align-center { align-items: center; } - .xs\:cu-align-end { + .xs\:bui-align-end { align-items: end; } - .xs\:cu-align-stretch { + .xs\:bui-align-stretch { align-items: stretch; } - .xs\:cu-jc-start { + .xs\:bui-jc-start { justify-content: start; } - .xs\:cu-jc-center { + .xs\:bui-jc-center { justify-content: center; } - .xs\:cu-jc-end { + .xs\:bui-jc-end { justify-content: end; } - .xs\:cu-jc-between { + .xs\:bui-jc-between { justify-content: space-between; } - .xs\:cu-fd-row { + .xs\:bui-fd-row { flex-direction: row; } - .xs\:cu-fd-column { + .xs\:bui-fd-column { flex-direction: column; } - .xs\:cu-fd-row-reverse { + .xs\:bui-fd-row-reverse { flex-direction: row-reverse; } - .xs\:cu-fd-column-reverse { + .xs\:bui-fd-column-reverse { flex-direction: column-reverse; } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-align-start { + .sm\:bui-align-start { align-items: start; } - .sm\:cu-align-center { + .sm\:bui-align-center { align-items: center; } - .sm\:cu-align-end { + .sm\:bui-align-end { align-items: end; } - .sm\:cu-align-stretch { + .sm\:bui-align-stretch { align-items: stretch; } - .sm\:cu-jc-start { + .sm\:bui-jc-start { justify-content: start; } - .sm\:cu-jc-center { + .sm\:bui-jc-center { justify-content: center; } - .sm\:cu-jc-end { + .sm\:bui-jc-end { justify-content: end; } - .sm\:cu-jc-between { + .sm\:bui-jc-between { justify-content: space-between; } - .sm\:cu-fd-row { + .sm\:bui-fd-row { flex-direction: row; } - .sm\:cu-fd-column { + .sm\:bui-fd-column { flex-direction: column; } - .sm\:cu-fd-row-reverse { + .sm\:bui-fd-row-reverse { flex-direction: row-reverse; } - .sm\:cu-fd-column-reverse { + .sm\:bui-fd-column-reverse { flex-direction: column-reverse; } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-align-start { + .md\:bui-align-start { align-items: start; } - .md\:cu-align-center { + .md\:bui-align-center { align-items: center; } - .md\:cu-align-end { + .md\:bui-align-end { align-items: end; } - .md\:cu-align-stretch { + .md\:bui-align-stretch { align-items: stretch; } - .md\:cu-jc-start { + .md\:bui-jc-start { justify-content: start; } - .md\:cu-jc-center { + .md\:bui-jc-center { justify-content: center; } - .md\:cu-jc-end { + .md\:bui-jc-end { justify-content: end; } - .md\:cu-jc-between { + .md\:bui-jc-between { justify-content: space-between; } - .md\:cu-fd-row { + .md\:bui-fd-row { flex-direction: row; } - .md\:cu-fd-column { + .md\:bui-fd-column { flex-direction: column; } - .md\:cu-fd-row-reverse { + .md\:bui-fd-row-reverse { flex-direction: row-reverse; } - .md\:cu-fd-column-reverse { + .md\:bui-fd-column-reverse { flex-direction: column-reverse; } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-align-start { + .lg\:bui-align-start { align-items: start; } - .lg\:cu-align-center { + .lg\:bui-align-center { align-items: center; } - .lg\:cu-align-end { + .lg\:bui-align-end { align-items: end; } - .lg\:cu-align-stretch { + .lg\:bui-align-stretch { align-items: stretch; } - .lg\:cu-jc-start { + .lg\:bui-jc-start { justify-content: start; } - .lg\:cu-jc-center { + .lg\:bui-jc-center { justify-content: center; } - .lg\:cu-jc-end { + .lg\:bui-jc-end { justify-content: end; } - .lg\:cu-jc-between { + .lg\:bui-jc-between { justify-content: space-between; } - .lg\:cu-fd-row { + .lg\:bui-fd-row { flex-direction: row; } - .lg\:cu-fd-column { + .lg\:bui-fd-column { flex-direction: column; } - .lg\:cu-fd-row-reverse { + .lg\:bui-fd-row-reverse { flex-direction: row-reverse; } - .lg\:cu-fd-column-reverse { + .lg\:bui-fd-column-reverse { flex-direction: column-reverse; } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-align-start { + .xl\:bui-align-start { align-items: start; } - .xl\:cu-align-center { + .xl\:bui-align-center { align-items: center; } - .xl\:cu-align-end { + .xl\:bui-align-end { align-items: end; } - .xl\:cu-align-stretch { + .xl\:bui-align-stretch { align-items: stretch; } - .xl\:cu-jc-start { + .xl\:bui-jc-start { justify-content: start; } - .xl\:cu-jc-center { + .xl\:bui-jc-center { justify-content: center; } - .xl\:cu-jc-end { + .xl\:bui-jc-end { justify-content: end; } - .xl\:cu-jc-between { + .xl\:bui-jc-between { justify-content: space-between; } - .xl\:cu-fd-row { + .xl\:bui-fd-row { flex-direction: row; } - .xl\:cu-fd-column { + .xl\:bui-fd-column { flex-direction: column; } - .xl\:cu-fd-row-reverse { + .xl\:bui-fd-row-reverse { flex-direction: row-reverse; } - .xl\:cu-fd-column-reverse { + .xl\:bui-fd-column-reverse { flex-direction: column-reverse; } } diff --git a/packages/canon/src/css/utilities/gap.css b/packages/canon/src/css/utilities/gap.css index c86c91b958..ec4168fa6b 100644 --- a/packages/canon/src/css/utilities/gap.css +++ b/packages/canon/src/css/utilities/gap.css @@ -1,422 +1,422 @@ -.cu-gap { +.bui-gap { gap: var(--gap); } -.cu-gap-0\.5 { - gap: var(--canon-space-0_5); +.bui-gap-0\.5 { + gap: var(--bui-space-0_5); } -.cu-gap-1 { - gap: var(--canon-space-1); +.bui-gap-1 { + gap: var(--bui-space-1); } -.cu-gap-1\.5 { - gap: var(--canon-space-1_5); +.bui-gap-1\.5 { + gap: var(--bui-space-1_5); } -.cu-gap-2 { - gap: var(--canon-space-2); +.bui-gap-2 { + gap: var(--bui-space-2); } -.cu-gap-3 { - gap: var(--canon-space-3); +.bui-gap-3 { + gap: var(--bui-space-3); } -.cu-gap-4 { - gap: var(--canon-space-4); +.bui-gap-4 { + gap: var(--bui-space-4); } -.cu-gap-5 { - gap: var(--canon-space-5); +.bui-gap-5 { + gap: var(--bui-space-5); } -.cu-gap-6 { - gap: var(--canon-space-6); +.bui-gap-6 { + gap: var(--bui-space-6); } -.cu-gap-7 { - gap: var(--canon-space-7); +.bui-gap-7 { + gap: var(--bui-space-7); } -.cu-gap-8 { - gap: var(--canon-space-8); +.bui-gap-8 { + gap: var(--bui-space-8); } -.cu-gap-9 { - gap: var(--canon-space-9); +.bui-gap-9 { + gap: var(--bui-space-9); } -.cu-gap-10 { - gap: var(--canon-space-10); +.bui-gap-10 { + gap: var(--bui-space-10); } -.cu-gap-11 { - gap: var(--canon-space-11); +.bui-gap-11 { + gap: var(--bui-space-11); } -.cu-gap-12 { - gap: var(--canon-space-12); +.bui-gap-12 { + gap: var(--bui-space-12); } -.cu-gap-13 { - gap: var(--canon-space-13); +.bui-gap-13 { + gap: var(--bui-space-13); } -.cu-gap-14 { - gap: var(--canon-space-14); +.bui-gap-14 { + gap: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-gap { + .xs\:bui-gap { gap: var(--gap-xs); } - .xs\:cu-gap-0\.5 { - gap: var(--canon-space-0_5); + .xs\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); } - .xs\:cu-gap-1 { - gap: var(--canon-space-1); + .xs\:bui-gap-1 { + gap: var(--bui-space-1); } - .xs\:cu-gap-1\.5 { - gap: var(--canon-space-1_5); + .xs\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); } - .xs\:cu-gap-2 { - gap: var(--canon-space-2); + .xs\:bui-gap-2 { + gap: var(--bui-space-2); } - .xs\:cu-gap-3 { - gap: var(--canon-space-3); + .xs\:bui-gap-3 { + gap: var(--bui-space-3); } - .xs\:cu-gap-4 { - gap: var(--canon-space-4); + .xs\:bui-gap-4 { + gap: var(--bui-space-4); } - .xs\:cu-gap-5 { - gap: var(--canon-space-5); + .xs\:bui-gap-5 { + gap: var(--bui-space-5); } - .xs\:cu-gap-6 { - gap: var(--canon-space-6); + .xs\:bui-gap-6 { + gap: var(--bui-space-6); } - .xs\:cu-gap-7 { - gap: var(--canon-space-7); + .xs\:bui-gap-7 { + gap: var(--bui-space-7); } - .xs\:cu-gap-8 { - gap: var(--canon-space-8); + .xs\:bui-gap-8 { + gap: var(--bui-space-8); } - .xs\:cu-gap-9 { - gap: var(--canon-space-9); + .xs\:bui-gap-9 { + gap: var(--bui-space-9); } - .xs\:cu-gap-10 { - gap: var(--canon-space-10); + .xs\:bui-gap-10 { + gap: var(--bui-space-10); } - .xs\:cu-gap-11 { - gap: var(--canon-space-11); + .xs\:bui-gap-11 { + gap: var(--bui-space-11); } - .xs\:cu-gap-12 { - gap: var(--canon-space-12); + .xs\:bui-gap-12 { + gap: var(--bui-space-12); } - .xs\:cu-gap-13 { - gap: var(--canon-space-13); + .xs\:bui-gap-13 { + gap: var(--bui-space-13); } - .xs\:cu-gap-14 { - gap: var(--canon-space-14); + .xs\:bui-gap-14 { + gap: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-gap { + .sm\:bui-gap { gap: var(--gap-sm); } - .sm\:cu-gap-0\.5 { - gap: var(--canon-space-0_5); + .sm\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); } - .sm\:cu-gap-1 { - gap: var(--canon-space-1); + .sm\:bui-gap-1 { + gap: var(--bui-space-1); } - .sm\:cu-gap-1\.5 { - gap: var(--canon-space-1_5); + .sm\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); } - .sm\:cu-gap-2 { - gap: var(--canon-space-2); + .sm\:bui-gap-2 { + gap: var(--bui-space-2); } - .sm\:cu-gap-3 { - gap: var(--canon-space-3); + .sm\:bui-gap-3 { + gap: var(--bui-space-3); } - .sm\:cu-gap-4 { - gap: var(--canon-space-4); + .sm\:bui-gap-4 { + gap: var(--bui-space-4); } - .sm\:cu-gap-5 { - gap: var(--canon-space-5); + .sm\:bui-gap-5 { + gap: var(--bui-space-5); } - .sm\:cu-gap-6 { - gap: var(--canon-space-6); + .sm\:bui-gap-6 { + gap: var(--bui-space-6); } - .sm\:cu-gap-7 { - gap: var(--canon-space-7); + .sm\:bui-gap-7 { + gap: var(--bui-space-7); } - .sm\:cu-gap-8 { - gap: var(--canon-space-8); + .sm\:bui-gap-8 { + gap: var(--bui-space-8); } - .sm\:cu-gap-9 { - gap: var(--canon-space-9); + .sm\:bui-gap-9 { + gap: var(--bui-space-9); } - .sm\:cu-gap-10 { - gap: var(--canon-space-10); + .sm\:bui-gap-10 { + gap: var(--bui-space-10); } - .sm\:cu-gap-11 { - gap: var(--canon-space-11); + .sm\:bui-gap-11 { + gap: var(--bui-space-11); } - .sm\:cu-gap-12 { - gap: var(--canon-space-12); + .sm\:bui-gap-12 { + gap: var(--bui-space-12); } - .sm\:cu-gap-13 { - gap: var(--canon-space-13); + .sm\:bui-gap-13 { + gap: var(--bui-space-13); } - .sm\:cu-gap-14 { - gap: var(--canon-space-14); + .sm\:bui-gap-14 { + gap: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-gap { + .md\:bui-gap { gap: var(--gap-md); } - .md\:cu-gap-0\.5 { - gap: var(--canon-space-0_5); + .md\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); } - .md\:cu-gap-1 { - gap: var(--canon-space-1); + .md\:bui-gap-1 { + gap: var(--bui-space-1); } - .md\:cu-gap-1\.5 { - gap: var(--canon-space-1_5); + .md\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); } - .md\:cu-gap-2 { - gap: var(--canon-space-2); + .md\:bui-gap-2 { + gap: var(--bui-space-2); } - .md\:cu-gap-3 { - gap: var(--canon-space-3); + .md\:bui-gap-3 { + gap: var(--bui-space-3); } - .md\:cu-gap-4 { - gap: var(--canon-space-4); + .md\:bui-gap-4 { + gap: var(--bui-space-4); } - .md\:cu-gap-5 { - gap: var(--canon-space-5); + .md\:bui-gap-5 { + gap: var(--bui-space-5); } - .md\:cu-gap-6 { - gap: var(--canon-space-6); + .md\:bui-gap-6 { + gap: var(--bui-space-6); } - .md\:cu-gap-7 { - gap: var(--canon-space-7); + .md\:bui-gap-7 { + gap: var(--bui-space-7); } - .md\:cu-gap-8 { - gap: var(--canon-space-8); + .md\:bui-gap-8 { + gap: var(--bui-space-8); } - .md\:cu-gap-9 { - gap: var(--canon-space-9); + .md\:bui-gap-9 { + gap: var(--bui-space-9); } - .md\:cu-gap-10 { - gap: var(--canon-space-10); + .md\:bui-gap-10 { + gap: var(--bui-space-10); } - .md\:cu-gap-11 { - gap: var(--canon-space-11); + .md\:bui-gap-11 { + gap: var(--bui-space-11); } - .md\:cu-gap-12 { - gap: var(--canon-space-12); + .md\:bui-gap-12 { + gap: var(--bui-space-12); } - .md\:cu-gap-13 { - gap: var(--canon-space-13); + .md\:bui-gap-13 { + gap: var(--bui-space-13); } - .md\:cu-gap-14 { - gap: var(--canon-space-14); + .md\:bui-gap-14 { + gap: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-gap { + .lg\:bui-gap { gap: var(--gap-lg); } - .lg\:cu-gap-0\.5 { - gap: var(--canon-space-0_5); + .lg\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); } - .lg\:cu-gap-1 { - gap: var(--canon-space-1); + .lg\:bui-gap-1 { + gap: var(--bui-space-1); } - .lg\:cu-gap-1\.5 { - gap: var(--canon-space-1_5); + .lg\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); } - .lg\:cu-gap-2 { - gap: var(--canon-space-2); + .lg\:bui-gap-2 { + gap: var(--bui-space-2); } - .lg\:cu-gap-3 { - gap: var(--canon-space-3); + .lg\:bui-gap-3 { + gap: var(--bui-space-3); } - .lg\:cu-gap-4 { - gap: var(--canon-space-4); + .lg\:bui-gap-4 { + gap: var(--bui-space-4); } - .lg\:cu-gap-5 { - gap: var(--canon-space-5); + .lg\:bui-gap-5 { + gap: var(--bui-space-5); } - .lg\:cu-gap-6 { - gap: var(--canon-space-6); + .lg\:bui-gap-6 { + gap: var(--bui-space-6); } - .lg\:cu-gap-7 { - gap: var(--canon-space-7); + .lg\:bui-gap-7 { + gap: var(--bui-space-7); } - .lg\:cu-gap-8 { - gap: var(--canon-space-8); + .lg\:bui-gap-8 { + gap: var(--bui-space-8); } - .lg\:cu-gap-9 { - gap: var(--canon-space-9); + .lg\:bui-gap-9 { + gap: var(--bui-space-9); } - .lg\:cu-gap-10 { - gap: var(--canon-space-10); + .lg\:bui-gap-10 { + gap: var(--bui-space-10); } - .lg\:cu-gap-11 { - gap: var(--canon-space-11); + .lg\:bui-gap-11 { + gap: var(--bui-space-11); } - .lg\:cu-gap-12 { - gap: var(--canon-space-12); + .lg\:bui-gap-12 { + gap: var(--bui-space-12); } - .lg\:cu-gap-13 { - gap: var(--canon-space-13); + .lg\:bui-gap-13 { + gap: var(--bui-space-13); } - .lg\:cu-gap-14 { - gap: var(--canon-space-14); + .lg\:bui-gap-14 { + gap: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-gap { + .xl\:bui-gap { gap: var(--gap-xl); } - .xl\:cu-gap-0\.5 { - gap: var(--canon-space-0_5); + .xl\:bui-gap-0\.5 { + gap: var(--bui-space-0_5); } - .xl\:cu-gap-1 { - gap: var(--canon-space-1); + .xl\:bui-gap-1 { + gap: var(--bui-space-1); } - .xl\:cu-gap-1\.5 { - gap: var(--canon-space-1_5); + .xl\:bui-gap-1\.5 { + gap: var(--bui-space-1_5); } - .xl\:cu-gap-2 { - gap: var(--canon-space-2); + .xl\:bui-gap-2 { + gap: var(--bui-space-2); } - .xl\:cu-gap-3 { - gap: var(--canon-space-3); + .xl\:bui-gap-3 { + gap: var(--bui-space-3); } - .xl\:cu-gap-4 { - gap: var(--canon-space-4); + .xl\:bui-gap-4 { + gap: var(--bui-space-4); } - .xl\:cu-gap-5 { - gap: var(--canon-space-5); + .xl\:bui-gap-5 { + gap: var(--bui-space-5); } - .xl\:cu-gap-6 { - gap: var(--canon-space-6); + .xl\:bui-gap-6 { + gap: var(--bui-space-6); } - .xl\:cu-gap-7 { - gap: var(--canon-space-7); + .xl\:bui-gap-7 { + gap: var(--bui-space-7); } - .xl\:cu-gap-8 { - gap: var(--canon-space-8); + .xl\:bui-gap-8 { + gap: var(--bui-space-8); } - .xl\:cu-gap-9 { - gap: var(--canon-space-9); + .xl\:bui-gap-9 { + gap: var(--bui-space-9); } - .xl\:cu-gap-10 { - gap: var(--canon-space-10); + .xl\:bui-gap-10 { + gap: var(--bui-space-10); } - .xl\:cu-gap-11 { - gap: var(--canon-space-11); + .xl\:bui-gap-11 { + gap: var(--bui-space-11); } - .xl\:cu-gap-12 { - gap: var(--canon-space-12); + .xl\:bui-gap-12 { + gap: var(--bui-space-12); } - .xl\:cu-gap-13 { - gap: var(--canon-space-13); + .xl\:bui-gap-13 { + gap: var(--bui-space-13); } - .xl\:cu-gap-14 { - gap: var(--canon-space-14); + .xl\:bui-gap-14 { + gap: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/grid.css b/packages/canon/src/css/utilities/grid.css index a0afe55533..11de40bd7f 100644 --- a/packages/canon/src/css/utilities/grid.css +++ b/packages/canon/src/css/utilities/grid.css @@ -1,1292 +1,1292 @@ -.cu-columns-1 { +.bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } -.cu-columns-2 { +.bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } -.cu-columns-3 { +.bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } -.cu-columns-4 { +.bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } -.cu-columns-5 { +.bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } -.cu-columns-6 { +.bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } -.cu-columns-7 { +.bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } -.cu-columns-8 { +.bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } -.cu-columns-9 { +.bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } -.cu-columns-10 { +.bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } -.cu-columns-11 { +.bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } -.cu-columns-12 { +.bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } -.cu-columns-auto { +.bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } -.cu-col-span-1 { +.bui-col-span-1 { grid-column: span 1 / span 1; } -.cu-col-span-2 { +.bui-col-span-2 { grid-column: span 2 / span 2; } -.cu-col-span-3 { +.bui-col-span-3 { grid-column: span 3 / span 3; } -.cu-col-span-4 { +.bui-col-span-4 { grid-column: span 4 / span 4; } -.cu-col-span-5 { +.bui-col-span-5 { grid-column: span 5 / span 5; } -.cu-col-span-6 { +.bui-col-span-6 { grid-column: span 6 / span 6; } -.cu-col-span-7 { +.bui-col-span-7 { grid-column: span 7 / span 7; } -.cu-col-span-8 { +.bui-col-span-8 { grid-column: span 8 / span 8; } -.cu-col-span-9 { +.bui-col-span-9 { grid-column: span 9 / span 9; } -.cu-col-span-10 { +.bui-col-span-10 { grid-column: span 10 / span 10; } -.cu-col-span-11 { +.bui-col-span-11 { grid-column: span 11 / span 11; } -.cu-col-span-12 { +.bui-col-span-12 { grid-column: span 12 / span 12; } -.cu-col-span-auto { +.bui-col-span-auto { grid-column: span auto / span auto; } -.cu-col-start-1 { +.bui-col-start-1 { grid-column-start: 1; } -.cu-col-start-2 { +.bui-col-start-2 { grid-column-start: 2; } -.cu-col-start-3 { +.bui-col-start-3 { grid-column-start: 3; } -.cu-col-start-4 { +.bui-col-start-4 { grid-column-start: 4; } -.cu-col-start-5 { +.bui-col-start-5 { grid-column-start: 5; } -.cu-col-start-6 { +.bui-col-start-6 { grid-column-start: 6; } -.cu-col-start-7 { +.bui-col-start-7 { grid-column-start: 7; } -.cu-col-start-8 { +.bui-col-start-8 { grid-column-start: 8; } -.cu-col-start-9 { +.bui-col-start-9 { grid-column-start: 9; } -.cu-col-start-10 { +.bui-col-start-10 { grid-column-start: 10; } -.cu-col-start-11 { +.bui-col-start-11 { grid-column-start: 11; } -.cu-col-start-12 { +.bui-col-start-12 { grid-column-start: 12; } -.cu-col-start-13 { +.bui-col-start-13 { grid-column-start: 13; } -.cu-col-start-auto { +.bui-col-start-auto { grid-column-start: auto; } -.cu-col-end-1 { +.bui-col-end-1 { grid-column-end: 1; } -.cu-col-end-2 { +.bui-col-end-2 { grid-column-end: 2; } -.cu-col-end-3 { +.bui-col-end-3 { grid-column-end: 3; } -.cu-col-end-4 { +.bui-col-end-4 { grid-column-end: 4; } -.cu-col-end-5 { +.bui-col-end-5 { grid-column-end: 5; } -.cu-col-end-6 { +.bui-col-end-6 { grid-column-end: 6; } -.cu-col-end-7 { +.bui-col-end-7 { grid-column-end: 7; } -.cu-col-end-8 { +.bui-col-end-8 { grid-column-end: 8; } -.cu-col-end-9 { +.bui-col-end-9 { grid-column-end: 9; } -.cu-col-end-10 { +.bui-col-end-10 { grid-column-end: 10; } -.cu-col-end-11 { +.bui-col-end-11 { grid-column-end: 11; } -.cu-col-end-12 { +.bui-col-end-12 { grid-column-end: 12; } -.cu-col-end-13 { +.bui-col-end-13 { grid-column-end: 13; } -.cu-col-end-auto { +.bui-col-end-auto { grid-column-end: auto; } -.cu-row-span-1 { +.bui-row-span-1 { grid-row: span 1 / span 1; } -.cu-row-span-2 { +.bui-row-span-2 { grid-row: span 2 / span 2; } -.cu-row-span-3 { +.bui-row-span-3 { grid-row: span 3 / span 3; } -.cu-row-span-4 { +.bui-row-span-4 { grid-row: span 4 / span 4; } -.cu-row-span-5 { +.bui-row-span-5 { grid-row: span 5 / span 5; } -.cu-row-span-6 { +.bui-row-span-6 { grid-row: span 6 / span 6; } -.cu-row-span-7 { +.bui-row-span-7 { grid-row: span 7 / span 7; } -.cu-row-span-8 { +.bui-row-span-8 { grid-row: span 8 / span 8; } -.cu-row-span-9 { +.bui-row-span-9 { grid-row: span 9 / span 9; } -.cu-row-span-10 { +.bui-row-span-10 { grid-row: span 10 / span 10; } -.cu-row-span-11 { +.bui-row-span-11 { grid-row: span 11 / span 11; } -.cu-row-span-12 { +.bui-row-span-12 { grid-row: span 12 / span 12; } -.cu-row-span-auto { +.bui-row-span-auto { grid-row: span auto / span auto; } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-columns-1 { + .xs\:bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .xs\:cu-columns-2 { + .xs\:bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .xs\:cu-columns-3 { + .xs\:bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .xs\:cu-columns-4 { + .xs\:bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .xs\:cu-columns-5 { + .xs\:bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .xs\:cu-columns-6 { + .xs\:bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .xs\:cu-columns-7 { + .xs\:bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } - .xs\:cu-columns-8 { + .xs\:bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } - .xs\:cu-columns-9 { + .xs\:bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } - .xs\:cu-columns-10 { + .xs\:bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } - .xs\:cu-columns-11 { + .xs\:bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } - .xs\:cu-columns-12 { + .xs\:bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - .xs\:cu-columns-auto { + .xs\:bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } - .xs\:cu-col-span-1 { + .xs\:bui-col-span-1 { grid-column: span 1 / span 1; } - .xs\:cu-col-span-2 { + .xs\:bui-col-span-2 { grid-column: span 2 / span 2; } - .xs\:cu-col-span-3 { + .xs\:bui-col-span-3 { grid-column: span 3 / span 3; } - .xs\:cu-col-span-4 { + .xs\:bui-col-span-4 { grid-column: span 4 / span 4; } - .xs\:cu-col-span-5 { + .xs\:bui-col-span-5 { grid-column: span 5 / span 5; } - .xs\:cu-col-span-6 { + .xs\:bui-col-span-6 { grid-column: span 6 / span 6; } - .xs\:cu-col-span-7 { + .xs\:bui-col-span-7 { grid-column: span 7 / span 7; } - .xs\:cu-col-span-8 { + .xs\:bui-col-span-8 { grid-column: span 8 / span 8; } - .xs\:cu-col-span-9 { + .xs\:bui-col-span-9 { grid-column: span 9 / span 9; } - .xs\:cu-col-span-10 { + .xs\:bui-col-span-10 { grid-column: span 10 / span 10; } - .xs\:cu-col-span-11 { + .xs\:bui-col-span-11 { grid-column: span 11 / span 11; } - .xs\:cu-col-span-12 { + .xs\:bui-col-span-12 { grid-column: span 12 / span 12; } - .xs\:cu-col-span-auto { + .xs\:bui-col-span-auto { grid-column: span auto / span auto; } - .xs\:cu-col-start-1 { + .xs\:bui-col-start-1 { grid-column-start: 1; } - .xs\:cu-col-start-2 { + .xs\:bui-col-start-2 { grid-column-start: 2; } - .xs\:cu-col-start-3 { + .xs\:bui-col-start-3 { grid-column-start: 3; } - .xs\:cu-col-start-4 { + .xs\:bui-col-start-4 { grid-column-start: 4; } - .xs\:cu-col-start-5 { + .xs\:bui-col-start-5 { grid-column-start: 5; } - .xs\:cu-col-start-6 { + .xs\:bui-col-start-6 { grid-column-start: 6; } - .xs\:cu-col-start-7 { + .xs\:bui-col-start-7 { grid-column-start: 7; } - .xs\:cu-col-start-8 { + .xs\:bui-col-start-8 { grid-column-start: 8; } - .xs\:cu-col-start-9 { + .xs\:bui-col-start-9 { grid-column-start: 9; } - .xs\:cu-col-start-10 { + .xs\:bui-col-start-10 { grid-column-start: 10; } - .xs\:cu-col-start-11 { + .xs\:bui-col-start-11 { grid-column-start: 11; } - .xs\:cu-col-start-12 { + .xs\:bui-col-start-12 { grid-column-start: 12; } - .xs\:cu-col-start-13 { + .xs\:bui-col-start-13 { grid-column-start: 13; } - .xs\:cu-col-start-auto { + .xs\:bui-col-start-auto { grid-column-start: auto; } - .xs\:cu-col-end-1 { + .xs\:bui-col-end-1 { grid-column-end: 1; } - .xs\:cu-col-end-2 { + .xs\:bui-col-end-2 { grid-column-end: 2; } - .xs\:cu-col-end-3 { + .xs\:bui-col-end-3 { grid-column-end: 3; } - .xs\:cu-col-end-4 { + .xs\:bui-col-end-4 { grid-column-end: 4; } - .xs\:cu-col-end-5 { + .xs\:bui-col-end-5 { grid-column-end: 5; } - .xs\:cu-col-end-6 { + .xs\:bui-col-end-6 { grid-column-end: 6; } - .xs\:cu-col-end-7 { + .xs\:bui-col-end-7 { grid-column-end: 7; } - .xs\:cu-col-end-8 { + .xs\:bui-col-end-8 { grid-column-end: 8; } - .xs\:cu-col-end-9 { + .xs\:bui-col-end-9 { grid-column-end: 9; } - .xs\:cu-col-end-10 { + .xs\:bui-col-end-10 { grid-column-end: 10; } - .xs\:cu-col-end-11 { + .xs\:bui-col-end-11 { grid-column-end: 11; } - .xs\:cu-col-end-12 { + .xs\:bui-col-end-12 { grid-column-end: 12; } - .xs\:cu-col-end-13 { + .xs\:bui-col-end-13 { grid-column-end: 13; } - .xs\:cu-col-end-auto { + .xs\:bui-col-end-auto { grid-column-end: auto; } - .xs\:cu-row-span-1 { + .xs\:bui-row-span-1 { grid-row: span 1 / span 1; } - .xs\:cu-row-span-2 { + .xs\:bui-row-span-2 { grid-row: span 2 / span 2; } - .xs\:cu-row-span-3 { + .xs\:bui-row-span-3 { grid-row: span 3 / span 3; } - .xs\:cu-row-span-4 { + .xs\:bui-row-span-4 { grid-row: span 4 / span 4; } - .xs\:cu-row-span-5 { + .xs\:bui-row-span-5 { grid-row: span 5 / span 5; } - .xs\:cu-row-span-6 { + .xs\:bui-row-span-6 { grid-row: span 6 / span 6; } - .xs\:cu-row-span-7 { + .xs\:bui-row-span-7 { grid-row: span 7 / span 7; } - .xs\:cu-row-span-8 { + .xs\:bui-row-span-8 { grid-row: span 8 / span 8; } - .xs\:cu-row-span-9 { + .xs\:bui-row-span-9 { grid-row: span 9 / span 9; } - .xs\:cu-row-span-10 { + .xs\:bui-row-span-10 { grid-row: span 10 / span 10; } - .xs\:cu-row-span-11 { + .xs\:bui-row-span-11 { grid-row: span 11 / span 11; } - .xs\:cu-row-span-12 { + .xs\:bui-row-span-12 { grid-row: span 12 / span 12; } - .xs\:cu-row-span-auto { + .xs\:bui-row-span-auto { grid-row: span auto / span auto; } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-columns-1 { + .sm\:bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .sm\:cu-columns-2 { + .sm\:bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .sm\:cu-columns-3 { + .sm\:bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .sm\:cu-columns-4 { + .sm\:bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .sm\:cu-columns-5 { + .sm\:bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .sm\:cu-columns-6 { + .sm\:bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .sm\:cu-columns-7 { + .sm\:bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } - .sm\:cu-columns-8 { + .sm\:bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } - .sm\:cu-columns-9 { + .sm\:bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } - .sm\:cu-columns-10 { + .sm\:bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } - .sm\:cu-columns-11 { + .sm\:bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } - .sm\:cu-columns-12 { + .sm\:bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - .sm\:cu-columns-auto { + .sm\:bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } - .sm\:cu-col-span-1 { + .sm\:bui-col-span-1 { grid-column: span 1 / span 1; } - .sm\:cu-col-span-2 { + .sm\:bui-col-span-2 { grid-column: span 2 / span 2; } - .sm\:cu-col-span-3 { + .sm\:bui-col-span-3 { grid-column: span 3 / span 3; } - .sm\:cu-col-span-4 { + .sm\:bui-col-span-4 { grid-column: span 4 / span 4; } - .sm\:cu-col-span-5 { + .sm\:bui-col-span-5 { grid-column: span 5 / span 5; } - .sm\:cu-col-span-6 { + .sm\:bui-col-span-6 { grid-column: span 6 / span 6; } - .sm\:cu-col-span-7 { + .sm\:bui-col-span-7 { grid-column: span 7 / span 7; } - .sm\:cu-col-span-8 { + .sm\:bui-col-span-8 { grid-column: span 8 / span 8; } - .sm\:cu-col-span-9 { + .sm\:bui-col-span-9 { grid-column: span 9 / span 9; } - .sm\:cu-col-span-10 { + .sm\:bui-col-span-10 { grid-column: span 10 / span 10; } - .sm\:cu-col-span-11 { + .sm\:bui-col-span-11 { grid-column: span 11 / span 11; } - .sm\:cu-col-span-12 { + .sm\:bui-col-span-12 { grid-column: span 12 / span 12; } - .sm\:cu-col-span-auto { + .sm\:bui-col-span-auto { grid-column: span auto / span auto; } - .sm\:cu-col-start-1 { + .sm\:bui-col-start-1 { grid-column-start: 1; } - .sm\:cu-col-start-2 { + .sm\:bui-col-start-2 { grid-column-start: 2; } - .sm\:cu-col-start-3 { + .sm\:bui-col-start-3 { grid-column-start: 3; } - .sm\:cu-col-start-4 { + .sm\:bui-col-start-4 { grid-column-start: 4; } - .sm\:cu-col-start-5 { + .sm\:bui-col-start-5 { grid-column-start: 5; } - .sm\:cu-col-start-6 { + .sm\:bui-col-start-6 { grid-column-start: 6; } - .sm\:cu-col-start-7 { + .sm\:bui-col-start-7 { grid-column-start: 7; } - .sm\:cu-col-start-8 { + .sm\:bui-col-start-8 { grid-column-start: 8; } - .sm\:cu-col-start-9 { + .sm\:bui-col-start-9 { grid-column-start: 9; } - .sm\:cu-col-start-10 { + .sm\:bui-col-start-10 { grid-column-start: 10; } - .sm\:cu-col-start-11 { + .sm\:bui-col-start-11 { grid-column-start: 11; } - .sm\:cu-col-start-12 { + .sm\:bui-col-start-12 { grid-column-start: 12; } - .sm\:cu-col-start-13 { + .sm\:bui-col-start-13 { grid-column-start: 13; } - .sm\:cu-col-start-auto { + .sm\:bui-col-start-auto { grid-column-start: auto; } - .sm\:cu-col-end-1 { + .sm\:bui-col-end-1 { grid-column-end: 1; } - .sm\:cu-col-end-2 { + .sm\:bui-col-end-2 { grid-column-end: 2; } - .sm\:cu-col-end-3 { + .sm\:bui-col-end-3 { grid-column-end: 3; } - .sm\:cu-col-end-4 { + .sm\:bui-col-end-4 { grid-column-end: 4; } - .sm\:cu-col-end-5 { + .sm\:bui-col-end-5 { grid-column-end: 5; } - .sm\:cu-col-end-6 { + .sm\:bui-col-end-6 { grid-column-end: 6; } - .sm\:cu-col-end-7 { + .sm\:bui-col-end-7 { grid-column-end: 7; } - .sm\:cu-col-end-8 { + .sm\:bui-col-end-8 { grid-column-end: 8; } - .sm\:cu-col-end-9 { + .sm\:bui-col-end-9 { grid-column-end: 9; } - .sm\:cu-col-end-10 { + .sm\:bui-col-end-10 { grid-column-end: 10; } - .sm\:cu-col-end-11 { + .sm\:bui-col-end-11 { grid-column-end: 11; } - .sm\:cu-col-end-12 { + .sm\:bui-col-end-12 { grid-column-end: 12; } - .sm\:cu-col-end-13 { + .sm\:bui-col-end-13 { grid-column-end: 13; } - .sm\:cu-col-end-auto { + .sm\:bui-col-end-auto { grid-column-end: auto; } - .sm\:cu-row-span-1 { + .sm\:bui-row-span-1 { grid-row: span 1 / span 1; } - .sm\:cu-row-span-2 { + .sm\:bui-row-span-2 { grid-row: span 2 / span 2; } - .sm\:cu-row-span-3 { + .sm\:bui-row-span-3 { grid-row: span 3 / span 3; } - .sm\:cu-row-span-4 { + .sm\:bui-row-span-4 { grid-row: span 4 / span 4; } - .sm\:cu-row-span-5 { + .sm\:bui-row-span-5 { grid-row: span 5 / span 5; } - .sm\:cu-row-span-6 { + .sm\:bui-row-span-6 { grid-row: span 6 / span 6; } - .sm\:cu-row-span-7 { + .sm\:bui-row-span-7 { grid-row: span 7 / span 7; } - .sm\:cu-row-span-8 { + .sm\:bui-row-span-8 { grid-row: span 8 / span 8; } - .sm\:cu-row-span-9 { + .sm\:bui-row-span-9 { grid-row: span 9 / span 9; } - .sm\:cu-row-span-10 { + .sm\:bui-row-span-10 { grid-row: span 10 / span 10; } - .sm\:cu-row-span-11 { + .sm\:bui-row-span-11 { grid-row: span 11 / span 11; } - .sm\:cu-row-span-12 { + .sm\:bui-row-span-12 { grid-row: span 12 / span 12; } - .sm\:cu-row-span-auto { + .sm\:bui-row-span-auto { grid-row: span auto / span auto; } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-columns-1 { + .md\:bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .md\:cu-columns-2 { + .md\:bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .md\:cu-columns-3 { + .md\:bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .md\:cu-columns-4 { + .md\:bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .md\:cu-columns-5 { + .md\:bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .md\:cu-columns-6 { + .md\:bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .md\:cu-columns-7 { + .md\:bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } - .md\:cu-columns-8 { + .md\:bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } - .md\:cu-columns-9 { + .md\:bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } - .md\:cu-columns-10 { + .md\:bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } - .md\:cu-columns-11 { + .md\:bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } - .md\:cu-columns-12 { + .md\:bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - .md\:cu-columns-auto { + .md\:bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } - .md\:cu-col-span-1 { + .md\:bui-col-span-1 { grid-column: span 1 / span 1; } - .md\:cu-col-span-2 { + .md\:bui-col-span-2 { grid-column: span 2 / span 2; } - .md\:cu-col-span-3 { + .md\:bui-col-span-3 { grid-column: span 3 / span 3; } - .md\:cu-col-span-4 { + .md\:bui-col-span-4 { grid-column: span 4 / span 4; } - .md\:cu-col-span-5 { + .md\:bui-col-span-5 { grid-column: span 5 / span 5; } - .md\:cu-col-span-6 { + .md\:bui-col-span-6 { grid-column: span 6 / span 6; } - .md\:cu-col-span-7 { + .md\:bui-col-span-7 { grid-column: span 7 / span 7; } - .md\:cu-col-span-8 { + .md\:bui-col-span-8 { grid-column: span 8 / span 8; } - .md\:cu-col-span-9 { + .md\:bui-col-span-9 { grid-column: span 9 / span 9; } - .md\:cu-col-span-10 { + .md\:bui-col-span-10 { grid-column: span 10 / span 10; } - .md\:cu-col-span-11 { + .md\:bui-col-span-11 { grid-column: span 11 / span 11; } - .md\:cu-col-span-12 { + .md\:bui-col-span-12 { grid-column: span 12 / span 12; } - .md\:cu-col-span-auto { + .md\:bui-col-span-auto { grid-column: span auto / span auto; } - .md\:cu-col-start-1 { + .md\:bui-col-start-1 { grid-column-start: 1; } - .md\:cu-col-start-2 { + .md\:bui-col-start-2 { grid-column-start: 2; } - .md\:cu-col-start-3 { + .md\:bui-col-start-3 { grid-column-start: 3; } - .md\:cu-col-start-4 { + .md\:bui-col-start-4 { grid-column-start: 4; } - .md\:cu-col-start-5 { + .md\:bui-col-start-5 { grid-column-start: 5; } - .md\:cu-col-start-6 { + .md\:bui-col-start-6 { grid-column-start: 6; } - .md\:cu-col-start-7 { + .md\:bui-col-start-7 { grid-column-start: 7; } - .md\:cu-col-start-8 { + .md\:bui-col-start-8 { grid-column-start: 8; } - .md\:cu-col-start-9 { + .md\:bui-col-start-9 { grid-column-start: 9; } - .md\:cu-col-start-10 { + .md\:bui-col-start-10 { grid-column-start: 10; } - .md\:cu-col-start-11 { + .md\:bui-col-start-11 { grid-column-start: 11; } - .md\:cu-col-start-12 { + .md\:bui-col-start-12 { grid-column-start: 12; } - .md\:cu-col-start-13 { + .md\:bui-col-start-13 { grid-column-start: 13; } - .md\:cu-col-start-auto { + .md\:bui-col-start-auto { grid-column-start: auto; } - .md\:cu-col-end-1 { + .md\:bui-col-end-1 { grid-column-end: 1; } - .md\:cu-col-end-2 { + .md\:bui-col-end-2 { grid-column-end: 2; } - .md\:cu-col-end-3 { + .md\:bui-col-end-3 { grid-column-end: 3; } - .md\:cu-col-end-4 { + .md\:bui-col-end-4 { grid-column-end: 4; } - .md\:cu-col-end-5 { + .md\:bui-col-end-5 { grid-column-end: 5; } - .md\:cu-col-end-6 { + .md\:bui-col-end-6 { grid-column-end: 6; } - .md\:cu-col-end-7 { + .md\:bui-col-end-7 { grid-column-end: 7; } - .md\:cu-col-end-8 { + .md\:bui-col-end-8 { grid-column-end: 8; } - .md\:cu-col-end-9 { + .md\:bui-col-end-9 { grid-column-end: 9; } - .md\:cu-col-end-10 { + .md\:bui-col-end-10 { grid-column-end: 10; } - .md\:cu-col-end-11 { + .md\:bui-col-end-11 { grid-column-end: 11; } - .md\:cu-col-end-12 { + .md\:bui-col-end-12 { grid-column-end: 12; } - .md\:cu-col-end-13 { + .md\:bui-col-end-13 { grid-column-end: 13; } - .md\:cu-col-end-auto { + .md\:bui-col-end-auto { grid-column-end: auto; } - .md\:cu-row-span-1 { + .md\:bui-row-span-1 { grid-row: span 1 / span 1; } - .md\:cu-row-span-2 { + .md\:bui-row-span-2 { grid-row: span 2 / span 2; } - .md\:cu-row-span-3 { + .md\:bui-row-span-3 { grid-row: span 3 / span 3; } - .md\:cu-row-span-4 { + .md\:bui-row-span-4 { grid-row: span 4 / span 4; } - .md\:cu-row-span-5 { + .md\:bui-row-span-5 { grid-row: span 5 / span 5; } - .md\:cu-row-span-6 { + .md\:bui-row-span-6 { grid-row: span 6 / span 6; } - .md\:cu-row-span-7 { + .md\:bui-row-span-7 { grid-row: span 7 / span 7; } - .md\:cu-row-span-8 { + .md\:bui-row-span-8 { grid-row: span 8 / span 8; } - .md\:cu-row-span-9 { + .md\:bui-row-span-9 { grid-row: span 9 / span 9; } - .md\:cu-row-span-10 { + .md\:bui-row-span-10 { grid-row: span 10 / span 10; } - .md\:cu-row-span-11 { + .md\:bui-row-span-11 { grid-row: span 11 / span 11; } - .md\:cu-row-span-12 { + .md\:bui-row-span-12 { grid-row: span 12 / span 12; } - .md\:cu-row-span-auto { + .md\:bui-row-span-auto { grid-row: span auto / span auto; } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-columns-1 { + .lg\:bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .lg\:cu-columns-2 { + .lg\:bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .lg\:cu-columns-3 { + .lg\:bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .lg\:cu-columns-4 { + .lg\:bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .lg\:cu-columns-5 { + .lg\:bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .lg\:cu-columns-6 { + .lg\:bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .lg\:cu-columns-7 { + .lg\:bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } - .lg\:cu-columns-8 { + .lg\:bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } - .lg\:cu-columns-9 { + .lg\:bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } - .lg\:cu-columns-10 { + .lg\:bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } - .lg\:cu-columns-11 { + .lg\:bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } - .lg\:cu-columns-12 { + .lg\:bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - .lg\:cu-columns-auto { + .lg\:bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } - .lg\:cu-col-span-1 { + .lg\:bui-col-span-1 { grid-column: span 1 / span 1; } - .lg\:cu-col-span-2 { + .lg\:bui-col-span-2 { grid-column: span 2 / span 2; } - .lg\:cu-col-span-3 { + .lg\:bui-col-span-3 { grid-column: span 3 / span 3; } - .lg\:cu-col-span-4 { + .lg\:bui-col-span-4 { grid-column: span 4 / span 4; } - .lg\:cu-col-span-5 { + .lg\:bui-col-span-5 { grid-column: span 5 / span 5; } - .lg\:cu-col-span-6 { + .lg\:bui-col-span-6 { grid-column: span 6 / span 6; } - .lg\:cu-col-span-7 { + .lg\:bui-col-span-7 { grid-column: span 7 / span 7; } - .lg\:cu-col-span-8 { + .lg\:bui-col-span-8 { grid-column: span 8 / span 8; } - .lg\:cu-col-span-9 { + .lg\:bui-col-span-9 { grid-column: span 9 / span 9; } - .lg\:cu-col-span-10 { + .lg\:bui-col-span-10 { grid-column: span 10 / span 10; } - .lg\:cu-col-span-11 { + .lg\:bui-col-span-11 { grid-column: span 11 / span 11; } - .lg\:cu-col-span-12 { + .lg\:bui-col-span-12 { grid-column: span 12 / span 12; } - .lg\:cu-col-span-auto { + .lg\:bui-col-span-auto { grid-column: span auto / span auto; } - .lg\:cu-col-start-1 { + .lg\:bui-col-start-1 { grid-column-start: 1; } - .lg\:cu-col-start-2 { + .lg\:bui-col-start-2 { grid-column-start: 2; } - .lg\:cu-col-start-3 { + .lg\:bui-col-start-3 { grid-column-start: 3; } - .lg\:cu-col-start-4 { + .lg\:bui-col-start-4 { grid-column-start: 4; } - .lg\:cu-col-start-5 { + .lg\:bui-col-start-5 { grid-column-start: 5; } - .lg\:cu-col-start-6 { + .lg\:bui-col-start-6 { grid-column-start: 6; } - .lg\:cu-col-start-7 { + .lg\:bui-col-start-7 { grid-column-start: 7; } - .lg\:cu-col-start-8 { + .lg\:bui-col-start-8 { grid-column-start: 8; } - .lg\:cu-col-start-9 { + .lg\:bui-col-start-9 { grid-column-start: 9; } - .lg\:cu-col-start-10 { + .lg\:bui-col-start-10 { grid-column-start: 10; } - .lg\:cu-col-start-11 { + .lg\:bui-col-start-11 { grid-column-start: 11; } - .lg\:cu-col-start-12 { + .lg\:bui-col-start-12 { grid-column-start: 12; } - .lg\:cu-col-start-13 { + .lg\:bui-col-start-13 { grid-column-start: 13; } - .lg\:cu-col-start-auto { + .lg\:bui-col-start-auto { grid-column-start: auto; } - .lg\:cu-col-end-1 { + .lg\:bui-col-end-1 { grid-column-end: 1; } - .lg\:cu-col-end-2 { + .lg\:bui-col-end-2 { grid-column-end: 2; } - .lg\:cu-col-end-3 { + .lg\:bui-col-end-3 { grid-column-end: 3; } - .lg\:cu-col-end-4 { + .lg\:bui-col-end-4 { grid-column-end: 4; } - .lg\:cu-col-end-5 { + .lg\:bui-col-end-5 { grid-column-end: 5; } - .lg\:cu-col-end-6 { + .lg\:bui-col-end-6 { grid-column-end: 6; } - .lg\:cu-col-end-7 { + .lg\:bui-col-end-7 { grid-column-end: 7; } - .lg\:cu-col-end-8 { + .lg\:bui-col-end-8 { grid-column-end: 8; } - .lg\:cu-col-end-9 { + .lg\:bui-col-end-9 { grid-column-end: 9; } - .lg\:cu-col-end-10 { + .lg\:bui-col-end-10 { grid-column-end: 10; } - .lg\:cu-col-end-11 { + .lg\:bui-col-end-11 { grid-column-end: 11; } - .lg\:cu-col-end-12 { + .lg\:bui-col-end-12 { grid-column-end: 12; } - .lg\:cu-col-end-13 { + .lg\:bui-col-end-13 { grid-column-end: 13; } - .lg\:cu-col-end-auto { + .lg\:bui-col-end-auto { grid-column-end: auto; } - .lg\:cu-row-span-1 { + .lg\:bui-row-span-1 { grid-row: span 1 / span 1; } - .lg\:cu-row-span-2 { + .lg\:bui-row-span-2 { grid-row: span 2 / span 2; } - .lg\:cu-row-span-3 { + .lg\:bui-row-span-3 { grid-row: span 3 / span 3; } - .lg\:cu-row-span-4 { + .lg\:bui-row-span-4 { grid-row: span 4 / span 4; } - .lg\:cu-row-span-5 { + .lg\:bui-row-span-5 { grid-row: span 5 / span 5; } - .lg\:cu-row-span-6 { + .lg\:bui-row-span-6 { grid-row: span 6 / span 6; } - .lg\:cu-row-span-7 { + .lg\:bui-row-span-7 { grid-row: span 7 / span 7; } - .lg\:cu-row-span-8 { + .lg\:bui-row-span-8 { grid-row: span 8 / span 8; } - .lg\:cu-row-span-9 { + .lg\:bui-row-span-9 { grid-row: span 9 / span 9; } - .lg\:cu-row-span-10 { + .lg\:bui-row-span-10 { grid-row: span 10 / span 10; } - .lg\:cu-row-span-11 { + .lg\:bui-row-span-11 { grid-row: span 11 / span 11; } - .lg\:cu-row-span-12 { + .lg\:bui-row-span-12 { grid-row: span 12 / span 12; } - .lg\:cu-row-span-auto { + .lg\:bui-row-span-auto { grid-row: span auto / span auto; } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-columns-1 { + .xl\:bui-columns-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } - .xl\:cu-columns-2 { + .xl\:bui-columns-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .xl\:cu-columns-3 { + .xl\:bui-columns-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } - .xl\:cu-columns-4 { + .xl\:bui-columns-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); } - .xl\:cu-columns-5 { + .xl\:bui-columns-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); } - .xl\:cu-columns-6 { + .xl\:bui-columns-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); } - .xl\:cu-columns-7 { + .xl\:bui-columns-7 { grid-template-columns: repeat(7, minmax(0, 1fr)); } - .xl\:cu-columns-8 { + .xl\:bui-columns-8 { grid-template-columns: repeat(8, minmax(0, 1fr)); } - .xl\:cu-columns-9 { + .xl\:bui-columns-9 { grid-template-columns: repeat(9, minmax(0, 1fr)); } - .xl\:cu-columns-10 { + .xl\:bui-columns-10 { grid-template-columns: repeat(10, minmax(0, 1fr)); } - .xl\:cu-columns-11 { + .xl\:bui-columns-11 { grid-template-columns: repeat(11, minmax(0, 1fr)); } - .xl\:cu-columns-12 { + .xl\:bui-columns-12 { grid-template-columns: repeat(12, minmax(0, 1fr)); } - .xl\:cu-columns-auto { + .xl\:bui-columns-auto { grid-template-columns: repeat(auto-fit, minmax(0, 1fr)); } - .xl\:cu-col-span-1 { + .xl\:bui-col-span-1 { grid-column: span 1 / span 1; } - .xl\:cu-col-span-2 { + .xl\:bui-col-span-2 { grid-column: span 2 / span 2; } - .xl\:cu-col-span-3 { + .xl\:bui-col-span-3 { grid-column: span 3 / span 3; } - .xl\:cu-col-span-4 { + .xl\:bui-col-span-4 { grid-column: span 4 / span 4; } - .xl\:cu-col-span-5 { + .xl\:bui-col-span-5 { grid-column: span 5 / span 5; } - .xl\:cu-col-span-6 { + .xl\:bui-col-span-6 { grid-column: span 6 / span 6; } - .xl\:cu-col-span-7 { + .xl\:bui-col-span-7 { grid-column: span 7 / span 7; } - .xl\:cu-col-span-8 { + .xl\:bui-col-span-8 { grid-column: span 8 / span 8; } - .xl\:cu-col-span-9 { + .xl\:bui-col-span-9 { grid-column: span 9 / span 9; } - .xl\:cu-col-span-10 { + .xl\:bui-col-span-10 { grid-column: span 10 / span 10; } - .xl\:cu-col-span-11 { + .xl\:bui-col-span-11 { grid-column: span 11 / span 11; } - .xl\:cu-col-span-12 { + .xl\:bui-col-span-12 { grid-column: span 12 / span 12; } - .xl\:cu-col-span-auto { + .xl\:bui-col-span-auto { grid-column: span auto / span auto; } - .xl\:cu-col-start-1 { + .xl\:bui-col-start-1 { grid-column-start: 1; } - .xl\:cu-col-start-2 { + .xl\:bui-col-start-2 { grid-column-start: 2; } - .xl\:cu-col-start-3 { + .xl\:bui-col-start-3 { grid-column-start: 3; } - .xl\:cu-col-start-4 { + .xl\:bui-col-start-4 { grid-column-start: 4; } - .xl\:cu-col-start-5 { + .xl\:bui-col-start-5 { grid-column-start: 5; } - .xl\:cu-col-start-6 { + .xl\:bui-col-start-6 { grid-column-start: 6; } - .xl\:cu-col-start-7 { + .xl\:bui-col-start-7 { grid-column-start: 7; } - .xl\:cu-col-start-8 { + .xl\:bui-col-start-8 { grid-column-start: 8; } - .xl\:cu-col-start-9 { + .xl\:bui-col-start-9 { grid-column-start: 9; } - .xl\:cu-col-start-10 { + .xl\:bui-col-start-10 { grid-column-start: 10; } - .xl\:cu-col-start-11 { + .xl\:bui-col-start-11 { grid-column-start: 11; } - .xl\:cu-col-start-12 { + .xl\:bui-col-start-12 { grid-column-start: 12; } - .xl\:cu-col-start-13 { + .xl\:bui-col-start-13 { grid-column-start: 13; } - .xl\:cu-col-start-auto { + .xl\:bui-col-start-auto { grid-column-start: auto; } - .xl\:cu-col-end-1 { + .xl\:bui-col-end-1 { grid-column-end: 1; } - .xl\:cu-col-end-2 { + .xl\:bui-col-end-2 { grid-column-end: 2; } - .xl\:cu-col-end-3 { + .xl\:bui-col-end-3 { grid-column-end: 3; } - .xl\:cu-col-end-4 { + .xl\:bui-col-end-4 { grid-column-end: 4; } - .xl\:cu-col-end-5 { + .xl\:bui-col-end-5 { grid-column-end: 5; } - .xl\:cu-col-end-6 { + .xl\:bui-col-end-6 { grid-column-end: 6; } - .xl\:cu-col-end-7 { + .xl\:bui-col-end-7 { grid-column-end: 7; } - .xl\:cu-col-end-8 { + .xl\:bui-col-end-8 { grid-column-end: 8; } - .xl\:cu-col-end-9 { + .xl\:bui-col-end-9 { grid-column-end: 9; } - .xl\:cu-col-end-10 { + .xl\:bui-col-end-10 { grid-column-end: 10; } - .xl\:cu-col-end-11 { + .xl\:bui-col-end-11 { grid-column-end: 11; } - .xl\:cu-col-end-12 { + .xl\:bui-col-end-12 { grid-column-end: 12; } - .xl\:cu-col-end-13 { + .xl\:bui-col-end-13 { grid-column-end: 13; } - .xl\:cu-col-end-auto { + .xl\:bui-col-end-auto { grid-column-end: auto; } - .xl\:cu-row-span-1 { + .xl\:bui-row-span-1 { grid-row: span 1 / span 1; } - .xl\:cu-row-span-2 { + .xl\:bui-row-span-2 { grid-row: span 2 / span 2; } - .xl\:cu-row-span-3 { + .xl\:bui-row-span-3 { grid-row: span 3 / span 3; } - .xl\:cu-row-span-4 { + .xl\:bui-row-span-4 { grid-row: span 4 / span 4; } - .xl\:cu-row-span-5 { + .xl\:bui-row-span-5 { grid-row: span 5 / span 5; } - .xl\:cu-row-span-6 { + .xl\:bui-row-span-6 { grid-row: span 6 / span 6; } - .xl\:cu-row-span-7 { + .xl\:bui-row-span-7 { grid-row: span 7 / span 7; } - .xl\:cu-row-span-8 { + .xl\:bui-row-span-8 { grid-row: span 8 / span 8; } - .xl\:cu-row-span-9 { + .xl\:bui-row-span-9 { grid-row: span 9 / span 9; } - .xl\:cu-row-span-10 { + .xl\:bui-row-span-10 { grid-row: span 10 / span 10; } - .xl\:cu-row-span-11 { + .xl\:bui-row-span-11 { grid-row: span 11 / span 11; } - .xl\:cu-row-span-12 { + .xl\:bui-row-span-12 { grid-row: span 12 / span 12; } - .xl\:cu-row-span-auto { + .xl\:bui-row-span-auto { grid-row: span auto / span auto; } } diff --git a/packages/canon/src/css/utilities/height.css b/packages/canon/src/css/utilities/height.css index 130dfc5020..5c245cbfb9 100644 --- a/packages/canon/src/css/utilities/height.css +++ b/packages/canon/src/css/utilities/height.css @@ -1,86 +1,86 @@ -.cu-h { +.bui-h { height: var(--height); } -.cu-min-h { +.bui-min-h { min-height: var(--min-height); } -.cu-max-h { +.bui-max-h { max-height: var(--max-height); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-h { + .xs\:bui-h { height: var(--height); } - .xs\:cu-min-h { + .xs\:bui-min-h { min-height: var(--min-height); } - .xs\:cu-max-h { + .xs\:bui-max-h { max-height: var(--max-height); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-h { + .sm\:bui-h { height: var(--height); } - .sm\:cu-min-h { + .sm\:bui-min-h { min-height: var(--min-height); } - .sm\:cu-max-h { + .sm\:bui-max-h { max-height: var(--max-height); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-h { + .md\:bui-h { height: var(--height); } - .md\:cu-min-h { + .md\:bui-min-h { min-height: var(--min-height); } - .md\:cu-max-h { + .md\:bui-max-h { max-height: var(--max-height); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-h { + .lg\:bui-h { height: var(--height); } - .lg\:cu-min-h { + .lg\:bui-min-h { min-height: var(--min-height); } - .lg\:cu-max-h { + .lg\:bui-max-h { max-height: var(--max-height); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-h { + .xl\:bui-h { height: var(--height); } - .xl\:cu-min-h { + .xl\:bui-min-h { min-height: var(--min-height); } - .xl\:cu-max-h { + .xl\:bui-max-h { max-height: var(--max-height); } } diff --git a/packages/canon/src/css/utilities/m.css b/packages/canon/src/css/utilities/m.css index ecee3aca35..b2c1099d9b 100644 --- a/packages/canon/src/css/utilities/m.css +++ b/packages/canon/src/css/utilities/m.css @@ -1,422 +1,422 @@ -.cu-m { +.bui-m { margin: var(--m); } -.cu-m-0\.5 { - margin: var(--canon-space-0_5); +.bui-m-0\.5 { + margin: var(--bui-space-0_5); } -.cu-m-1 { - margin: var(--canon-space-1); +.bui-m-1 { + margin: var(--bui-space-1); } -.cu-m-1\.5 { - margin: var(--canon-space-1_5); +.bui-m-1\.5 { + margin: var(--bui-space-1_5); } -.cu-m-2 { - margin: var(--canon-space-2); +.bui-m-2 { + margin: var(--bui-space-2); } -.cu-m-3 { - margin: var(--canon-space-3); +.bui-m-3 { + margin: var(--bui-space-3); } -.cu-m-4 { - margin: var(--canon-space-4); +.bui-m-4 { + margin: var(--bui-space-4); } -.cu-m-5 { - margin: var(--canon-space-5); +.bui-m-5 { + margin: var(--bui-space-5); } -.cu-m-6 { - margin: var(--canon-space-6); +.bui-m-6 { + margin: var(--bui-space-6); } -.cu-m-7 { - margin: var(--canon-space-7); +.bui-m-7 { + margin: var(--bui-space-7); } -.cu-m-8 { - margin: var(--canon-space-8); +.bui-m-8 { + margin: var(--bui-space-8); } -.cu-m-9 { - margin: var(--canon-space-9); +.bui-m-9 { + margin: var(--bui-space-9); } -.cu-m-10 { - margin: var(--canon-space-10); +.bui-m-10 { + margin: var(--bui-space-10); } -.cu-m-11 { - margin: var(--canon-space-11); +.bui-m-11 { + margin: var(--bui-space-11); } -.cu-m-12 { - margin: var(--canon-space-12); +.bui-m-12 { + margin: var(--bui-space-12); } -.cu-m-13 { - margin: var(--canon-space-13); +.bui-m-13 { + margin: var(--bui-space-13); } -.cu-m-14 { - margin: var(--canon-space-14); +.bui-m-14 { + margin: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-m { + .xs\:bui-m { margin: var(--p-xs); } - .xs\:cu-m-0\.5 { - margin: var(--canon-space-0_5); + .xs\:bui-m-0\.5 { + margin: var(--bui-space-0_5); } - .xs\:cu-m-1 { - margin: var(--canon-space-1); + .xs\:bui-m-1 { + margin: var(--bui-space-1); } - .xs\:cu-m-1\.5 { - margin: var(--canon-space-1_5); + .xs\:bui-m-1\.5 { + margin: var(--bui-space-1_5); } - .xs\:cu-m-2 { - margin: var(--canon-space-2); + .xs\:bui-m-2 { + margin: var(--bui-space-2); } - .xs\:cu-m-3 { - margin: var(--canon-space-3); + .xs\:bui-m-3 { + margin: var(--bui-space-3); } - .xs\:cu-m-4 { - margin: var(--canon-space-4); + .xs\:bui-m-4 { + margin: var(--bui-space-4); } - .xs\:cu-m-5 { - margin: var(--canon-space-5); + .xs\:bui-m-5 { + margin: var(--bui-space-5); } - .xs\:cu-m-6 { - margin: var(--canon-space-6); + .xs\:bui-m-6 { + margin: var(--bui-space-6); } - .xs\:cu-m-7 { - margin: var(--canon-space-7); + .xs\:bui-m-7 { + margin: var(--bui-space-7); } - .xs\:cu-m-8 { - margin: var(--canon-space-8); + .xs\:bui-m-8 { + margin: var(--bui-space-8); } - .xs\:cu-m-9 { - margin: var(--canon-space-9); + .xs\:bui-m-9 { + margin: var(--bui-space-9); } - .xs\:cu-m-10 { - margin: var(--canon-space-10); + .xs\:bui-m-10 { + margin: var(--bui-space-10); } - .xs\:cu-m-11 { - margin: var(--canon-space-11); + .xs\:bui-m-11 { + margin: var(--bui-space-11); } - .xs\:cu-m-12 { - margin: var(--canon-space-12); + .xs\:bui-m-12 { + margin: var(--bui-space-12); } - .xs\:cu-m-13 { - margin: var(--canon-space-13); + .xs\:bui-m-13 { + margin: var(--bui-space-13); } - .xs\:cu-m-14 { - margin: var(--canon-space-14); + .xs\:bui-m-14 { + margin: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-m { + .sm\:bui-m { margin: var(--p-sm); } - .sm\:cu-m-0\.5 { - margin: var(--canon-space-0_5); + .sm\:bui-m-0\.5 { + margin: var(--bui-space-0_5); } - .sm\:cu-m-1 { - margin: var(--canon-space-1); + .sm\:bui-m-1 { + margin: var(--bui-space-1); } - .sm\:cu-m-1\.5 { - margin: var(--canon-space-1_5); + .sm\:bui-m-1\.5 { + margin: var(--bui-space-1_5); } - .sm\:cu-m-2 { - margin: var(--canon-space-2); + .sm\:bui-m-2 { + margin: var(--bui-space-2); } - .sm\:cu-m-3 { - margin: var(--canon-space-3); + .sm\:bui-m-3 { + margin: var(--bui-space-3); } - .sm\:cu-m-4 { - margin: var(--canon-space-4); + .sm\:bui-m-4 { + margin: var(--bui-space-4); } - .sm\:cu-m-5 { - margin: var(--canon-space-5); + .sm\:bui-m-5 { + margin: var(--bui-space-5); } - .sm\:cu-m-6 { - margin: var(--canon-space-6); + .sm\:bui-m-6 { + margin: var(--bui-space-6); } - .sm\:cu-m-7 { - margin: var(--canon-space-7); + .sm\:bui-m-7 { + margin: var(--bui-space-7); } - .sm\:cu-m-8 { - margin: var(--canon-space-8); + .sm\:bui-m-8 { + margin: var(--bui-space-8); } - .sm\:cu-m-9 { - margin: var(--canon-space-9); + .sm\:bui-m-9 { + margin: var(--bui-space-9); } - .sm\:cu-m-10 { - margin: var(--canon-space-10); + .sm\:bui-m-10 { + margin: var(--bui-space-10); } - .sm\:cu-m-11 { - margin: var(--canon-space-11); + .sm\:bui-m-11 { + margin: var(--bui-space-11); } - .sm\:cu-m-12 { - margin: var(--canon-space-12); + .sm\:bui-m-12 { + margin: var(--bui-space-12); } - .sm\:cu-m-13 { - margin: var(--canon-space-13); + .sm\:bui-m-13 { + margin: var(--bui-space-13); } - .sm\:cu-m-14 { - margin: var(--canon-space-14); + .sm\:bui-m-14 { + margin: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-m { + .md\:bui-m { margin: var(--p-md); } - .md\:cu-m-0\.5 { - margin: var(--canon-space-0_5); + .md\:bui-m-0\.5 { + margin: var(--bui-space-0_5); } - .md\:cu-m-1 { - margin: var(--canon-space-1); + .md\:bui-m-1 { + margin: var(--bui-space-1); } - .md\:cu-m-1\.5 { - margin: var(--canon-space-1_5); + .md\:bui-m-1\.5 { + margin: var(--bui-space-1_5); } - .md\:cu-m-2 { - margin: var(--canon-space-2); + .md\:bui-m-2 { + margin: var(--bui-space-2); } - .md\:cu-m-3 { - margin: var(--canon-space-3); + .md\:bui-m-3 { + margin: var(--bui-space-3); } - .md\:cu-m-4 { - margin: var(--canon-space-4); + .md\:bui-m-4 { + margin: var(--bui-space-4); } - .md\:cu-m-5 { - margin: var(--canon-space-5); + .md\:bui-m-5 { + margin: var(--bui-space-5); } - .md\:cu-m-6 { - margin: var(--canon-space-6); + .md\:bui-m-6 { + margin: var(--bui-space-6); } - .md\:cu-m-7 { - margin: var(--canon-space-7); + .md\:bui-m-7 { + margin: var(--bui-space-7); } - .md\:cu-m-8 { - margin: var(--canon-space-8); + .md\:bui-m-8 { + margin: var(--bui-space-8); } - .md\:cu-m-9 { - margin: var(--canon-space-9); + .md\:bui-m-9 { + margin: var(--bui-space-9); } - .md\:cu-m-10 { - margin: var(--canon-space-10); + .md\:bui-m-10 { + margin: var(--bui-space-10); } - .md\:cu-m-11 { - margin: var(--canon-space-11); + .md\:bui-m-11 { + margin: var(--bui-space-11); } - .md\:cu-m-12 { - margin: var(--canon-space-12); + .md\:bui-m-12 { + margin: var(--bui-space-12); } - .md\:cu-m-13 { - margin: var(--canon-space-13); + .md\:bui-m-13 { + margin: var(--bui-space-13); } - .md\:cu-m-14 { - margin: var(--canon-space-14); + .md\:bui-m-14 { + margin: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-m { + .lg\:bui-m { margin: var(--p-lg); } - .lg\:cu-m-0\.5 { - margin: var(--canon-space-0_5); + .lg\:bui-m-0\.5 { + margin: var(--bui-space-0_5); } - .lg\:cu-m-1 { - margin: var(--canon-space-1); + .lg\:bui-m-1 { + margin: var(--bui-space-1); } - .lg\:cu-m-1\.5 { - margin: var(--canon-space-1_5); + .lg\:bui-m-1\.5 { + margin: var(--bui-space-1_5); } - .lg\:cu-m-2 { - margin: var(--canon-space-2); + .lg\:bui-m-2 { + margin: var(--bui-space-2); } - .lg\:cu-m-3 { - margin: var(--canon-space-3); + .lg\:bui-m-3 { + margin: var(--bui-space-3); } - .lg\:cu-m-4 { - margin: var(--canon-space-4); + .lg\:bui-m-4 { + margin: var(--bui-space-4); } - .lg\:cu-m-5 { - margin: var(--canon-space-5); + .lg\:bui-m-5 { + margin: var(--bui-space-5); } - .lg\:cu-m-6 { - margin: var(--canon-space-6); + .lg\:bui-m-6 { + margin: var(--bui-space-6); } - .lg\:cu-m-7 { - margin: var(--canon-space-7); + .lg\:bui-m-7 { + margin: var(--bui-space-7); } - .lg\:cu-m-8 { - margin: var(--canon-space-8); + .lg\:bui-m-8 { + margin: var(--bui-space-8); } - .lg\:cu-m-9 { - margin: var(--canon-space-9); + .lg\:bui-m-9 { + margin: var(--bui-space-9); } - .lg\:cu-m-10 { - margin: var(--canon-space-10); + .lg\:bui-m-10 { + margin: var(--bui-space-10); } - .lg\:cu-m-11 { - margin: var(--canon-space-11); + .lg\:bui-m-11 { + margin: var(--bui-space-11); } - .lg\:cu-m-12 { - margin: var(--canon-space-12); + .lg\:bui-m-12 { + margin: var(--bui-space-12); } - .lg\:cu-m-13 { - margin: var(--canon-space-13); + .lg\:bui-m-13 { + margin: var(--bui-space-13); } - .lg\:cu-m-14 { - margin: var(--canon-space-14); + .lg\:bui-m-14 { + margin: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-m { + .xl\:bui-m { margin: var(--p-xl); } - .xl\:cu-m-0\.5 { - margin: var(--canon-space-0_5); + .xl\:bui-m-0\.5 { + margin: var(--bui-space-0_5); } - .xl\:cu-m-1 { - margin: var(--canon-space-1); + .xl\:bui-m-1 { + margin: var(--bui-space-1); } - .xl\:cu-m-1\.5 { - margin: var(--canon-space-1_5); + .xl\:bui-m-1\.5 { + margin: var(--bui-space-1_5); } - .xl\:cu-m-2 { - margin: var(--canon-space-2); + .xl\:bui-m-2 { + margin: var(--bui-space-2); } - .xl\:cu-m-3 { - margin: var(--canon-space-3); + .xl\:bui-m-3 { + margin: var(--bui-space-3); } - .xl\:cu-m-4 { - margin: var(--canon-space-4); + .xl\:bui-m-4 { + margin: var(--bui-space-4); } - .xl\:cu-m-5 { - margin: var(--canon-space-5); + .xl\:bui-m-5 { + margin: var(--bui-space-5); } - .xl\:cu-m-6 { - margin: var(--canon-space-6); + .xl\:bui-m-6 { + margin: var(--bui-space-6); } - .xl\:cu-m-7 { - margin: var(--canon-space-7); + .xl\:bui-m-7 { + margin: var(--bui-space-7); } - .xl\:cu-m-8 { - margin: var(--canon-space-8); + .xl\:bui-m-8 { + margin: var(--bui-space-8); } - .xl\:cu-m-9 { - margin: var(--canon-space-9); + .xl\:bui-m-9 { + margin: var(--bui-space-9); } - .xl\:cu-m-10 { - margin: var(--canon-space-10); + .xl\:bui-m-10 { + margin: var(--bui-space-10); } - .xl\:cu-m-11 { - margin: var(--canon-space-11); + .xl\:bui-m-11 { + margin: var(--bui-space-11); } - .xl\:cu-m-12 { - margin: var(--canon-space-12); + .xl\:bui-m-12 { + margin: var(--bui-space-12); } - .xl\:cu-m-13 { - margin: var(--canon-space-13); + .xl\:bui-m-13 { + margin: var(--bui-space-13); } - .xl\:cu-m-14 { - margin: var(--canon-space-14); + .xl\:bui-m-14 { + margin: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/mb.css b/packages/canon/src/css/utilities/mb.css index 6d3831bad3..576d4d488e 100644 --- a/packages/canon/src/css/utilities/mb.css +++ b/packages/canon/src/css/utilities/mb.css @@ -1,422 +1,422 @@ -.cu-mb { +.bui-mb { margin-bottom: var(--mb); } -.cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); +.bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } -.cu-mb-1 { - margin-bottom: var(--canon-space-1); +.bui-mb-1 { + margin-bottom: var(--bui-space-1); } -.cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); +.bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } -.cu-mb-2 { - margin-bottom: var(--canon-space-2); +.bui-mb-2 { + margin-bottom: var(--bui-space-2); } -.cu-mb-3 { - margin-bottom: var(--canon-space-3); +.bui-mb-3 { + margin-bottom: var(--bui-space-3); } -.cu-mb-4 { - margin-bottom: var(--canon-space-4); +.bui-mb-4 { + margin-bottom: var(--bui-space-4); } -.cu-mb-5 { - margin-bottom: var(--canon-space-5); +.bui-mb-5 { + margin-bottom: var(--bui-space-5); } -.cu-mb-6 { - margin-bottom: var(--canon-space-6); +.bui-mb-6 { + margin-bottom: var(--bui-space-6); } -.cu-mb-7 { - margin-bottom: var(--canon-space-7); +.bui-mb-7 { + margin-bottom: var(--bui-space-7); } -.cu-mb-8 { - margin-bottom: var(--canon-space-8); +.bui-mb-8 { + margin-bottom: var(--bui-space-8); } -.cu-mb-9 { - margin-bottom: var(--canon-space-9); +.bui-mb-9 { + margin-bottom: var(--bui-space-9); } -.cu-mb-10 { - margin-bottom: var(--canon-space-10); +.bui-mb-10 { + margin-bottom: var(--bui-space-10); } -.cu-mb-11 { - margin-bottom: var(--canon-space-11); +.bui-mb-11 { + margin-bottom: var(--bui-space-11); } -.cu-mb-12 { - margin-bottom: var(--canon-space-12); +.bui-mb-12 { + margin-bottom: var(--bui-space-12); } -.cu-mb-13 { - margin-bottom: var(--canon-space-13); +.bui-mb-13 { + margin-bottom: var(--bui-space-13); } -.cu-mb-14 { - margin-bottom: var(--canon-space-14); +.bui-mb-14 { + margin-bottom: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-mb { + .xs\:bui-mb { margin-bottom: var(--pb-xs); } - .xs\:cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); + .xs\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } - .xs\:cu-mb-1 { - margin-bottom: var(--canon-space-1); + .xs\:bui-mb-1 { + margin-bottom: var(--bui-space-1); } - .xs\:cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); + .xs\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } - .xs\:cu-mb-2 { - margin-bottom: var(--canon-space-2); + .xs\:bui-mb-2 { + margin-bottom: var(--bui-space-2); } - .xs\:cu-mb-3 { - margin-bottom: var(--canon-space-3); + .xs\:bui-mb-3 { + margin-bottom: var(--bui-space-3); } - .xs\:cu-mb-4 { - margin-bottom: var(--canon-space-4); + .xs\:bui-mb-4 { + margin-bottom: var(--bui-space-4); } - .xs\:cu-mb-5 { - margin-bottom: var(--canon-space-5); + .xs\:bui-mb-5 { + margin-bottom: var(--bui-space-5); } - .xs\:cu-mb-6 { - margin-bottom: var(--canon-space-6); + .xs\:bui-mb-6 { + margin-bottom: var(--bui-space-6); } - .xs\:cu-mb-7 { - margin-bottom: var(--canon-space-7); + .xs\:bui-mb-7 { + margin-bottom: var(--bui-space-7); } - .xs\:cu-mb-8 { - margin-bottom: var(--canon-space-8); + .xs\:bui-mb-8 { + margin-bottom: var(--bui-space-8); } - .xs\:cu-mb-9 { - margin-bottom: var(--canon-space-9); + .xs\:bui-mb-9 { + margin-bottom: var(--bui-space-9); } - .xs\:cu-mb-10 { - margin-bottom: var(--canon-space-10); + .xs\:bui-mb-10 { + margin-bottom: var(--bui-space-10); } - .xs\:cu-mb-11 { - margin-bottom: var(--canon-space-11); + .xs\:bui-mb-11 { + margin-bottom: var(--bui-space-11); } - .xs\:cu-mb-12 { - margin-bottom: var(--canon-space-12); + .xs\:bui-mb-12 { + margin-bottom: var(--bui-space-12); } - .xs\:cu-mb-13 { - margin-bottom: var(--canon-space-13); + .xs\:bui-mb-13 { + margin-bottom: var(--bui-space-13); } - .xs\:cu-mb-14 { - margin-bottom: var(--canon-space-14); + .xs\:bui-mb-14 { + margin-bottom: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-mb { + .sm\:bui-mb { margin-bottom: var(--pb-sm); } - .sm\:cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); + .sm\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } - .sm\:cu-mb-1 { - margin-bottom: var(--canon-space-1); + .sm\:bui-mb-1 { + margin-bottom: var(--bui-space-1); } - .sm\:cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); + .sm\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } - .sm\:cu-mb-2 { - margin-bottom: var(--canon-space-2); + .sm\:bui-mb-2 { + margin-bottom: var(--bui-space-2); } - .sm\:cu-mb-3 { - margin-bottom: var(--canon-space-3); + .sm\:bui-mb-3 { + margin-bottom: var(--bui-space-3); } - .sm\:cu-mb-4 { - margin-bottom: var(--canon-space-4); + .sm\:bui-mb-4 { + margin-bottom: var(--bui-space-4); } - .sm\:cu-mb-5 { - margin-bottom: var(--canon-space-5); + .sm\:bui-mb-5 { + margin-bottom: var(--bui-space-5); } - .sm\:cu-mb-6 { - margin-bottom: var(--canon-space-6); + .sm\:bui-mb-6 { + margin-bottom: var(--bui-space-6); } - .sm\:cu-mb-7 { - margin-bottom: var(--canon-space-7); + .sm\:bui-mb-7 { + margin-bottom: var(--bui-space-7); } - .sm\:cu-mb-8 { - margin-bottom: var(--canon-space-8); + .sm\:bui-mb-8 { + margin-bottom: var(--bui-space-8); } - .sm\:cu-mb-9 { - margin-bottom: var(--canon-space-9); + .sm\:bui-mb-9 { + margin-bottom: var(--bui-space-9); } - .sm\:cu-mb-10 { - margin-bottom: var(--canon-space-10); + .sm\:bui-mb-10 { + margin-bottom: var(--bui-space-10); } - .sm\:cu-mb-11 { - margin-bottom: var(--canon-space-11); + .sm\:bui-mb-11 { + margin-bottom: var(--bui-space-11); } - .sm\:cu-mb-12 { - margin-bottom: var(--canon-space-12); + .sm\:bui-mb-12 { + margin-bottom: var(--bui-space-12); } - .sm\:cu-mb-13 { - margin-bottom: var(--canon-space-13); + .sm\:bui-mb-13 { + margin-bottom: var(--bui-space-13); } - .sm\:cu-mb-14 { - margin-bottom: var(--canon-space-14); + .sm\:bui-mb-14 { + margin-bottom: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-mb { + .md\:bui-mb { margin-bottom: var(--pb-md); } - .md\:cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); + .md\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } - .md\:cu-mb-1 { - margin-bottom: var(--canon-space-1); + .md\:bui-mb-1 { + margin-bottom: var(--bui-space-1); } - .md\:cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); + .md\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } - .md\:cu-mb-2 { - margin-bottom: var(--canon-space-2); + .md\:bui-mb-2 { + margin-bottom: var(--bui-space-2); } - .md\:cu-mb-3 { - margin-bottom: var(--canon-space-3); + .md\:bui-mb-3 { + margin-bottom: var(--bui-space-3); } - .md\:cu-mb-4 { - margin-bottom: var(--canon-space-4); + .md\:bui-mb-4 { + margin-bottom: var(--bui-space-4); } - .md\:cu-mb-5 { - margin-bottom: var(--canon-space-5); + .md\:bui-mb-5 { + margin-bottom: var(--bui-space-5); } - .md\:cu-mb-6 { - margin-bottom: var(--canon-space-6); + .md\:bui-mb-6 { + margin-bottom: var(--bui-space-6); } - .md\:cu-mb-7 { - margin-bottom: var(--canon-space-7); + .md\:bui-mb-7 { + margin-bottom: var(--bui-space-7); } - .md\:cu-mb-8 { - margin-bottom: var(--canon-space-8); + .md\:bui-mb-8 { + margin-bottom: var(--bui-space-8); } - .md\:cu-mb-9 { - margin-bottom: var(--canon-space-9); + .md\:bui-mb-9 { + margin-bottom: var(--bui-space-9); } - .md\:cu-mb-10 { - margin-bottom: var(--canon-space-10); + .md\:bui-mb-10 { + margin-bottom: var(--bui-space-10); } - .md\:cu-mb-11 { - margin-bottom: var(--canon-space-11); + .md\:bui-mb-11 { + margin-bottom: var(--bui-space-11); } - .md\:cu-mb-12 { - margin-bottom: var(--canon-space-12); + .md\:bui-mb-12 { + margin-bottom: var(--bui-space-12); } - .md\:cu-mb-13 { - margin-bottom: var(--canon-space-13); + .md\:bui-mb-13 { + margin-bottom: var(--bui-space-13); } - .md\:cu-mb-14 { - margin-bottom: var(--canon-space-14); + .md\:bui-mb-14 { + margin-bottom: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-mb { + .lg\:bui-mb { margin-bottom: var(--pb-lg); } - .lg\:cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); + .lg\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } - .lg\:cu-mb-1 { - margin-bottom: var(--canon-space-1); + .lg\:bui-mb-1 { + margin-bottom: var(--bui-space-1); } - .lg\:cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); + .lg\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } - .lg\:cu-mb-2 { - margin-bottom: var(--canon-space-2); + .lg\:bui-mb-2 { + margin-bottom: var(--bui-space-2); } - .lg\:cu-mb-3 { - margin-bottom: var(--canon-space-3); + .lg\:bui-mb-3 { + margin-bottom: var(--bui-space-3); } - .lg\:cu-mb-4 { - margin-bottom: var(--canon-space-4); + .lg\:bui-mb-4 { + margin-bottom: var(--bui-space-4); } - .lg\:cu-mb-5 { - margin-bottom: var(--canon-space-5); + .lg\:bui-mb-5 { + margin-bottom: var(--bui-space-5); } - .lg\:cu-mb-6 { - margin-bottom: var(--canon-space-6); + .lg\:bui-mb-6 { + margin-bottom: var(--bui-space-6); } - .lg\:cu-mb-7 { - margin-bottom: var(--canon-space-7); + .lg\:bui-mb-7 { + margin-bottom: var(--bui-space-7); } - .lg\:cu-mb-8 { - margin-bottom: var(--canon-space-8); + .lg\:bui-mb-8 { + margin-bottom: var(--bui-space-8); } - .lg\:cu-mb-9 { - margin-bottom: var(--canon-space-9); + .lg\:bui-mb-9 { + margin-bottom: var(--bui-space-9); } - .lg\:cu-mb-10 { - margin-bottom: var(--canon-space-10); + .lg\:bui-mb-10 { + margin-bottom: var(--bui-space-10); } - .lg\:cu-mb-11 { - margin-bottom: var(--canon-space-11); + .lg\:bui-mb-11 { + margin-bottom: var(--bui-space-11); } - .lg\:cu-mb-12 { - margin-bottom: var(--canon-space-12); + .lg\:bui-mb-12 { + margin-bottom: var(--bui-space-12); } - .lg\:cu-mb-13 { - margin-bottom: var(--canon-space-13); + .lg\:bui-mb-13 { + margin-bottom: var(--bui-space-13); } - .lg\:cu-mb-14 { - margin-bottom: var(--canon-space-14); + .lg\:bui-mb-14 { + margin-bottom: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-mb { + .xl\:bui-mb { margin-bottom: var(--pb-xl); } - .xl\:cu-mb-0\.5 { - margin-bottom: var(--canon-space-0_5); + .xl\:bui-mb-0\.5 { + margin-bottom: var(--bui-space-0_5); } - .xl\:cu-mb-1 { - margin-bottom: var(--canon-space-1); + .xl\:bui-mb-1 { + margin-bottom: var(--bui-space-1); } - .xl\:cu-mb-1\.5 { - margin-bottom: var(--canon-space-1_5); + .xl\:bui-mb-1\.5 { + margin-bottom: var(--bui-space-1_5); } - .xl\:cu-mb-2 { - margin-bottom: var(--canon-space-2); + .xl\:bui-mb-2 { + margin-bottom: var(--bui-space-2); } - .xl\:cu-mb-3 { - margin-bottom: var(--canon-space-3); + .xl\:bui-mb-3 { + margin-bottom: var(--bui-space-3); } - .xl\:cu-mb-4 { - margin-bottom: var(--canon-space-4); + .xl\:bui-mb-4 { + margin-bottom: var(--bui-space-4); } - .xl\:cu-mb-5 { - margin-bottom: var(--canon-space-5); + .xl\:bui-mb-5 { + margin-bottom: var(--bui-space-5); } - .xl\:cu-mb-6 { - margin-bottom: var(--canon-space-6); + .xl\:bui-mb-6 { + margin-bottom: var(--bui-space-6); } - .xl\:cu-mb-7 { - margin-bottom: var(--canon-space-7); + .xl\:bui-mb-7 { + margin-bottom: var(--bui-space-7); } - .xl\:cu-mb-8 { - margin-bottom: var(--canon-space-8); + .xl\:bui-mb-8 { + margin-bottom: var(--bui-space-8); } - .xl\:cu-mb-9 { - margin-bottom: var(--canon-space-9); + .xl\:bui-mb-9 { + margin-bottom: var(--bui-space-9); } - .xl\:cu-mb-10 { - margin-bottom: var(--canon-space-10); + .xl\:bui-mb-10 { + margin-bottom: var(--bui-space-10); } - .xl\:cu-mb-11 { - margin-bottom: var(--canon-space-11); + .xl\:bui-mb-11 { + margin-bottom: var(--bui-space-11); } - .xl\:cu-mb-12 { - margin-bottom: var(--canon-space-12); + .xl\:bui-mb-12 { + margin-bottom: var(--bui-space-12); } - .xl\:cu-mb-13 { - margin-bottom: var(--canon-space-13); + .xl\:bui-mb-13 { + margin-bottom: var(--bui-space-13); } - .xl\:cu-mb-14 { - margin-bottom: var(--canon-space-14); + .xl\:bui-mb-14 { + margin-bottom: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/ml.css b/packages/canon/src/css/utilities/ml.css index b495d1a8bf..b11fe779fb 100644 --- a/packages/canon/src/css/utilities/ml.css +++ b/packages/canon/src/css/utilities/ml.css @@ -1,422 +1,422 @@ -.cu-ml { +.bui-ml { margin-left: var(--ml); } -.cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); +.bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } -.cu-ml-1 { - margin-left: var(--canon-space-1); +.bui-ml-1 { + margin-left: var(--bui-space-1); } -.cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); +.bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } -.cu-ml-2 { - margin-left: var(--canon-space-2); +.bui-ml-2 { + margin-left: var(--bui-space-2); } -.cu-ml-3 { - margin-left: var(--canon-space-3); +.bui-ml-3 { + margin-left: var(--bui-space-3); } -.cu-ml-4 { - margin-left: var(--canon-space-4); +.bui-ml-4 { + margin-left: var(--bui-space-4); } -.cu-ml-5 { - margin-left: var(--canon-space-5); +.bui-ml-5 { + margin-left: var(--bui-space-5); } -.cu-ml-6 { - margin-left: var(--canon-space-6); +.bui-ml-6 { + margin-left: var(--bui-space-6); } -.cu-ml-7 { - margin-left: var(--canon-space-7); +.bui-ml-7 { + margin-left: var(--bui-space-7); } -.cu-ml-8 { - margin-left: var(--canon-space-8); +.bui-ml-8 { + margin-left: var(--bui-space-8); } -.cu-ml-9 { - margin-left: var(--canon-space-9); +.bui-ml-9 { + margin-left: var(--bui-space-9); } -.cu-ml-10 { - margin-left: var(--canon-space-10); +.bui-ml-10 { + margin-left: var(--bui-space-10); } -.cu-ml-11 { - margin-left: var(--canon-space-11); +.bui-ml-11 { + margin-left: var(--bui-space-11); } -.cu-ml-12 { - margin-left: var(--canon-space-12); +.bui-ml-12 { + margin-left: var(--bui-space-12); } -.cu-ml-13 { - margin-left: var(--canon-space-13); +.bui-ml-13 { + margin-left: var(--bui-space-13); } -.cu-ml-14 { - margin-left: var(--canon-space-14); +.bui-ml-14 { + margin-left: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-ml { + .xs\:bui-ml { margin-left: var(--pl-xs); } - .xs\:cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); + .xs\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } - .xs\:cu-ml-1 { - margin-left: var(--canon-space-1); + .xs\:bui-ml-1 { + margin-left: var(--bui-space-1); } - .xs\:cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); + .xs\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } - .xs\:cu-ml-2 { - margin-left: var(--canon-space-2); + .xs\:bui-ml-2 { + margin-left: var(--bui-space-2); } - .xs\:cu-ml-3 { - margin-left: var(--canon-space-3); + .xs\:bui-ml-3 { + margin-left: var(--bui-space-3); } - .xs\:cu-ml-4 { - margin-left: var(--canon-space-4); + .xs\:bui-ml-4 { + margin-left: var(--bui-space-4); } - .xs\:cu-ml-5 { - margin-left: var(--canon-space-5); + .xs\:bui-ml-5 { + margin-left: var(--bui-space-5); } - .xs\:cu-ml-6 { - margin-left: var(--canon-space-6); + .xs\:bui-ml-6 { + margin-left: var(--bui-space-6); } - .xs\:cu-ml-7 { - margin-left: var(--canon-space-7); + .xs\:bui-ml-7 { + margin-left: var(--bui-space-7); } - .xs\:cu-ml-8 { - margin-left: var(--canon-space-8); + .xs\:bui-ml-8 { + margin-left: var(--bui-space-8); } - .xs\:cu-ml-9 { - margin-left: var(--canon-space-9); + .xs\:bui-ml-9 { + margin-left: var(--bui-space-9); } - .xs\:cu-ml-10 { - margin-left: var(--canon-space-10); + .xs\:bui-ml-10 { + margin-left: var(--bui-space-10); } - .xs\:cu-ml-11 { - margin-left: var(--canon-space-11); + .xs\:bui-ml-11 { + margin-left: var(--bui-space-11); } - .xs\:cu-ml-12 { - margin-left: var(--canon-space-12); + .xs\:bui-ml-12 { + margin-left: var(--bui-space-12); } - .xs\:cu-ml-13 { - margin-left: var(--canon-space-13); + .xs\:bui-ml-13 { + margin-left: var(--bui-space-13); } - .xs\:cu-ml-14 { - margin-left: var(--canon-space-14); + .xs\:bui-ml-14 { + margin-left: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-ml { + .sm\:bui-ml { margin-left: var(--pl-sm); } - .sm\:cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); + .sm\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } - .sm\:cu-ml-1 { - margin-left: var(--canon-space-1); + .sm\:bui-ml-1 { + margin-left: var(--bui-space-1); } - .sm\:cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); + .sm\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } - .sm\:cu-ml-2 { - margin-left: var(--canon-space-2); + .sm\:bui-ml-2 { + margin-left: var(--bui-space-2); } - .sm\:cu-ml-3 { - margin-left: var(--canon-space-3); + .sm\:bui-ml-3 { + margin-left: var(--bui-space-3); } - .sm\:cu-ml-4 { - margin-left: var(--canon-space-4); + .sm\:bui-ml-4 { + margin-left: var(--bui-space-4); } - .sm\:cu-ml-5 { - margin-left: var(--canon-space-5); + .sm\:bui-ml-5 { + margin-left: var(--bui-space-5); } - .sm\:cu-ml-6 { - margin-left: var(--canon-space-6); + .sm\:bui-ml-6 { + margin-left: var(--bui-space-6); } - .sm\:cu-ml-7 { - margin-left: var(--canon-space-7); + .sm\:bui-ml-7 { + margin-left: var(--bui-space-7); } - .sm\:cu-ml-8 { - margin-left: var(--canon-space-8); + .sm\:bui-ml-8 { + margin-left: var(--bui-space-8); } - .sm\:cu-ml-9 { - margin-left: var(--canon-space-9); + .sm\:bui-ml-9 { + margin-left: var(--bui-space-9); } - .sm\:cu-ml-10 { - margin-left: var(--canon-space-10); + .sm\:bui-ml-10 { + margin-left: var(--bui-space-10); } - .sm\:cu-ml-11 { - margin-left: var(--canon-space-11); + .sm\:bui-ml-11 { + margin-left: var(--bui-space-11); } - .sm\:cu-ml-12 { - margin-left: var(--canon-space-12); + .sm\:bui-ml-12 { + margin-left: var(--bui-space-12); } - .sm\:cu-ml-13 { - margin-left: var(--canon-space-13); + .sm\:bui-ml-13 { + margin-left: var(--bui-space-13); } - .sm\:cu-ml-14 { - margin-left: var(--canon-space-14); + .sm\:bui-ml-14 { + margin-left: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-ml { + .md\:bui-ml { margin-left: var(--pl-md); } - .md\:cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); + .md\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } - .md\:cu-ml-1 { - margin-left: var(--canon-space-1); + .md\:bui-ml-1 { + margin-left: var(--bui-space-1); } - .md\:cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); + .md\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } - .md\:cu-ml-2 { - margin-left: var(--canon-space-2); + .md\:bui-ml-2 { + margin-left: var(--bui-space-2); } - .md\:cu-ml-3 { - margin-left: var(--canon-space-3); + .md\:bui-ml-3 { + margin-left: var(--bui-space-3); } - .md\:cu-ml-4 { - margin-left: var(--canon-space-4); + .md\:bui-ml-4 { + margin-left: var(--bui-space-4); } - .md\:cu-ml-5 { - margin-left: var(--canon-space-5); + .md\:bui-ml-5 { + margin-left: var(--bui-space-5); } - .md\:cu-ml-6 { - margin-left: var(--canon-space-6); + .md\:bui-ml-6 { + margin-left: var(--bui-space-6); } - .md\:cu-ml-7 { - margin-left: var(--canon-space-7); + .md\:bui-ml-7 { + margin-left: var(--bui-space-7); } - .md\:cu-ml-8 { - margin-left: var(--canon-space-8); + .md\:bui-ml-8 { + margin-left: var(--bui-space-8); } - .md\:cu-ml-9 { - margin-left: var(--canon-space-9); + .md\:bui-ml-9 { + margin-left: var(--bui-space-9); } - .md\:cu-ml-10 { - margin-left: var(--canon-space-10); + .md\:bui-ml-10 { + margin-left: var(--bui-space-10); } - .md\:cu-ml-11 { - margin-left: var(--canon-space-11); + .md\:bui-ml-11 { + margin-left: var(--bui-space-11); } - .md\:cu-ml-12 { - margin-left: var(--canon-space-12); + .md\:bui-ml-12 { + margin-left: var(--bui-space-12); } - .md\:cu-ml-13 { - margin-left: var(--canon-space-13); + .md\:bui-ml-13 { + margin-left: var(--bui-space-13); } - .md\:cu-ml-14 { - margin-left: var(--canon-space-14); + .md\:bui-ml-14 { + margin-left: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-ml { + .lg\:bui-ml { margin-left: var(--pl-lg); } - .lg\:cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); + .lg\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } - .lg\:cu-ml-1 { - margin-left: var(--canon-space-1); + .lg\:bui-ml-1 { + margin-left: var(--bui-space-1); } - .lg\:cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); + .lg\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } - .lg\:cu-ml-2 { - margin-left: var(--canon-space-2); + .lg\:bui-ml-2 { + margin-left: var(--bui-space-2); } - .lg\:cu-ml-3 { - margin-left: var(--canon-space-3); + .lg\:bui-ml-3 { + margin-left: var(--bui-space-3); } - .lg\:cu-ml-4 { - margin-left: var(--canon-space-4); + .lg\:bui-ml-4 { + margin-left: var(--bui-space-4); } - .lg\:cu-ml-5 { - margin-left: var(--canon-space-5); + .lg\:bui-ml-5 { + margin-left: var(--bui-space-5); } - .lg\:cu-ml-6 { - margin-left: var(--canon-space-6); + .lg\:bui-ml-6 { + margin-left: var(--bui-space-6); } - .lg\:cu-ml-7 { - margin-left: var(--canon-space-7); + .lg\:bui-ml-7 { + margin-left: var(--bui-space-7); } - .lg\:cu-ml-8 { - margin-left: var(--canon-space-8); + .lg\:bui-ml-8 { + margin-left: var(--bui-space-8); } - .lg\:cu-ml-9 { - margin-left: var(--canon-space-9); + .lg\:bui-ml-9 { + margin-left: var(--bui-space-9); } - .lg\:cu-ml-10 { - margin-left: var(--canon-space-10); + .lg\:bui-ml-10 { + margin-left: var(--bui-space-10); } - .lg\:cu-ml-11 { - margin-left: var(--canon-space-11); + .lg\:bui-ml-11 { + margin-left: var(--bui-space-11); } - .lg\:cu-ml-12 { - margin-left: var(--canon-space-12); + .lg\:bui-ml-12 { + margin-left: var(--bui-space-12); } - .lg\:cu-ml-13 { - margin-left: var(--canon-space-13); + .lg\:bui-ml-13 { + margin-left: var(--bui-space-13); } - .lg\:cu-ml-14 { - margin-left: var(--canon-space-14); + .lg\:bui-ml-14 { + margin-left: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-ml { + .xl\:bui-ml { margin-left: var(--pl-xl); } - .xl\:cu-ml-0\.5 { - margin-left: var(--canon-space-0_5); + .xl\:bui-ml-0\.5 { + margin-left: var(--bui-space-0_5); } - .xl\:cu-ml-1 { - margin-left: var(--canon-space-1); + .xl\:bui-ml-1 { + margin-left: var(--bui-space-1); } - .xl\:cu-ml-1\.5 { - margin-left: var(--canon-space-1_5); + .xl\:bui-ml-1\.5 { + margin-left: var(--bui-space-1_5); } - .xl\:cu-ml-2 { - margin-left: var(--canon-space-2); + .xl\:bui-ml-2 { + margin-left: var(--bui-space-2); } - .xl\:cu-ml-3 { - margin-left: var(--canon-space-3); + .xl\:bui-ml-3 { + margin-left: var(--bui-space-3); } - .xl\:cu-ml-4 { - margin-left: var(--canon-space-4); + .xl\:bui-ml-4 { + margin-left: var(--bui-space-4); } - .xl\:cu-ml-5 { - margin-left: var(--canon-space-5); + .xl\:bui-ml-5 { + margin-left: var(--bui-space-5); } - .xl\:cu-ml-6 { - margin-left: var(--canon-space-6); + .xl\:bui-ml-6 { + margin-left: var(--bui-space-6); } - .xl\:cu-ml-7 { - margin-left: var(--canon-space-7); + .xl\:bui-ml-7 { + margin-left: var(--bui-space-7); } - .xl\:cu-ml-8 { - margin-left: var(--canon-space-8); + .xl\:bui-ml-8 { + margin-left: var(--bui-space-8); } - .xl\:cu-ml-9 { - margin-left: var(--canon-space-9); + .xl\:bui-ml-9 { + margin-left: var(--bui-space-9); } - .xl\:cu-ml-10 { - margin-left: var(--canon-space-10); + .xl\:bui-ml-10 { + margin-left: var(--bui-space-10); } - .xl\:cu-ml-11 { - margin-left: var(--canon-space-11); + .xl\:bui-ml-11 { + margin-left: var(--bui-space-11); } - .xl\:cu-ml-12 { - margin-left: var(--canon-space-12); + .xl\:bui-ml-12 { + margin-left: var(--bui-space-12); } - .xl\:cu-ml-13 { - margin-left: var(--canon-space-13); + .xl\:bui-ml-13 { + margin-left: var(--bui-space-13); } - .xl\:cu-ml-14 { - margin-left: var(--canon-space-14); + .xl\:bui-ml-14 { + margin-left: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/mr.css b/packages/canon/src/css/utilities/mr.css index 9a179e1103..215a9dc88d 100644 --- a/packages/canon/src/css/utilities/mr.css +++ b/packages/canon/src/css/utilities/mr.css @@ -1,422 +1,422 @@ -.cu-mr { +.bui-mr { margin-right: var(--mr); } -.cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); +.bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } -.cu-mr-1 { - margin-right: var(--canon-space-1); +.bui-mr-1 { + margin-right: var(--bui-space-1); } -.cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); +.bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } -.cu-mr-2 { - margin-right: var(--canon-space-2); +.bui-mr-2 { + margin-right: var(--bui-space-2); } -.cu-mr-3 { - margin-right: var(--canon-space-3); +.bui-mr-3 { + margin-right: var(--bui-space-3); } -.cu-mr-4 { - margin-right: var(--canon-space-4); +.bui-mr-4 { + margin-right: var(--bui-space-4); } -.cu-mr-5 { - margin-right: var(--canon-space-5); +.bui-mr-5 { + margin-right: var(--bui-space-5); } -.cu-mr-6 { - margin-right: var(--canon-space-6); +.bui-mr-6 { + margin-right: var(--bui-space-6); } -.cu-mr-7 { - margin-right: var(--canon-space-7); +.bui-mr-7 { + margin-right: var(--bui-space-7); } -.cu-mr-8 { - margin-right: var(--canon-space-8); +.bui-mr-8 { + margin-right: var(--bui-space-8); } -.cu-mr-9 { - margin-right: var(--canon-space-9); +.bui-mr-9 { + margin-right: var(--bui-space-9); } -.cu-mr-10 { - margin-right: var(--canon-space-10); +.bui-mr-10 { + margin-right: var(--bui-space-10); } -.cu-mr-11 { - margin-right: var(--canon-space-11); +.bui-mr-11 { + margin-right: var(--bui-space-11); } -.cu-mr-12 { - margin-right: var(--canon-space-12); +.bui-mr-12 { + margin-right: var(--bui-space-12); } -.cu-mr-13 { - margin-right: var(--canon-space-13); +.bui-mr-13 { + margin-right: var(--bui-space-13); } -.cu-mr-14 { - margin-right: var(--canon-space-14); +.bui-mr-14 { + margin-right: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-mr { + .xs\:bui-mr { margin-right: var(--pr-xs); } - .xs\:cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); + .xs\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } - .xs\:cu-mr-1 { - margin-right: var(--canon-space-1); + .xs\:bui-mr-1 { + margin-right: var(--bui-space-1); } - .xs\:cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); + .xs\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } - .xs\:cu-mr-2 { - margin-right: var(--canon-space-2); + .xs\:bui-mr-2 { + margin-right: var(--bui-space-2); } - .xs\:cu-mr-3 { - margin-right: var(--canon-space-3); + .xs\:bui-mr-3 { + margin-right: var(--bui-space-3); } - .xs\:cu-mr-4 { - margin-right: var(--canon-space-4); + .xs\:bui-mr-4 { + margin-right: var(--bui-space-4); } - .xs\:cu-mr-5 { - margin-right: var(--canon-space-5); + .xs\:bui-mr-5 { + margin-right: var(--bui-space-5); } - .xs\:cu-mr-6 { - margin-right: var(--canon-space-6); + .xs\:bui-mr-6 { + margin-right: var(--bui-space-6); } - .xs\:cu-mr-7 { - margin-right: var(--canon-space-7); + .xs\:bui-mr-7 { + margin-right: var(--bui-space-7); } - .xs\:cu-mr-8 { - margin-right: var(--canon-space-8); + .xs\:bui-mr-8 { + margin-right: var(--bui-space-8); } - .xs\:cu-mr-9 { - margin-right: var(--canon-space-9); + .xs\:bui-mr-9 { + margin-right: var(--bui-space-9); } - .xs\:cu-mr-10 { - margin-right: var(--canon-space-10); + .xs\:bui-mr-10 { + margin-right: var(--bui-space-10); } - .xs\:cu-mr-11 { - margin-right: var(--canon-space-11); + .xs\:bui-mr-11 { + margin-right: var(--bui-space-11); } - .xs\:cu-mr-12 { - margin-right: var(--canon-space-12); + .xs\:bui-mr-12 { + margin-right: var(--bui-space-12); } - .xs\:cu-mr-13 { - margin-right: var(--canon-space-13); + .xs\:bui-mr-13 { + margin-right: var(--bui-space-13); } - .xs\:cu-mr-14 { - margin-right: var(--canon-space-14); + .xs\:bui-mr-14 { + margin-right: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-mr { + .sm\:bui-mr { margin-right: var(--pr-sm); } - .sm\:cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); + .sm\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } - .sm\:cu-mr-1 { - margin-right: var(--canon-space-1); + .sm\:bui-mr-1 { + margin-right: var(--bui-space-1); } - .sm\:cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); + .sm\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } - .sm\:cu-mr-2 { - margin-right: var(--canon-space-2); + .sm\:bui-mr-2 { + margin-right: var(--bui-space-2); } - .sm\:cu-mr-3 { - margin-right: var(--canon-space-3); + .sm\:bui-mr-3 { + margin-right: var(--bui-space-3); } - .sm\:cu-mr-4 { - margin-right: var(--canon-space-4); + .sm\:bui-mr-4 { + margin-right: var(--bui-space-4); } - .sm\:cu-mr-5 { - margin-right: var(--canon-space-5); + .sm\:bui-mr-5 { + margin-right: var(--bui-space-5); } - .sm\:cu-mr-6 { - margin-right: var(--canon-space-6); + .sm\:bui-mr-6 { + margin-right: var(--bui-space-6); } - .sm\:cu-mr-7 { - margin-right: var(--canon-space-7); + .sm\:bui-mr-7 { + margin-right: var(--bui-space-7); } - .sm\:cu-mr-8 { - margin-right: var(--canon-space-8); + .sm\:bui-mr-8 { + margin-right: var(--bui-space-8); } - .sm\:cu-mr-9 { - margin-right: var(--canon-space-9); + .sm\:bui-mr-9 { + margin-right: var(--bui-space-9); } - .sm\:cu-mr-10 { - margin-right: var(--canon-space-10); + .sm\:bui-mr-10 { + margin-right: var(--bui-space-10); } - .sm\:cu-mr-11 { - margin-right: var(--canon-space-11); + .sm\:bui-mr-11 { + margin-right: var(--bui-space-11); } - .sm\:cu-mr-12 { - margin-right: var(--canon-space-12); + .sm\:bui-mr-12 { + margin-right: var(--bui-space-12); } - .sm\:cu-mr-13 { - margin-right: var(--canon-space-13); + .sm\:bui-mr-13 { + margin-right: var(--bui-space-13); } - .sm\:cu-mr-14 { - margin-right: var(--canon-space-14); + .sm\:bui-mr-14 { + margin-right: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-mr { + .md\:bui-mr { margin-right: var(--pr-md); } - .md\:cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); + .md\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } - .md\:cu-mr-1 { - margin-right: var(--canon-space-1); + .md\:bui-mr-1 { + margin-right: var(--bui-space-1); } - .md\:cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); + .md\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } - .md\:cu-mr-2 { - margin-right: var(--canon-space-2); + .md\:bui-mr-2 { + margin-right: var(--bui-space-2); } - .md\:cu-mr-3 { - margin-right: var(--canon-space-3); + .md\:bui-mr-3 { + margin-right: var(--bui-space-3); } - .md\:cu-mr-4 { - margin-right: var(--canon-space-4); + .md\:bui-mr-4 { + margin-right: var(--bui-space-4); } - .md\:cu-mr-5 { - margin-right: var(--canon-space-5); + .md\:bui-mr-5 { + margin-right: var(--bui-space-5); } - .md\:cu-mr-6 { - margin-right: var(--canon-space-6); + .md\:bui-mr-6 { + margin-right: var(--bui-space-6); } - .md\:cu-mr-7 { - margin-right: var(--canon-space-7); + .md\:bui-mr-7 { + margin-right: var(--bui-space-7); } - .md\:cu-mr-8 { - margin-right: var(--canon-space-8); + .md\:bui-mr-8 { + margin-right: var(--bui-space-8); } - .md\:cu-mr-9 { - margin-right: var(--canon-space-9); + .md\:bui-mr-9 { + margin-right: var(--bui-space-9); } - .md\:cu-mr-10 { - margin-right: var(--canon-space-10); + .md\:bui-mr-10 { + margin-right: var(--bui-space-10); } - .md\:cu-mr-11 { - margin-right: var(--canon-space-11); + .md\:bui-mr-11 { + margin-right: var(--bui-space-11); } - .md\:cu-mr-12 { - margin-right: var(--canon-space-12); + .md\:bui-mr-12 { + margin-right: var(--bui-space-12); } - .md\:cu-mr-13 { - margin-right: var(--canon-space-13); + .md\:bui-mr-13 { + margin-right: var(--bui-space-13); } - .md\:cu-mr-14 { - margin-right: var(--canon-space-14); + .md\:bui-mr-14 { + margin-right: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-mr { + .lg\:bui-mr { margin-right: var(--pr-lg); } - .lg\:cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); + .lg\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } - .lg\:cu-mr-1 { - margin-right: var(--canon-space-1); + .lg\:bui-mr-1 { + margin-right: var(--bui-space-1); } - .lg\:cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); + .lg\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } - .lg\:cu-mr-2 { - margin-right: var(--canon-space-2); + .lg\:bui-mr-2 { + margin-right: var(--bui-space-2); } - .lg\:cu-mr-3 { - margin-right: var(--canon-space-3); + .lg\:bui-mr-3 { + margin-right: var(--bui-space-3); } - .lg\:cu-mr-4 { - margin-right: var(--canon-space-4); + .lg\:bui-mr-4 { + margin-right: var(--bui-space-4); } - .lg\:cu-mr-5 { - margin-right: var(--canon-space-5); + .lg\:bui-mr-5 { + margin-right: var(--bui-space-5); } - .lg\:cu-mr-6 { - margin-right: var(--canon-space-6); + .lg\:bui-mr-6 { + margin-right: var(--bui-space-6); } - .lg\:cu-mr-7 { - margin-right: var(--canon-space-7); + .lg\:bui-mr-7 { + margin-right: var(--bui-space-7); } - .lg\:cu-mr-8 { - margin-right: var(--canon-space-8); + .lg\:bui-mr-8 { + margin-right: var(--bui-space-8); } - .lg\:cu-mr-9 { - margin-right: var(--canon-space-9); + .lg\:bui-mr-9 { + margin-right: var(--bui-space-9); } - .lg\:cu-mr-10 { - margin-right: var(--canon-space-10); + .lg\:bui-mr-10 { + margin-right: var(--bui-space-10); } - .lg\:cu-mr-11 { - margin-right: var(--canon-space-11); + .lg\:bui-mr-11 { + margin-right: var(--bui-space-11); } - .lg\:cu-mr-12 { - margin-right: var(--canon-space-12); + .lg\:bui-mr-12 { + margin-right: var(--bui-space-12); } - .lg\:cu-mr-13 { - margin-right: var(--canon-space-13); + .lg\:bui-mr-13 { + margin-right: var(--bui-space-13); } - .lg\:cu-mr-14 { - margin-right: var(--canon-space-14); + .lg\:bui-mr-14 { + margin-right: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-mr { + .xl\:bui-mr { margin-right: var(--pr-xl); } - .xl\:cu-mr-0\.5 { - margin-right: var(--canon-space-0_5); + .xl\:bui-mr-0\.5 { + margin-right: var(--bui-space-0_5); } - .xl\:cu-mr-1 { - margin-right: var(--canon-space-1); + .xl\:bui-mr-1 { + margin-right: var(--bui-space-1); } - .xl\:cu-mr-1\.5 { - margin-right: var(--canon-space-1_5); + .xl\:bui-mr-1\.5 { + margin-right: var(--bui-space-1_5); } - .xl\:cu-mr-2 { - margin-right: var(--canon-space-2); + .xl\:bui-mr-2 { + margin-right: var(--bui-space-2); } - .xl\:cu-mr-3 { - margin-right: var(--canon-space-3); + .xl\:bui-mr-3 { + margin-right: var(--bui-space-3); } - .xl\:cu-mr-4 { - margin-right: var(--canon-space-4); + .xl\:bui-mr-4 { + margin-right: var(--bui-space-4); } - .xl\:cu-mr-5 { - margin-right: var(--canon-space-5); + .xl\:bui-mr-5 { + margin-right: var(--bui-space-5); } - .xl\:cu-mr-6 { - margin-right: var(--canon-space-6); + .xl\:bui-mr-6 { + margin-right: var(--bui-space-6); } - .xl\:cu-mr-7 { - margin-right: var(--canon-space-7); + .xl\:bui-mr-7 { + margin-right: var(--bui-space-7); } - .xl\:cu-mr-8 { - margin-right: var(--canon-space-8); + .xl\:bui-mr-8 { + margin-right: var(--bui-space-8); } - .xl\:cu-mr-9 { - margin-right: var(--canon-space-9); + .xl\:bui-mr-9 { + margin-right: var(--bui-space-9); } - .xl\:cu-mr-10 { - margin-right: var(--canon-space-10); + .xl\:bui-mr-10 { + margin-right: var(--bui-space-10); } - .xl\:cu-mr-11 { - margin-right: var(--canon-space-11); + .xl\:bui-mr-11 { + margin-right: var(--bui-space-11); } - .xl\:cu-mr-12 { - margin-right: var(--canon-space-12); + .xl\:bui-mr-12 { + margin-right: var(--bui-space-12); } - .xl\:cu-mr-13 { - margin-right: var(--canon-space-13); + .xl\:bui-mr-13 { + margin-right: var(--bui-space-13); } - .xl\:cu-mr-14 { - margin-right: var(--canon-space-14); + .xl\:bui-mr-14 { + margin-right: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/mt.css b/packages/canon/src/css/utilities/mt.css index 2e0815946d..30035bf8bd 100644 --- a/packages/canon/src/css/utilities/mt.css +++ b/packages/canon/src/css/utilities/mt.css @@ -1,422 +1,422 @@ -.cu-mt { +.bui-mt { margin-top: var(--mt); } -.cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); +.bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } -.cu-mt-1 { - margin-top: var(--canon-space-1); +.bui-mt-1 { + margin-top: var(--bui-space-1); } -.cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); +.bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } -.cu-mt-2 { - margin-top: var(--canon-space-2); +.bui-mt-2 { + margin-top: var(--bui-space-2); } -.cu-mt-3 { - margin-top: var(--canon-space-3); +.bui-mt-3 { + margin-top: var(--bui-space-3); } -.cu-mt-4 { - margin-top: var(--canon-space-4); +.bui-mt-4 { + margin-top: var(--bui-space-4); } -.cu-mt-5 { - margin-top: var(--canon-space-5); +.bui-mt-5 { + margin-top: var(--bui-space-5); } -.cu-mt-6 { - margin-top: var(--canon-space-6); +.bui-mt-6 { + margin-top: var(--bui-space-6); } -.cu-mt-7 { - margin-top: var(--canon-space-7); +.bui-mt-7 { + margin-top: var(--bui-space-7); } -.cu-mt-8 { - margin-top: var(--canon-space-8); +.bui-mt-8 { + margin-top: var(--bui-space-8); } -.cu-mt-9 { - margin-top: var(--canon-space-9); +.bui-mt-9 { + margin-top: var(--bui-space-9); } -.cu-mt-10 { - margin-top: var(--canon-space-10); +.bui-mt-10 { + margin-top: var(--bui-space-10); } -.cu-mt-11 { - margin-top: var(--canon-space-11); +.bui-mt-11 { + margin-top: var(--bui-space-11); } -.cu-mt-12 { - margin-top: var(--canon-space-12); +.bui-mt-12 { + margin-top: var(--bui-space-12); } -.cu-mt-13 { - margin-top: var(--canon-space-13); +.bui-mt-13 { + margin-top: var(--bui-space-13); } -.cu-mt-14 { - margin-top: var(--canon-space-14); +.bui-mt-14 { + margin-top: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-mt { + .xs\:bui-mt { margin-top: var(--pt-xs); } - .xs\:cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); + .xs\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } - .xs\:cu-mt-1 { - margin-top: var(--canon-space-1); + .xs\:bui-mt-1 { + margin-top: var(--bui-space-1); } - .xs\:cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); + .xs\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } - .xs\:cu-mt-2 { - margin-top: var(--canon-space-2); + .xs\:bui-mt-2 { + margin-top: var(--bui-space-2); } - .xs\:cu-mt-3 { - margin-top: var(--canon-space-3); + .xs\:bui-mt-3 { + margin-top: var(--bui-space-3); } - .xs\:cu-mt-4 { - margin-top: var(--canon-space-4); + .xs\:bui-mt-4 { + margin-top: var(--bui-space-4); } - .xs\:cu-mt-5 { - margin-top: var(--canon-space-5); + .xs\:bui-mt-5 { + margin-top: var(--bui-space-5); } - .xs\:cu-mt-6 { - margin-top: var(--canon-space-6); + .xs\:bui-mt-6 { + margin-top: var(--bui-space-6); } - .xs\:cu-mt-7 { - margin-top: var(--canon-space-7); + .xs\:bui-mt-7 { + margin-top: var(--bui-space-7); } - .xs\:cu-mt-8 { - margin-top: var(--canon-space-8); + .xs\:bui-mt-8 { + margin-top: var(--bui-space-8); } - .xs\:cu-mt-9 { - margin-top: var(--canon-space-9); + .xs\:bui-mt-9 { + margin-top: var(--bui-space-9); } - .xs\:cu-mt-10 { - margin-top: var(--canon-space-10); + .xs\:bui-mt-10 { + margin-top: var(--bui-space-10); } - .xs\:cu-mt-11 { - margin-top: var(--canon-space-11); + .xs\:bui-mt-11 { + margin-top: var(--bui-space-11); } - .xs\:cu-mt-12 { - margin-top: var(--canon-space-12); + .xs\:bui-mt-12 { + margin-top: var(--bui-space-12); } - .xs\:cu-mt-13 { - margin-top: var(--canon-space-13); + .xs\:bui-mt-13 { + margin-top: var(--bui-space-13); } - .xs\:cu-mt-14 { - margin-top: var(--canon-space-14); + .xs\:bui-mt-14 { + margin-top: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-mt { + .sm\:bui-mt { margin-top: var(--pt-sm); } - .sm\:cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); + .sm\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } - .sm\:cu-mt-1 { - margin-top: var(--canon-space-1); + .sm\:bui-mt-1 { + margin-top: var(--bui-space-1); } - .sm\:cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); + .sm\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } - .sm\:cu-mt-2 { - margin-top: var(--canon-space-2); + .sm\:bui-mt-2 { + margin-top: var(--bui-space-2); } - .sm\:cu-mt-3 { - margin-top: var(--canon-space-3); + .sm\:bui-mt-3 { + margin-top: var(--bui-space-3); } - .sm\:cu-mt-4 { - margin-top: var(--canon-space-4); + .sm\:bui-mt-4 { + margin-top: var(--bui-space-4); } - .sm\:cu-mt-5 { - margin-top: var(--canon-space-5); + .sm\:bui-mt-5 { + margin-top: var(--bui-space-5); } - .sm\:cu-mt-6 { - margin-top: var(--canon-space-6); + .sm\:bui-mt-6 { + margin-top: var(--bui-space-6); } - .sm\:cu-mt-7 { - margin-top: var(--canon-space-7); + .sm\:bui-mt-7 { + margin-top: var(--bui-space-7); } - .sm\:cu-mt-8 { - margin-top: var(--canon-space-8); + .sm\:bui-mt-8 { + margin-top: var(--bui-space-8); } - .sm\:cu-mt-9 { - margin-top: var(--canon-space-9); + .sm\:bui-mt-9 { + margin-top: var(--bui-space-9); } - .sm\:cu-mt-10 { - margin-top: var(--canon-space-10); + .sm\:bui-mt-10 { + margin-top: var(--bui-space-10); } - .sm\:cu-mt-11 { - margin-top: var(--canon-space-11); + .sm\:bui-mt-11 { + margin-top: var(--bui-space-11); } - .sm\:cu-mt-12 { - margin-top: var(--canon-space-12); + .sm\:bui-mt-12 { + margin-top: var(--bui-space-12); } - .sm\:cu-mt-13 { - margin-top: var(--canon-space-13); + .sm\:bui-mt-13 { + margin-top: var(--bui-space-13); } - .sm\:cu-mt-14 { - margin-top: var(--canon-space-14); + .sm\:bui-mt-14 { + margin-top: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-mt { + .md\:bui-mt { margin-top: var(--pt-md); } - .md\:cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); + .md\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } - .md\:cu-mt-1 { - margin-top: var(--canon-space-1); + .md\:bui-mt-1 { + margin-top: var(--bui-space-1); } - .md\:cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); + .md\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } - .md\:cu-mt-2 { - margin-top: var(--canon-space-2); + .md\:bui-mt-2 { + margin-top: var(--bui-space-2); } - .md\:cu-mt-3 { - margin-top: var(--canon-space-3); + .md\:bui-mt-3 { + margin-top: var(--bui-space-3); } - .md\:cu-mt-4 { - margin-top: var(--canon-space-4); + .md\:bui-mt-4 { + margin-top: var(--bui-space-4); } - .md\:cu-mt-5 { - margin-top: var(--canon-space-5); + .md\:bui-mt-5 { + margin-top: var(--bui-space-5); } - .md\:cu-mt-6 { - margin-top: var(--canon-space-6); + .md\:bui-mt-6 { + margin-top: var(--bui-space-6); } - .md\:cu-mt-7 { - margin-top: var(--canon-space-7); + .md\:bui-mt-7 { + margin-top: var(--bui-space-7); } - .md\:cu-mt-8 { - margin-top: var(--canon-space-8); + .md\:bui-mt-8 { + margin-top: var(--bui-space-8); } - .md\:cu-mt-9 { - margin-top: var(--canon-space-9); + .md\:bui-mt-9 { + margin-top: var(--bui-space-9); } - .md\:cu-mt-10 { - margin-top: var(--canon-space-10); + .md\:bui-mt-10 { + margin-top: var(--bui-space-10); } - .md\:cu-mt-11 { - margin-top: var(--canon-space-11); + .md\:bui-mt-11 { + margin-top: var(--bui-space-11); } - .md\:cu-mt-12 { - margin-top: var(--canon-space-12); + .md\:bui-mt-12 { + margin-top: var(--bui-space-12); } - .md\:cu-mt-13 { - margin-top: var(--canon-space-13); + .md\:bui-mt-13 { + margin-top: var(--bui-space-13); } - .md\:cu-mt-14 { - margin-top: var(--canon-space-14); + .md\:bui-mt-14 { + margin-top: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-mt { + .lg\:bui-mt { margin-top: var(--pt-lg); } - .lg\:cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); + .lg\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } - .lg\:cu-mt-1 { - margin-top: var(--canon-space-1); + .lg\:bui-mt-1 { + margin-top: var(--bui-space-1); } - .lg\:cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); + .lg\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } - .lg\:cu-mt-2 { - margin-top: var(--canon-space-2); + .lg\:bui-mt-2 { + margin-top: var(--bui-space-2); } - .lg\:cu-mt-3 { - margin-top: var(--canon-space-3); + .lg\:bui-mt-3 { + margin-top: var(--bui-space-3); } - .lg\:cu-mt-4 { - margin-top: var(--canon-space-4); + .lg\:bui-mt-4 { + margin-top: var(--bui-space-4); } - .lg\:cu-mt-5 { - margin-top: var(--canon-space-5); + .lg\:bui-mt-5 { + margin-top: var(--bui-space-5); } - .lg\:cu-mt-6 { - margin-top: var(--canon-space-6); + .lg\:bui-mt-6 { + margin-top: var(--bui-space-6); } - .lg\:cu-mt-7 { - margin-top: var(--canon-space-7); + .lg\:bui-mt-7 { + margin-top: var(--bui-space-7); } - .lg\:cu-mt-8 { - margin-top: var(--canon-space-8); + .lg\:bui-mt-8 { + margin-top: var(--bui-space-8); } - .lg\:cu-mt-9 { - margin-top: var(--canon-space-9); + .lg\:bui-mt-9 { + margin-top: var(--bui-space-9); } - .lg\:cu-mt-10 { - margin-top: var(--canon-space-10); + .lg\:bui-mt-10 { + margin-top: var(--bui-space-10); } - .lg\:cu-mt-11 { - margin-top: var(--canon-space-11); + .lg\:bui-mt-11 { + margin-top: var(--bui-space-11); } - .lg\:cu-mt-12 { - margin-top: var(--canon-space-12); + .lg\:bui-mt-12 { + margin-top: var(--bui-space-12); } - .lg\:cu-mt-13 { - margin-top: var(--canon-space-13); + .lg\:bui-mt-13 { + margin-top: var(--bui-space-13); } - .lg\:cu-mt-14 { - margin-top: var(--canon-space-14); + .lg\:bui-mt-14 { + margin-top: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-mt { + .xl\:bui-mt { margin-top: var(--pt-xl); } - .xl\:cu-mt-0\.5 { - margin-top: var(--canon-space-0_5); + .xl\:bui-mt-0\.5 { + margin-top: var(--bui-space-0_5); } - .xl\:cu-mt-1 { - margin-top: var(--canon-space-1); + .xl\:bui-mt-1 { + margin-top: var(--bui-space-1); } - .xl\:cu-mt-1\.5 { - margin-top: var(--canon-space-1_5); + .xl\:bui-mt-1\.5 { + margin-top: var(--bui-space-1_5); } - .xl\:cu-mt-2 { - margin-top: var(--canon-space-2); + .xl\:bui-mt-2 { + margin-top: var(--bui-space-2); } - .xl\:cu-mt-3 { - margin-top: var(--canon-space-3); + .xl\:bui-mt-3 { + margin-top: var(--bui-space-3); } - .xl\:cu-mt-4 { - margin-top: var(--canon-space-4); + .xl\:bui-mt-4 { + margin-top: var(--bui-space-4); } - .xl\:cu-mt-5 { - margin-top: var(--canon-space-5); + .xl\:bui-mt-5 { + margin-top: var(--bui-space-5); } - .xl\:cu-mt-6 { - margin-top: var(--canon-space-6); + .xl\:bui-mt-6 { + margin-top: var(--bui-space-6); } - .xl\:cu-mt-7 { - margin-top: var(--canon-space-7); + .xl\:bui-mt-7 { + margin-top: var(--bui-space-7); } - .xl\:cu-mt-8 { - margin-top: var(--canon-space-8); + .xl\:bui-mt-8 { + margin-top: var(--bui-space-8); } - .xl\:cu-mt-9 { - margin-top: var(--canon-space-9); + .xl\:bui-mt-9 { + margin-top: var(--bui-space-9); } - .xl\:cu-mt-10 { - margin-top: var(--canon-space-10); + .xl\:bui-mt-10 { + margin-top: var(--bui-space-10); } - .xl\:cu-mt-11 { - margin-top: var(--canon-space-11); + .xl\:bui-mt-11 { + margin-top: var(--bui-space-11); } - .xl\:cu-mt-12 { - margin-top: var(--canon-space-12); + .xl\:bui-mt-12 { + margin-top: var(--bui-space-12); } - .xl\:cu-mt-13 { - margin-top: var(--canon-space-13); + .xl\:bui-mt-13 { + margin-top: var(--bui-space-13); } - .xl\:cu-mt-14 { - margin-top: var(--canon-space-14); + .xl\:bui-mt-14 { + margin-top: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/mx.css b/packages/canon/src/css/utilities/mx.css index 43f5838e4e..36bdf32796 100644 --- a/packages/canon/src/css/utilities/mx.css +++ b/packages/canon/src/css/utilities/mx.css @@ -1,524 +1,524 @@ -.cu-mx { +.bui-mx { margin-left: var(--mx); margin-right: var(--mx); } -.cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); +.bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } -.cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); +.bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } -.cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); +.bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } -.cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); +.bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } -.cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); +.bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } -.cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); +.bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } -.cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); +.bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } -.cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); +.bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } -.cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); +.bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } -.cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); +.bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } -.cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); +.bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } -.cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); +.bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } -.cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); +.bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } -.cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); +.bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } -.cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); +.bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } -.cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); +.bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-mx { + .xs\:bui-mx { margin-left: var(--px-xs); margin-right: var(--px-xs); } - .xs\:cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); + .xs\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } - .xs\:cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); + .xs\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } - .xs\:cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); + .xs\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } - .xs\:cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); + .xs\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } - .xs\:cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); + .xs\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } - .xs\:cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); + .xs\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } - .xs\:cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); + .xs\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } - .xs\:cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); + .xs\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } - .xs\:cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); + .xs\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } - .xs\:cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); + .xs\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } - .xs\:cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); + .xs\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } - .xs\:cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); + .xs\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } - .xs\:cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); + .xs\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } - .xs\:cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); + .xs\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } - .xs\:cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); + .xs\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } - .xs\:cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); + .xs\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-mx { + .sm\:bui-mx { margin-left: var(--px-sm); margin-right: var(--px-sm); } - .sm\:cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); + .sm\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } - .sm\:cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); + .sm\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } - .sm\:cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); + .sm\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } - .sm\:cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); + .sm\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } - .sm\:cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); + .sm\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } - .sm\:cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); + .sm\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } - .sm\:cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); + .sm\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } - .sm\:cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); + .sm\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } - .sm\:cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); + .sm\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } - .sm\:cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); + .sm\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } - .sm\:cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); + .sm\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } - .sm\:cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); + .sm\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } - .sm\:cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); + .sm\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } - .sm\:cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); + .sm\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } - .sm\:cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); + .sm\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } - .sm\:cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); + .sm\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-mx { + .md\:bui-mx { margin-left: var(--px-md); margin-right: var(--px-md); } - .md\:cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); + .md\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } - .md\:cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); + .md\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } - .md\:cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); + .md\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } - .md\:cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); + .md\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } - .md\:cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); + .md\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } - .md\:cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); + .md\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } - .md\:cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); + .md\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } - .md\:cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); + .md\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } - .md\:cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); + .md\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } - .md\:cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); + .md\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } - .md\:cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); + .md\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } - .md\:cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); + .md\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } - .md\:cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); + .md\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } - .md\:cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); + .md\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } - .md\:cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); + .md\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } - .md\:cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); + .md\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-mx { + .lg\:bui-mx { margin-left: var(--px-lg); margin-right: var(--px-lg); } - .lg\:cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); + .lg\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } - .lg\:cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); + .lg\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } - .lg\:cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); + .lg\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } - .lg\:cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); + .lg\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } - .lg\:cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); + .lg\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } - .lg\:cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); + .lg\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } - .lg\:cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); + .lg\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } - .lg\:cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); + .lg\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } - .lg\:cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); + .lg\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } - .lg\:cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); + .lg\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } - .lg\:cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); + .lg\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } - .lg\:cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); + .lg\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } - .lg\:cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); + .lg\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } - .lg\:cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); + .lg\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } - .lg\:cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); + .lg\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } - .lg\:cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); + .lg\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-mx { + .xl\:bui-mx { margin-left: var(--px-xl); margin-right: var(--px-xl); } - .xl\:cu-mx-0\.5 { - margin-left: var(--canon-space-0_5); - margin-right: var(--canon-space-0_5); + .xl\:bui-mx-0\.5 { + margin-left: var(--bui-space-0_5); + margin-right: var(--bui-space-0_5); } - .xl\:cu-mx-1 { - margin-left: var(--canon-space-1); - margin-right: var(--canon-space-1); + .xl\:bui-mx-1 { + margin-left: var(--bui-space-1); + margin-right: var(--bui-space-1); } - .xl\:cu-mx-1\.5 { - margin-left: var(--canon-space-1_5); - margin-right: var(--canon-space-1_5); + .xl\:bui-mx-1\.5 { + margin-left: var(--bui-space-1_5); + margin-right: var(--bui-space-1_5); } - .xl\:cu-mx-2 { - margin-left: var(--canon-space-2); - margin-right: var(--canon-space-2); + .xl\:bui-mx-2 { + margin-left: var(--bui-space-2); + margin-right: var(--bui-space-2); } - .xl\:cu-mx-3 { - margin-left: var(--canon-space-3); - margin-right: var(--canon-space-3); + .xl\:bui-mx-3 { + margin-left: var(--bui-space-3); + margin-right: var(--bui-space-3); } - .xl\:cu-mx-4 { - margin-left: var(--canon-space-4); - margin-right: var(--canon-space-4); + .xl\:bui-mx-4 { + margin-left: var(--bui-space-4); + margin-right: var(--bui-space-4); } - .xl\:cu-mx-5 { - margin-left: var(--canon-space-5); - margin-right: var(--canon-space-5); + .xl\:bui-mx-5 { + margin-left: var(--bui-space-5); + margin-right: var(--bui-space-5); } - .xl\:cu-mx-6 { - margin-left: var(--canon-space-6); - margin-right: var(--canon-space-6); + .xl\:bui-mx-6 { + margin-left: var(--bui-space-6); + margin-right: var(--bui-space-6); } - .xl\:cu-mx-7 { - margin-left: var(--canon-space-7); - margin-right: var(--canon-space-7); + .xl\:bui-mx-7 { + margin-left: var(--bui-space-7); + margin-right: var(--bui-space-7); } - .xl\:cu-mx-8 { - margin-left: var(--canon-space-8); - margin-right: var(--canon-space-8); + .xl\:bui-mx-8 { + margin-left: var(--bui-space-8); + margin-right: var(--bui-space-8); } - .xl\:cu-mx-9 { - margin-left: var(--canon-space-9); - margin-right: var(--canon-space-9); + .xl\:bui-mx-9 { + margin-left: var(--bui-space-9); + margin-right: var(--bui-space-9); } - .xl\:cu-mx-10 { - margin-left: var(--canon-space-10); - margin-right: var(--canon-space-10); + .xl\:bui-mx-10 { + margin-left: var(--bui-space-10); + margin-right: var(--bui-space-10); } - .xl\:cu-mx-11 { - margin-left: var(--canon-space-11); - margin-right: var(--canon-space-11); + .xl\:bui-mx-11 { + margin-left: var(--bui-space-11); + margin-right: var(--bui-space-11); } - .xl\:cu-mx-12 { - margin-left: var(--canon-space-12); - margin-right: var(--canon-space-12); + .xl\:bui-mx-12 { + margin-left: var(--bui-space-12); + margin-right: var(--bui-space-12); } - .xl\:cu-mx-13 { - margin-left: var(--canon-space-13); - margin-right: var(--canon-space-13); + .xl\:bui-mx-13 { + margin-left: var(--bui-space-13); + margin-right: var(--bui-space-13); } - .xl\:cu-mx-14 { - margin-left: var(--canon-space-14); - margin-right: var(--canon-space-14); + .xl\:bui-mx-14 { + margin-left: var(--bui-space-14); + margin-right: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/my.css b/packages/canon/src/css/utilities/my.css index ae72ea9209..ab3aadf77e 100644 --- a/packages/canon/src/css/utilities/my.css +++ b/packages/canon/src/css/utilities/my.css @@ -1,524 +1,524 @@ -.cu-my { +.bui-my { margin-top: var(--my); margin-bottom: var(--my); } -.cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); +.bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } -.cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); +.bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } -.cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); +.bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } -.cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); +.bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } -.cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); +.bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } -.cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); +.bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } -.cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); +.bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } -.cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); +.bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } -.cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); +.bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } -.cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); +.bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } -.cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); +.bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } -.cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); +.bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } -.cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); +.bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } -.cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); +.bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } -.cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); +.bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } -.cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); +.bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-my { + .xs\:bui-my { margin-top: var(--py-xs); margin-bottom: var(--py-xs); } - .xs\:cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); + .xs\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } - .xs\:cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); + .xs\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } - .xs\:cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); + .xs\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } - .xs\:cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); + .xs\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } - .xs\:cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); + .xs\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } - .xs\:cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); + .xs\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } - .xs\:cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); + .xs\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } - .xs\:cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); + .xs\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } - .xs\:cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); + .xs\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } - .xs\:cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); + .xs\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } - .xs\:cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); + .xs\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } - .xs\:cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); + .xs\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } - .xs\:cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); + .xs\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } - .xs\:cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); + .xs\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } - .xs\:cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); + .xs\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } - .xs\:cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); + .xs\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-my { + .sm\:bui-my { margin-top: var(--py-sm); margin-bottom: var(--py-sm); } - .sm\:cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); + .sm\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } - .sm\:cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); + .sm\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } - .sm\:cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); + .sm\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } - .sm\:cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); + .sm\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } - .sm\:cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); + .sm\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } - .sm\:cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); + .sm\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } - .sm\:cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); + .sm\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } - .sm\:cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); + .sm\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } - .sm\:cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); + .sm\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } - .sm\:cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); + .sm\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } - .sm\:cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); + .sm\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } - .sm\:cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); + .sm\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } - .sm\:cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); + .sm\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } - .sm\:cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); + .sm\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } - .sm\:cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); + .sm\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } - .sm\:cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); + .sm\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-my { + .md\:bui-my { margin-top: var(--py-md); margin-bottom: var(--py-md); } - .md\:cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); + .md\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } - .md\:cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); + .md\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } - .md\:cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); + .md\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } - .md\:cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); + .md\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } - .md\:cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); + .md\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } - .md\:cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); + .md\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } - .md\:cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); + .md\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } - .md\:cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); + .md\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } - .md\:cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); + .md\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } - .md\:cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); + .md\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } - .md\:cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); + .md\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } - .md\:cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); + .md\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } - .md\:cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); + .md\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } - .md\:cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); + .md\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } - .md\:cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); + .md\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } - .md\:cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); + .md\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-my { + .lg\:bui-my { margin-top: var(--py-lg); margin-bottom: var(--py-lg); } - .lg\:cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); + .lg\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } - .lg\:cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); + .lg\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } - .lg\:cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); + .lg\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } - .lg\:cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); + .lg\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } - .lg\:cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); + .lg\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } - .lg\:cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); + .lg\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } - .lg\:cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); + .lg\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } - .lg\:cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); + .lg\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } - .lg\:cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); + .lg\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } - .lg\:cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); + .lg\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } - .lg\:cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); + .lg\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } - .lg\:cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); + .lg\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } - .lg\:cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); + .lg\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } - .lg\:cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); + .lg\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } - .lg\:cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); + .lg\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } - .lg\:cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); + .lg\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-my { + .xl\:bui-my { margin-top: var(--py-xl); margin-bottom: var(--py-xl); } - .xl\:cu-my-0\.5 { - margin-top: var(--canon-space-0_5); - margin-bottom: var(--canon-space-0_5); + .xl\:bui-my-0\.5 { + margin-top: var(--bui-space-0_5); + margin-bottom: var(--bui-space-0_5); } - .xl\:cu-my-1 { - margin-top: var(--canon-space-1); - margin-bottom: var(--canon-space-1); + .xl\:bui-my-1 { + margin-top: var(--bui-space-1); + margin-bottom: var(--bui-space-1); } - .xl\:cu-my-1\.5 { - margin-top: var(--canon-space-1_5); - margin-bottom: var(--canon-space-1_5); + .xl\:bui-my-1\.5 { + margin-top: var(--bui-space-1_5); + margin-bottom: var(--bui-space-1_5); } - .xl\:cu-my-2 { - margin-top: var(--canon-space-2); - margin-bottom: var(--canon-space-2); + .xl\:bui-my-2 { + margin-top: var(--bui-space-2); + margin-bottom: var(--bui-space-2); } - .xl\:cu-my-3 { - margin-top: var(--canon-space-3); - margin-bottom: var(--canon-space-3); + .xl\:bui-my-3 { + margin-top: var(--bui-space-3); + margin-bottom: var(--bui-space-3); } - .xl\:cu-my-4 { - margin-top: var(--canon-space-4); - margin-bottom: var(--canon-space-4); + .xl\:bui-my-4 { + margin-top: var(--bui-space-4); + margin-bottom: var(--bui-space-4); } - .xl\:cu-my-5 { - margin-top: var(--canon-space-5); - margin-bottom: var(--canon-space-5); + .xl\:bui-my-5 { + margin-top: var(--bui-space-5); + margin-bottom: var(--bui-space-5); } - .xl\:cu-my-6 { - margin-top: var(--canon-space-6); - margin-bottom: var(--canon-space-6); + .xl\:bui-my-6 { + margin-top: var(--bui-space-6); + margin-bottom: var(--bui-space-6); } - .xl\:cu-my-7 { - margin-top: var(--canon-space-7); - margin-bottom: var(--canon-space-7); + .xl\:bui-my-7 { + margin-top: var(--bui-space-7); + margin-bottom: var(--bui-space-7); } - .xl\:cu-my-8 { - margin-top: var(--canon-space-8); - margin-bottom: var(--canon-space-8); + .xl\:bui-my-8 { + margin-top: var(--bui-space-8); + margin-bottom: var(--bui-space-8); } - .xl\:cu-my-9 { - margin-top: var(--canon-space-9); - margin-bottom: var(--canon-space-9); + .xl\:bui-my-9 { + margin-top: var(--bui-space-9); + margin-bottom: var(--bui-space-9); } - .xl\:cu-my-10 { - margin-top: var(--canon-space-10); - margin-bottom: var(--canon-space-10); + .xl\:bui-my-10 { + margin-top: var(--bui-space-10); + margin-bottom: var(--bui-space-10); } - .xl\:cu-my-11 { - margin-top: var(--canon-space-11); - margin-bottom: var(--canon-space-11); + .xl\:bui-my-11 { + margin-top: var(--bui-space-11); + margin-bottom: var(--bui-space-11); } - .xl\:cu-my-12 { - margin-top: var(--canon-space-12); - margin-bottom: var(--canon-space-12); + .xl\:bui-my-12 { + margin-top: var(--bui-space-12); + margin-bottom: var(--bui-space-12); } - .xl\:cu-my-13 { - margin-top: var(--canon-space-13); - margin-bottom: var(--canon-space-13); + .xl\:bui-my-13 { + margin-top: var(--bui-space-13); + margin-bottom: var(--bui-space-13); } - .xl\:cu-my-14 { - margin-top: var(--canon-space-14); - margin-bottom: var(--canon-space-14); + .xl\:bui-my-14 { + margin-top: var(--bui-space-14); + margin-bottom: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/p.css b/packages/canon/src/css/utilities/p.css index 6540c626d2..113f10777e 100644 --- a/packages/canon/src/css/utilities/p.css +++ b/packages/canon/src/css/utilities/p.css @@ -1,422 +1,422 @@ -.cu-p { +.bui-p { padding: var(--p); } -.cu-p-0\.5 { - padding: var(--canon-space-0_5); +.bui-p-0\.5 { + padding: var(--bui-space-0_5); } -.cu-p-1 { - padding: var(--canon-space-1); +.bui-p-1 { + padding: var(--bui-space-1); } -.cu-p-1\.5 { - padding: var(--canon-space-1_5); +.bui-p-1\.5 { + padding: var(--bui-space-1_5); } -.cu-p-2 { - padding: var(--canon-space-2); +.bui-p-2 { + padding: var(--bui-space-2); } -.cu-p-3 { - padding: var(--canon-space-3); +.bui-p-3 { + padding: var(--bui-space-3); } -.cu-p-4 { - padding: var(--canon-space-4); +.bui-p-4 { + padding: var(--bui-space-4); } -.cu-p-5 { - padding: var(--canon-space-5); +.bui-p-5 { + padding: var(--bui-space-5); } -.cu-p-6 { - padding: var(--canon-space-6); +.bui-p-6 { + padding: var(--bui-space-6); } -.cu-p-7 { - padding: var(--canon-space-7); +.bui-p-7 { + padding: var(--bui-space-7); } -.cu-p-8 { - padding: var(--canon-space-8); +.bui-p-8 { + padding: var(--bui-space-8); } -.cu-p-9 { - padding: var(--canon-space-9); +.bui-p-9 { + padding: var(--bui-space-9); } -.cu-p-10 { - padding: var(--canon-space-10); +.bui-p-10 { + padding: var(--bui-space-10); } -.cu-p-11 { - padding: var(--canon-space-11); +.bui-p-11 { + padding: var(--bui-space-11); } -.cu-p-12 { - padding: var(--canon-space-12); +.bui-p-12 { + padding: var(--bui-space-12); } -.cu-p-13 { - padding: var(--canon-space-13); +.bui-p-13 { + padding: var(--bui-space-13); } -.cu-p-14 { - padding: var(--canon-space-14); +.bui-p-14 { + padding: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-p { + .xs\:bui-p { padding: var(--p-xs); } - .xs\:cu-p-0\.5 { - padding: var(--canon-space-0_5); + .xs\:bui-p-0\.5 { + padding: var(--bui-space-0_5); } - .xs\:cu-p-1 { - padding: var(--canon-space-1); + .xs\:bui-p-1 { + padding: var(--bui-space-1); } - .xs\:cu-p-1\.5 { - padding: var(--canon-space-1_5); + .xs\:bui-p-1\.5 { + padding: var(--bui-space-1_5); } - .xs\:cu-p-2 { - padding: var(--canon-space-2); + .xs\:bui-p-2 { + padding: var(--bui-space-2); } - .xs\:cu-p-3 { - padding: var(--canon-space-3); + .xs\:bui-p-3 { + padding: var(--bui-space-3); } - .xs\:cu-p-4 { - padding: var(--canon-space-4); + .xs\:bui-p-4 { + padding: var(--bui-space-4); } - .xs\:cu-p-5 { - padding: var(--canon-space-5); + .xs\:bui-p-5 { + padding: var(--bui-space-5); } - .xs\:cu-p-6 { - padding: var(--canon-space-6); + .xs\:bui-p-6 { + padding: var(--bui-space-6); } - .xs\:cu-p-7 { - padding: var(--canon-space-7); + .xs\:bui-p-7 { + padding: var(--bui-space-7); } - .xs\:cu-p-8 { - padding: var(--canon-space-8); + .xs\:bui-p-8 { + padding: var(--bui-space-8); } - .xs\:cu-p-9 { - padding: var(--canon-space-9); + .xs\:bui-p-9 { + padding: var(--bui-space-9); } - .xs\:cu-p-10 { - padding: var(--canon-space-10); + .xs\:bui-p-10 { + padding: var(--bui-space-10); } - .xs\:cu-p-11 { - padding: var(--canon-space-11); + .xs\:bui-p-11 { + padding: var(--bui-space-11); } - .xs\:cu-p-12 { - padding: var(--canon-space-12); + .xs\:bui-p-12 { + padding: var(--bui-space-12); } - .xs\:cu-p-13 { - padding: var(--canon-space-13); + .xs\:bui-p-13 { + padding: var(--bui-space-13); } - .xs\:cu-p-14 { - padding: var(--canon-space-14); + .xs\:bui-p-14 { + padding: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-p { + .sm\:bui-p { padding: var(--p-sm); } - .sm\:cu-p-0\.5 { - padding: var(--canon-space-0_5); + .sm\:bui-p-0\.5 { + padding: var(--bui-space-0_5); } - .sm\:cu-p-1 { - padding: var(--canon-space-1); + .sm\:bui-p-1 { + padding: var(--bui-space-1); } - .sm\:cu-p-1\.5 { - padding: var(--canon-space-1_5); + .sm\:bui-p-1\.5 { + padding: var(--bui-space-1_5); } - .sm\:cu-p-2 { - padding: var(--canon-space-2); + .sm\:bui-p-2 { + padding: var(--bui-space-2); } - .sm\:cu-p-3 { - padding: var(--canon-space-3); + .sm\:bui-p-3 { + padding: var(--bui-space-3); } - .sm\:cu-p-4 { - padding: var(--canon-space-4); + .sm\:bui-p-4 { + padding: var(--bui-space-4); } - .sm\:cu-p-5 { - padding: var(--canon-space-5); + .sm\:bui-p-5 { + padding: var(--bui-space-5); } - .sm\:cu-p-6 { - padding: var(--canon-space-6); + .sm\:bui-p-6 { + padding: var(--bui-space-6); } - .sm\:cu-p-7 { - padding: var(--canon-space-7); + .sm\:bui-p-7 { + padding: var(--bui-space-7); } - .sm\:cu-p-8 { - padding: var(--canon-space-8); + .sm\:bui-p-8 { + padding: var(--bui-space-8); } - .sm\:cu-p-9 { - padding: var(--canon-space-9); + .sm\:bui-p-9 { + padding: var(--bui-space-9); } - .sm\:cu-p-10 { - padding: var(--canon-space-10); + .sm\:bui-p-10 { + padding: var(--bui-space-10); } - .sm\:cu-p-11 { - padding: var(--canon-space-11); + .sm\:bui-p-11 { + padding: var(--bui-space-11); } - .sm\:cu-p-12 { - padding: var(--canon-space-12); + .sm\:bui-p-12 { + padding: var(--bui-space-12); } - .sm\:cu-p-13 { - padding: var(--canon-space-13); + .sm\:bui-p-13 { + padding: var(--bui-space-13); } - .sm\:cu-p-14 { - padding: var(--canon-space-14); + .sm\:bui-p-14 { + padding: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-p { + .md\:bui-p { padding: var(--p-md); } - .md\:cu-p-0\.5 { - padding: var(--canon-space-0_5); + .md\:bui-p-0\.5 { + padding: var(--bui-space-0_5); } - .md\:cu-p-1 { - padding: var(--canon-space-1); + .md\:bui-p-1 { + padding: var(--bui-space-1); } - .md\:cu-p-1\.5 { - padding: var(--canon-space-1_5); + .md\:bui-p-1\.5 { + padding: var(--bui-space-1_5); } - .md\:cu-p-2 { - padding: var(--canon-space-2); + .md\:bui-p-2 { + padding: var(--bui-space-2); } - .md\:cu-p-3 { - padding: var(--canon-space-3); + .md\:bui-p-3 { + padding: var(--bui-space-3); } - .md\:cu-p-4 { - padding: var(--canon-space-4); + .md\:bui-p-4 { + padding: var(--bui-space-4); } - .md\:cu-p-5 { - padding: var(--canon-space-5); + .md\:bui-p-5 { + padding: var(--bui-space-5); } - .md\:cu-p-6 { - padding: var(--canon-space-6); + .md\:bui-p-6 { + padding: var(--bui-space-6); } - .md\:cu-p-7 { - padding: var(--canon-space-7); + .md\:bui-p-7 { + padding: var(--bui-space-7); } - .md\:cu-p-8 { - padding: var(--canon-space-8); + .md\:bui-p-8 { + padding: var(--bui-space-8); } - .md\:cu-p-9 { - padding: var(--canon-space-9); + .md\:bui-p-9 { + padding: var(--bui-space-9); } - .md\:cu-p-10 { - padding: var(--canon-space-10); + .md\:bui-p-10 { + padding: var(--bui-space-10); } - .md\:cu-p-11 { - padding: var(--canon-space-11); + .md\:bui-p-11 { + padding: var(--bui-space-11); } - .md\:cu-p-12 { - padding: var(--canon-space-12); + .md\:bui-p-12 { + padding: var(--bui-space-12); } - .md\:cu-p-13 { - padding: var(--canon-space-13); + .md\:bui-p-13 { + padding: var(--bui-space-13); } - .md\:cu-p-14 { - padding: var(--canon-space-14); + .md\:bui-p-14 { + padding: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-p { + .lg\:bui-p { padding: var(--p-lg); } - .lg\:cu-p-0\.5 { - padding: var(--canon-space-0_5); + .lg\:bui-p-0\.5 { + padding: var(--bui-space-0_5); } - .lg\:cu-p-1 { - padding: var(--canon-space-1); + .lg\:bui-p-1 { + padding: var(--bui-space-1); } - .lg\:cu-p-1\.5 { - padding: var(--canon-space-1_5); + .lg\:bui-p-1\.5 { + padding: var(--bui-space-1_5); } - .lg\:cu-p-2 { - padding: var(--canon-space-2); + .lg\:bui-p-2 { + padding: var(--bui-space-2); } - .lg\:cu-p-3 { - padding: var(--canon-space-3); + .lg\:bui-p-3 { + padding: var(--bui-space-3); } - .lg\:cu-p-4 { - padding: var(--canon-space-4); + .lg\:bui-p-4 { + padding: var(--bui-space-4); } - .lg\:cu-p-5 { - padding: var(--canon-space-5); + .lg\:bui-p-5 { + padding: var(--bui-space-5); } - .lg\:cu-p-6 { - padding: var(--canon-space-6); + .lg\:bui-p-6 { + padding: var(--bui-space-6); } - .lg\:cu-p-7 { - padding: var(--canon-space-7); + .lg\:bui-p-7 { + padding: var(--bui-space-7); } - .lg\:cu-p-8 { - padding: var(--canon-space-8); + .lg\:bui-p-8 { + padding: var(--bui-space-8); } - .lg\:cu-p-9 { - padding: var(--canon-space-9); + .lg\:bui-p-9 { + padding: var(--bui-space-9); } - .lg\:cu-p-10 { - padding: var(--canon-space-10); + .lg\:bui-p-10 { + padding: var(--bui-space-10); } - .lg\:cu-p-11 { - padding: var(--canon-space-11); + .lg\:bui-p-11 { + padding: var(--bui-space-11); } - .lg\:cu-p-12 { - padding: var(--canon-space-12); + .lg\:bui-p-12 { + padding: var(--bui-space-12); } - .lg\:cu-p-13 { - padding: var(--canon-space-13); + .lg\:bui-p-13 { + padding: var(--bui-space-13); } - .lg\:cu-p-14 { - padding: var(--canon-space-14); + .lg\:bui-p-14 { + padding: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-p { + .xl\:bui-p { padding: var(--p-xl); } - .xl\:cu-p-0\.5 { - padding: var(--canon-space-0_5); + .xl\:bui-p-0\.5 { + padding: var(--bui-space-0_5); } - .xl\:cu-p-1 { - padding: var(--canon-space-1); + .xl\:bui-p-1 { + padding: var(--bui-space-1); } - .xl\:cu-p-1\.5 { - padding: var(--canon-space-1_5); + .xl\:bui-p-1\.5 { + padding: var(--bui-space-1_5); } - .xl\:cu-p-2 { - padding: var(--canon-space-2); + .xl\:bui-p-2 { + padding: var(--bui-space-2); } - .xl\:cu-p-3 { - padding: var(--canon-space-3); + .xl\:bui-p-3 { + padding: var(--bui-space-3); } - .xl\:cu-p-4 { - padding: var(--canon-space-4); + .xl\:bui-p-4 { + padding: var(--bui-space-4); } - .xl\:cu-p-5 { - padding: var(--canon-space-5); + .xl\:bui-p-5 { + padding: var(--bui-space-5); } - .xl\:cu-p-6 { - padding: var(--canon-space-6); + .xl\:bui-p-6 { + padding: var(--bui-space-6); } - .xl\:cu-p-7 { - padding: var(--canon-space-7); + .xl\:bui-p-7 { + padding: var(--bui-space-7); } - .xl\:cu-p-8 { - padding: var(--canon-space-8); + .xl\:bui-p-8 { + padding: var(--bui-space-8); } - .xl\:cu-p-9 { - padding: var(--canon-space-9); + .xl\:bui-p-9 { + padding: var(--bui-space-9); } - .xl\:cu-p-10 { - padding: var(--canon-space-10); + .xl\:bui-p-10 { + padding: var(--bui-space-10); } - .xl\:cu-p-11 { - padding: var(--canon-space-11); + .xl\:bui-p-11 { + padding: var(--bui-space-11); } - .xl\:cu-p-12 { - padding: var(--canon-space-12); + .xl\:bui-p-12 { + padding: var(--bui-space-12); } - .xl\:cu-p-13 { - padding: var(--canon-space-13); + .xl\:bui-p-13 { + padding: var(--bui-space-13); } - .xl\:cu-p-14 { - padding: var(--canon-space-14); + .xl\:bui-p-14 { + padding: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/pb.css b/packages/canon/src/css/utilities/pb.css index 722b744270..bdd3881f86 100644 --- a/packages/canon/src/css/utilities/pb.css +++ b/packages/canon/src/css/utilities/pb.css @@ -1,422 +1,422 @@ -.cu-pb { +.bui-pb { padding-bottom: var(--pb); } -.cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); +.bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } -.cu-pb-1 { - padding-bottom: var(--canon-space-1); +.bui-pb-1 { + padding-bottom: var(--bui-space-1); } -.cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); +.bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } -.cu-pb-2 { - padding-bottom: var(--canon-space-2); +.bui-pb-2 { + padding-bottom: var(--bui-space-2); } -.cu-pb-3 { - padding-bottom: var(--canon-space-3); +.bui-pb-3 { + padding-bottom: var(--bui-space-3); } -.cu-pb-4 { - padding-bottom: var(--canon-space-4); +.bui-pb-4 { + padding-bottom: var(--bui-space-4); } -.cu-pb-5 { - padding-bottom: var(--canon-space-5); +.bui-pb-5 { + padding-bottom: var(--bui-space-5); } -.cu-pb-6 { - padding-bottom: var(--canon-space-6); +.bui-pb-6 { + padding-bottom: var(--bui-space-6); } -.cu-pb-7 { - padding-bottom: var(--canon-space-7); +.bui-pb-7 { + padding-bottom: var(--bui-space-7); } -.cu-pb-8 { - padding-bottom: var(--canon-space-8); +.bui-pb-8 { + padding-bottom: var(--bui-space-8); } -.cu-pb-9 { - padding-bottom: var(--canon-space-9); +.bui-pb-9 { + padding-bottom: var(--bui-space-9); } -.cu-pb-10 { - padding-bottom: var(--canon-space-10); +.bui-pb-10 { + padding-bottom: var(--bui-space-10); } -.cu-pb-11 { - padding-bottom: var(--canon-space-11); +.bui-pb-11 { + padding-bottom: var(--bui-space-11); } -.cu-pb-12 { - padding-bottom: var(--canon-space-12); +.bui-pb-12 { + padding-bottom: var(--bui-space-12); } -.cu-pb-13 { - padding-bottom: var(--canon-space-13); +.bui-pb-13 { + padding-bottom: var(--bui-space-13); } -.cu-pb-14 { - padding-bottom: var(--canon-space-14); +.bui-pb-14 { + padding-bottom: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-pb { + .xs\:bui-pb { padding-bottom: var(--pb-xs); } - .xs\:cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); + .xs\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } - .xs\:cu-pb-1 { - padding-bottom: var(--canon-space-1); + .xs\:bui-pb-1 { + padding-bottom: var(--bui-space-1); } - .xs\:cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); + .xs\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } - .xs\:cu-pb-2 { - padding-bottom: var(--canon-space-2); + .xs\:bui-pb-2 { + padding-bottom: var(--bui-space-2); } - .xs\:cu-pb-3 { - padding-bottom: var(--canon-space-3); + .xs\:bui-pb-3 { + padding-bottom: var(--bui-space-3); } - .xs\:cu-pb-4 { - padding-bottom: var(--canon-space-4); + .xs\:bui-pb-4 { + padding-bottom: var(--bui-space-4); } - .xs\:cu-pb-5 { - padding-bottom: var(--canon-space-5); + .xs\:bui-pb-5 { + padding-bottom: var(--bui-space-5); } - .xs\:cu-pb-6 { - padding-bottom: var(--canon-space-6); + .xs\:bui-pb-6 { + padding-bottom: var(--bui-space-6); } - .xs\:cu-pb-7 { - padding-bottom: var(--canon-space-7); + .xs\:bui-pb-7 { + padding-bottom: var(--bui-space-7); } - .xs\:cu-pb-8 { - padding-bottom: var(--canon-space-8); + .xs\:bui-pb-8 { + padding-bottom: var(--bui-space-8); } - .xs\:cu-pb-9 { - padding-bottom: var(--canon-space-9); + .xs\:bui-pb-9 { + padding-bottom: var(--bui-space-9); } - .xs\:cu-pb-10 { - padding-bottom: var(--canon-space-10); + .xs\:bui-pb-10 { + padding-bottom: var(--bui-space-10); } - .xs\:cu-pb-11 { - padding-bottom: var(--canon-space-11); + .xs\:bui-pb-11 { + padding-bottom: var(--bui-space-11); } - .xs\:cu-pb-12 { - padding-bottom: var(--canon-space-12); + .xs\:bui-pb-12 { + padding-bottom: var(--bui-space-12); } - .xs\:cu-pb-13 { - padding-bottom: var(--canon-space-13); + .xs\:bui-pb-13 { + padding-bottom: var(--bui-space-13); } - .xs\:cu-pb-14 { - padding-bottom: var(--canon-space-14); + .xs\:bui-pb-14 { + padding-bottom: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-pb { + .sm\:bui-pb { padding-bottom: var(--pb-sm); } - .sm\:cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); + .sm\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } - .sm\:cu-pb-1 { - padding-bottom: var(--canon-space-1); + .sm\:bui-pb-1 { + padding-bottom: var(--bui-space-1); } - .sm\:cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); + .sm\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } - .sm\:cu-pb-2 { - padding-bottom: var(--canon-space-2); + .sm\:bui-pb-2 { + padding-bottom: var(--bui-space-2); } - .sm\:cu-pb-3 { - padding-bottom: var(--canon-space-3); + .sm\:bui-pb-3 { + padding-bottom: var(--bui-space-3); } - .sm\:cu-pb-4 { - padding-bottom: var(--canon-space-4); + .sm\:bui-pb-4 { + padding-bottom: var(--bui-space-4); } - .sm\:cu-pb-5 { - padding-bottom: var(--canon-space-5); + .sm\:bui-pb-5 { + padding-bottom: var(--bui-space-5); } - .sm\:cu-pb-6 { - padding-bottom: var(--canon-space-6); + .sm\:bui-pb-6 { + padding-bottom: var(--bui-space-6); } - .sm\:cu-pb-7 { - padding-bottom: var(--canon-space-7); + .sm\:bui-pb-7 { + padding-bottom: var(--bui-space-7); } - .sm\:cu-pb-8 { - padding-bottom: var(--canon-space-8); + .sm\:bui-pb-8 { + padding-bottom: var(--bui-space-8); } - .sm\:cu-pb-9 { - padding-bottom: var(--canon-space-9); + .sm\:bui-pb-9 { + padding-bottom: var(--bui-space-9); } - .sm\:cu-pb-10 { - padding-bottom: var(--canon-space-10); + .sm\:bui-pb-10 { + padding-bottom: var(--bui-space-10); } - .sm\:cu-pb-11 { - padding-bottom: var(--canon-space-11); + .sm\:bui-pb-11 { + padding-bottom: var(--bui-space-11); } - .sm\:cu-pb-12 { - padding-bottom: var(--canon-space-12); + .sm\:bui-pb-12 { + padding-bottom: var(--bui-space-12); } - .sm\:cu-pb-13 { - padding-bottom: var(--canon-space-13); + .sm\:bui-pb-13 { + padding-bottom: var(--bui-space-13); } - .sm\:cu-pb-14 { - padding-bottom: var(--canon-space-14); + .sm\:bui-pb-14 { + padding-bottom: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-pb { + .md\:bui-pb { padding-bottom: var(--pb-md); } - .md\:cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); + .md\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } - .md\:cu-pb-1 { - padding-bottom: var(--canon-space-1); + .md\:bui-pb-1 { + padding-bottom: var(--bui-space-1); } - .md\:cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); + .md\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } - .md\:cu-pb-2 { - padding-bottom: var(--canon-space-2); + .md\:bui-pb-2 { + padding-bottom: var(--bui-space-2); } - .md\:cu-pb-3 { - padding-bottom: var(--canon-space-3); + .md\:bui-pb-3 { + padding-bottom: var(--bui-space-3); } - .md\:cu-pb-4 { - padding-bottom: var(--canon-space-4); + .md\:bui-pb-4 { + padding-bottom: var(--bui-space-4); } - .md\:cu-pb-5 { - padding-bottom: var(--canon-space-5); + .md\:bui-pb-5 { + padding-bottom: var(--bui-space-5); } - .md\:cu-pb-6 { - padding-bottom: var(--canon-space-6); + .md\:bui-pb-6 { + padding-bottom: var(--bui-space-6); } - .md\:cu-pb-7 { - padding-bottom: var(--canon-space-7); + .md\:bui-pb-7 { + padding-bottom: var(--bui-space-7); } - .md\:cu-pb-8 { - padding-bottom: var(--canon-space-8); + .md\:bui-pb-8 { + padding-bottom: var(--bui-space-8); } - .md\:cu-pb-9 { - padding-bottom: var(--canon-space-9); + .md\:bui-pb-9 { + padding-bottom: var(--bui-space-9); } - .md\:cu-pb-10 { - padding-bottom: var(--canon-space-10); + .md\:bui-pb-10 { + padding-bottom: var(--bui-space-10); } - .md\:cu-pb-11 { - padding-bottom: var(--canon-space-11); + .md\:bui-pb-11 { + padding-bottom: var(--bui-space-11); } - .md\:cu-pb-12 { - padding-bottom: var(--canon-space-12); + .md\:bui-pb-12 { + padding-bottom: var(--bui-space-12); } - .md\:cu-pb-13 { - padding-bottom: var(--canon-space-13); + .md\:bui-pb-13 { + padding-bottom: var(--bui-space-13); } - .md\:cu-pb-14 { - padding-bottom: var(--canon-space-14); + .md\:bui-pb-14 { + padding-bottom: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-pb { + .lg\:bui-pb { padding-bottom: var(--pb-lg); } - .lg\:cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); + .lg\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } - .lg\:cu-pb-1 { - padding-bottom: var(--canon-space-1); + .lg\:bui-pb-1 { + padding-bottom: var(--bui-space-1); } - .lg\:cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); + .lg\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } - .lg\:cu-pb-2 { - padding-bottom: var(--canon-space-2); + .lg\:bui-pb-2 { + padding-bottom: var(--bui-space-2); } - .lg\:cu-pb-3 { - padding-bottom: var(--canon-space-3); + .lg\:bui-pb-3 { + padding-bottom: var(--bui-space-3); } - .lg\:cu-pb-4 { - padding-bottom: var(--canon-space-4); + .lg\:bui-pb-4 { + padding-bottom: var(--bui-space-4); } - .lg\:cu-pb-5 { - padding-bottom: var(--canon-space-5); + .lg\:bui-pb-5 { + padding-bottom: var(--bui-space-5); } - .lg\:cu-pb-6 { - padding-bottom: var(--canon-space-6); + .lg\:bui-pb-6 { + padding-bottom: var(--bui-space-6); } - .lg\:cu-pb-7 { - padding-bottom: var(--canon-space-7); + .lg\:bui-pb-7 { + padding-bottom: var(--bui-space-7); } - .lg\:cu-pb-8 { - padding-bottom: var(--canon-space-8); + .lg\:bui-pb-8 { + padding-bottom: var(--bui-space-8); } - .lg\:cu-pb-9 { - padding-bottom: var(--canon-space-9); + .lg\:bui-pb-9 { + padding-bottom: var(--bui-space-9); } - .lg\:cu-pb-10 { - padding-bottom: var(--canon-space-10); + .lg\:bui-pb-10 { + padding-bottom: var(--bui-space-10); } - .lg\:cu-pb-11 { - padding-bottom: var(--canon-space-11); + .lg\:bui-pb-11 { + padding-bottom: var(--bui-space-11); } - .lg\:cu-pb-12 { - padding-bottom: var(--canon-space-12); + .lg\:bui-pb-12 { + padding-bottom: var(--bui-space-12); } - .lg\:cu-pb-13 { - padding-bottom: var(--canon-space-13); + .lg\:bui-pb-13 { + padding-bottom: var(--bui-space-13); } - .lg\:cu-pb-14 { - padding-bottom: var(--canon-space-14); + .lg\:bui-pb-14 { + padding-bottom: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-pb { + .xl\:bui-pb { padding-bottom: var(--pb-xl); } - .xl\:cu-pb-0\.5 { - padding-bottom: var(--canon-space-0_5); + .xl\:bui-pb-0\.5 { + padding-bottom: var(--bui-space-0_5); } - .xl\:cu-pb-1 { - padding-bottom: var(--canon-space-1); + .xl\:bui-pb-1 { + padding-bottom: var(--bui-space-1); } - .xl\:cu-pb-1\.5 { - padding-bottom: var(--canon-space-1_5); + .xl\:bui-pb-1\.5 { + padding-bottom: var(--bui-space-1_5); } - .xl\:cu-pb-2 { - padding-bottom: var(--canon-space-2); + .xl\:bui-pb-2 { + padding-bottom: var(--bui-space-2); } - .xl\:cu-pb-3 { - padding-bottom: var(--canon-space-3); + .xl\:bui-pb-3 { + padding-bottom: var(--bui-space-3); } - .xl\:cu-pb-4 { - padding-bottom: var(--canon-space-4); + .xl\:bui-pb-4 { + padding-bottom: var(--bui-space-4); } - .xl\:cu-pb-5 { - padding-bottom: var(--canon-space-5); + .xl\:bui-pb-5 { + padding-bottom: var(--bui-space-5); } - .xl\:cu-pb-6 { - padding-bottom: var(--canon-space-6); + .xl\:bui-pb-6 { + padding-bottom: var(--bui-space-6); } - .xl\:cu-pb-7 { - padding-bottom: var(--canon-space-7); + .xl\:bui-pb-7 { + padding-bottom: var(--bui-space-7); } - .xl\:cu-pb-8 { - padding-bottom: var(--canon-space-8); + .xl\:bui-pb-8 { + padding-bottom: var(--bui-space-8); } - .xl\:cu-pb-9 { - padding-bottom: var(--canon-space-9); + .xl\:bui-pb-9 { + padding-bottom: var(--bui-space-9); } - .xl\:cu-pb-10 { - padding-bottom: var(--canon-space-10); + .xl\:bui-pb-10 { + padding-bottom: var(--bui-space-10); } - .xl\:cu-pb-11 { - padding-bottom: var(--canon-space-11); + .xl\:bui-pb-11 { + padding-bottom: var(--bui-space-11); } - .xl\:cu-pb-12 { - padding-bottom: var(--canon-space-12); + .xl\:bui-pb-12 { + padding-bottom: var(--bui-space-12); } - .xl\:cu-pb-13 { - padding-bottom: var(--canon-space-13); + .xl\:bui-pb-13 { + padding-bottom: var(--bui-space-13); } - .xl\:cu-pb-14 { - padding-bottom: var(--canon-space-14); + .xl\:bui-pb-14 { + padding-bottom: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/pl.css b/packages/canon/src/css/utilities/pl.css index a9db77e279..07e064293a 100644 --- a/packages/canon/src/css/utilities/pl.css +++ b/packages/canon/src/css/utilities/pl.css @@ -1,422 +1,422 @@ -.cu-pl { +.bui-pl { padding-left: var(--pl); } -.cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); +.bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } -.cu-pl-1 { - padding-left: var(--canon-space-1); +.bui-pl-1 { + padding-left: var(--bui-space-1); } -.cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); +.bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } -.cu-pl-2 { - padding-left: var(--canon-space-2); +.bui-pl-2 { + padding-left: var(--bui-space-2); } -.cu-pl-3 { - padding-left: var(--canon-space-3); +.bui-pl-3 { + padding-left: var(--bui-space-3); } -.cu-pl-4 { - padding-left: var(--canon-space-4); +.bui-pl-4 { + padding-left: var(--bui-space-4); } -.cu-pl-5 { - padding-left: var(--canon-space-5); +.bui-pl-5 { + padding-left: var(--bui-space-5); } -.cu-pl-6 { - padding-left: var(--canon-space-6); +.bui-pl-6 { + padding-left: var(--bui-space-6); } -.cu-pl-7 { - padding-left: var(--canon-space-7); +.bui-pl-7 { + padding-left: var(--bui-space-7); } -.cu-pl-8 { - padding-left: var(--canon-space-8); +.bui-pl-8 { + padding-left: var(--bui-space-8); } -.cu-pl-9 { - padding-left: var(--canon-space-9); +.bui-pl-9 { + padding-left: var(--bui-space-9); } -.cu-pl-10 { - padding-left: var(--canon-space-10); +.bui-pl-10 { + padding-left: var(--bui-space-10); } -.cu-pl-11 { - padding-left: var(--canon-space-11); +.bui-pl-11 { + padding-left: var(--bui-space-11); } -.cu-pl-12 { - padding-left: var(--canon-space-12); +.bui-pl-12 { + padding-left: var(--bui-space-12); } -.cu-pl-13 { - padding-left: var(--canon-space-13); +.bui-pl-13 { + padding-left: var(--bui-space-13); } -.cu-pl-14 { - padding-left: var(--canon-space-14); +.bui-pl-14 { + padding-left: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-pl { + .xs\:bui-pl { padding-left: var(--pl-xs); } - .xs\:cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); + .xs\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } - .xs\:cu-pl-1 { - padding-left: var(--canon-space-1); + .xs\:bui-pl-1 { + padding-left: var(--bui-space-1); } - .xs\:cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); + .xs\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } - .xs\:cu-pl-2 { - padding-left: var(--canon-space-2); + .xs\:bui-pl-2 { + padding-left: var(--bui-space-2); } - .xs\:cu-pl-3 { - padding-left: var(--canon-space-3); + .xs\:bui-pl-3 { + padding-left: var(--bui-space-3); } - .xs\:cu-pl-4 { - padding-left: var(--canon-space-4); + .xs\:bui-pl-4 { + padding-left: var(--bui-space-4); } - .xs\:cu-pl-5 { - padding-left: var(--canon-space-5); + .xs\:bui-pl-5 { + padding-left: var(--bui-space-5); } - .xs\:cu-pl-6 { - padding-left: var(--canon-space-6); + .xs\:bui-pl-6 { + padding-left: var(--bui-space-6); } - .xs\:cu-pl-7 { - padding-left: var(--canon-space-7); + .xs\:bui-pl-7 { + padding-left: var(--bui-space-7); } - .xs\:cu-pl-8 { - padding-left: var(--canon-space-8); + .xs\:bui-pl-8 { + padding-left: var(--bui-space-8); } - .xs\:cu-pl-9 { - padding-left: var(--canon-space-9); + .xs\:bui-pl-9 { + padding-left: var(--bui-space-9); } - .xs\:cu-pl-10 { - padding-left: var(--canon-space-10); + .xs\:bui-pl-10 { + padding-left: var(--bui-space-10); } - .xs\:cu-pl-11 { - padding-left: var(--canon-space-11); + .xs\:bui-pl-11 { + padding-left: var(--bui-space-11); } - .xs\:cu-pl-12 { - padding-left: var(--canon-space-12); + .xs\:bui-pl-12 { + padding-left: var(--bui-space-12); } - .xs\:cu-pl-13 { - padding-left: var(--canon-space-13); + .xs\:bui-pl-13 { + padding-left: var(--bui-space-13); } - .xs\:cu-pl-14 { - padding-left: var(--canon-space-14); + .xs\:bui-pl-14 { + padding-left: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-pl { + .sm\:bui-pl { padding-left: var(--pl-sm); } - .sm\:cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); + .sm\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } - .sm\:cu-pl-1 { - padding-left: var(--canon-space-1); + .sm\:bui-pl-1 { + padding-left: var(--bui-space-1); } - .sm\:cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); + .sm\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } - .sm\:cu-pl-2 { - padding-left: var(--canon-space-2); + .sm\:bui-pl-2 { + padding-left: var(--bui-space-2); } - .sm\:cu-pl-3 { - padding-left: var(--canon-space-3); + .sm\:bui-pl-3 { + padding-left: var(--bui-space-3); } - .sm\:cu-pl-4 { - padding-left: var(--canon-space-4); + .sm\:bui-pl-4 { + padding-left: var(--bui-space-4); } - .sm\:cu-pl-5 { - padding-left: var(--canon-space-5); + .sm\:bui-pl-5 { + padding-left: var(--bui-space-5); } - .sm\:cu-pl-6 { - padding-left: var(--canon-space-6); + .sm\:bui-pl-6 { + padding-left: var(--bui-space-6); } - .sm\:cu-pl-7 { - padding-left: var(--canon-space-7); + .sm\:bui-pl-7 { + padding-left: var(--bui-space-7); } - .sm\:cu-pl-8 { - padding-left: var(--canon-space-8); + .sm\:bui-pl-8 { + padding-left: var(--bui-space-8); } - .sm\:cu-pl-9 { - padding-left: var(--canon-space-9); + .sm\:bui-pl-9 { + padding-left: var(--bui-space-9); } - .sm\:cu-pl-10 { - padding-left: var(--canon-space-10); + .sm\:bui-pl-10 { + padding-left: var(--bui-space-10); } - .sm\:cu-pl-11 { - padding-left: var(--canon-space-11); + .sm\:bui-pl-11 { + padding-left: var(--bui-space-11); } - .sm\:cu-pl-12 { - padding-left: var(--canon-space-12); + .sm\:bui-pl-12 { + padding-left: var(--bui-space-12); } - .sm\:cu-pl-13 { - padding-left: var(--canon-space-13); + .sm\:bui-pl-13 { + padding-left: var(--bui-space-13); } - .sm\:cu-pl-14 { - padding-left: var(--canon-space-14); + .sm\:bui-pl-14 { + padding-left: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-pl { + .md\:bui-pl { padding-left: var(--pl-md); } - .md\:cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); + .md\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } - .md\:cu-pl-1 { - padding-left: var(--canon-space-1); + .md\:bui-pl-1 { + padding-left: var(--bui-space-1); } - .md\:cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); + .md\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } - .md\:cu-pl-2 { - padding-left: var(--canon-space-2); + .md\:bui-pl-2 { + padding-left: var(--bui-space-2); } - .md\:cu-pl-3 { - padding-left: var(--canon-space-3); + .md\:bui-pl-3 { + padding-left: var(--bui-space-3); } - .md\:cu-pl-4 { - padding-left: var(--canon-space-4); + .md\:bui-pl-4 { + padding-left: var(--bui-space-4); } - .md\:cu-pl-5 { - padding-left: var(--canon-space-5); + .md\:bui-pl-5 { + padding-left: var(--bui-space-5); } - .md\:cu-pl-6 { - padding-left: var(--canon-space-6); + .md\:bui-pl-6 { + padding-left: var(--bui-space-6); } - .md\:cu-pl-7 { - padding-left: var(--canon-space-7); + .md\:bui-pl-7 { + padding-left: var(--bui-space-7); } - .md\:cu-pl-8 { - padding-left: var(--canon-space-8); + .md\:bui-pl-8 { + padding-left: var(--bui-space-8); } - .md\:cu-pl-9 { - padding-left: var(--canon-space-9); + .md\:bui-pl-9 { + padding-left: var(--bui-space-9); } - .md\:cu-pl-10 { - padding-left: var(--canon-space-10); + .md\:bui-pl-10 { + padding-left: var(--bui-space-10); } - .md\:cu-pl-11 { - padding-left: var(--canon-space-11); + .md\:bui-pl-11 { + padding-left: var(--bui-space-11); } - .md\:cu-pl-12 { - padding-left: var(--canon-space-12); + .md\:bui-pl-12 { + padding-left: var(--bui-space-12); } - .md\:cu-pl-13 { - padding-left: var(--canon-space-13); + .md\:bui-pl-13 { + padding-left: var(--bui-space-13); } - .md\:cu-pl-14 { - padding-left: var(--canon-space-14); + .md\:bui-pl-14 { + padding-left: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-pl { + .lg\:bui-pl { padding-left: var(--pl-lg); } - .lg\:cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); + .lg\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } - .lg\:cu-pl-1 { - padding-left: var(--canon-space-1); + .lg\:bui-pl-1 { + padding-left: var(--bui-space-1); } - .lg\:cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); + .lg\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } - .lg\:cu-pl-2 { - padding-left: var(--canon-space-2); + .lg\:bui-pl-2 { + padding-left: var(--bui-space-2); } - .lg\:cu-pl-3 { - padding-left: var(--canon-space-3); + .lg\:bui-pl-3 { + padding-left: var(--bui-space-3); } - .lg\:cu-pl-4 { - padding-left: var(--canon-space-4); + .lg\:bui-pl-4 { + padding-left: var(--bui-space-4); } - .lg\:cu-pl-5 { - padding-left: var(--canon-space-5); + .lg\:bui-pl-5 { + padding-left: var(--bui-space-5); } - .lg\:cu-pl-6 { - padding-left: var(--canon-space-6); + .lg\:bui-pl-6 { + padding-left: var(--bui-space-6); } - .lg\:cu-pl-7 { - padding-left: var(--canon-space-7); + .lg\:bui-pl-7 { + padding-left: var(--bui-space-7); } - .lg\:cu-pl-8 { - padding-left: var(--canon-space-8); + .lg\:bui-pl-8 { + padding-left: var(--bui-space-8); } - .lg\:cu-pl-9 { - padding-left: var(--canon-space-9); + .lg\:bui-pl-9 { + padding-left: var(--bui-space-9); } - .lg\:cu-pl-10 { - padding-left: var(--canon-space-10); + .lg\:bui-pl-10 { + padding-left: var(--bui-space-10); } - .lg\:cu-pl-11 { - padding-left: var(--canon-space-11); + .lg\:bui-pl-11 { + padding-left: var(--bui-space-11); } - .lg\:cu-pl-12 { - padding-left: var(--canon-space-12); + .lg\:bui-pl-12 { + padding-left: var(--bui-space-12); } - .lg\:cu-pl-13 { - padding-left: var(--canon-space-13); + .lg\:bui-pl-13 { + padding-left: var(--bui-space-13); } - .lg\:cu-pl-14 { - padding-left: var(--canon-space-14); + .lg\:bui-pl-14 { + padding-left: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-pl { + .xl\:bui-pl { padding-left: var(--pl-xl); } - .xl\:cu-pl-0\.5 { - padding-left: var(--canon-space-0_5); + .xl\:bui-pl-0\.5 { + padding-left: var(--bui-space-0_5); } - .xl\:cu-pl-1 { - padding-left: var(--canon-space-1); + .xl\:bui-pl-1 { + padding-left: var(--bui-space-1); } - .xl\:cu-pl-1\.5 { - padding-left: var(--canon-space-1_5); + .xl\:bui-pl-1\.5 { + padding-left: var(--bui-space-1_5); } - .xl\:cu-pl-2 { - padding-left: var(--canon-space-2); + .xl\:bui-pl-2 { + padding-left: var(--bui-space-2); } - .xl\:cu-pl-3 { - padding-left: var(--canon-space-3); + .xl\:bui-pl-3 { + padding-left: var(--bui-space-3); } - .xl\:cu-pl-4 { - padding-left: var(--canon-space-4); + .xl\:bui-pl-4 { + padding-left: var(--bui-space-4); } - .xl\:cu-pl-5 { - padding-left: var(--canon-space-5); + .xl\:bui-pl-5 { + padding-left: var(--bui-space-5); } - .xl\:cu-pl-6 { - padding-left: var(--canon-space-6); + .xl\:bui-pl-6 { + padding-left: var(--bui-space-6); } - .xl\:cu-pl-7 { - padding-left: var(--canon-space-7); + .xl\:bui-pl-7 { + padding-left: var(--bui-space-7); } - .xl\:cu-pl-8 { - padding-left: var(--canon-space-8); + .xl\:bui-pl-8 { + padding-left: var(--bui-space-8); } - .xl\:cu-pl-9 { - padding-left: var(--canon-space-9); + .xl\:bui-pl-9 { + padding-left: var(--bui-space-9); } - .xl\:cu-pl-10 { - padding-left: var(--canon-space-10); + .xl\:bui-pl-10 { + padding-left: var(--bui-space-10); } - .xl\:cu-pl-11 { - padding-left: var(--canon-space-11); + .xl\:bui-pl-11 { + padding-left: var(--bui-space-11); } - .xl\:cu-pl-12 { - padding-left: var(--canon-space-12); + .xl\:bui-pl-12 { + padding-left: var(--bui-space-12); } - .xl\:cu-pl-13 { - padding-left: var(--canon-space-13); + .xl\:bui-pl-13 { + padding-left: var(--bui-space-13); } - .xl\:cu-pl-14 { - padding-left: var(--canon-space-14); + .xl\:bui-pl-14 { + padding-left: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/position.css b/packages/canon/src/css/utilities/position.css index 764ada623b..11cc0994b6 100644 --- a/packages/canon/src/css/utilities/position.css +++ b/packages/canon/src/css/utilities/position.css @@ -1,134 +1,134 @@ -.cu-position-absolute { +.bui-position-absolute { position: absolute; } -.cu-position-fixed { +.bui-position-fixed { position: fixed; } -.cu-position-sticky { +.bui-position-sticky { position: sticky; } -.cu-position-relative { +.bui-position-relative { position: relative; } -.cu-position-static { +.bui-position-static { position: static; } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-position-absolute { + .xs\:bui-position-absolute { position: absolute; } - .xs\:cu-position-fixed { + .xs\:bui-position-fixed { position: fixed; } - .xs\:cu-position-sticky { + .xs\:bui-position-sticky { position: sticky; } - .xs\:cu-position-relative { + .xs\:bui-position-relative { position: relative; } - .xs\:cu-position-static { + .xs\:bui-position-static { position: static; } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-position-absolute { + .sm\:bui-position-absolute { position: absolute; } - .sm\:cu-position-fixed { + .sm\:bui-position-fixed { position: fixed; } - .sm\:cu-position-sticky { + .sm\:bui-position-sticky { position: sticky; } - .sm\:cu-position-relative { + .sm\:bui-position-relative { position: relative; } - .sm\:cu-position-static { + .sm\:bui-position-static { position: static; } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-position-absolute { + .md\:bui-position-absolute { position: absolute; } - .md\:cu-position-fixed { + .md\:bui-position-fixed { position: fixed; } - .md\:cu-position-sticky { + .md\:bui-position-sticky { position: sticky; } - .md\:cu-position-relative { + .md\:bui-position-relative { position: relative; } - .md\:cu-position-static { + .md\:bui-position-static { position: static; } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-position-absolute { + .lg\:bui-position-absolute { position: absolute; } - .lg\:cu-position-fixed { + .lg\:bui-position-fixed { position: fixed; } - .lg\:cu-position-sticky { + .lg\:bui-position-sticky { position: sticky; } - .lg\:cu-position-relative { + .lg\:bui-position-relative { position: relative; } - .lg\:cu-position-static { + .lg\:bui-position-static { position: static; } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-position-absolute { + .xl\:bui-position-absolute { position: absolute; } - .xl\:cu-position-fixed { + .xl\:bui-position-fixed { position: fixed; } - .xl\:cu-position-sticky { + .xl\:bui-position-sticky { position: sticky; } - .xl\:cu-position-relative { + .xl\:bui-position-relative { position: relative; } - .xl\:cu-position-static { + .xl\:bui-position-static { position: static; } } diff --git a/packages/canon/src/css/utilities/pr.css b/packages/canon/src/css/utilities/pr.css index bcbb35f192..c6fa94a1c5 100644 --- a/packages/canon/src/css/utilities/pr.css +++ b/packages/canon/src/css/utilities/pr.css @@ -1,422 +1,422 @@ -.cu-pr { +.bui-pr { padding-right: var(--pr); } -.cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); +.bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } -.cu-pr-1 { - padding-right: var(--canon-space-1); +.bui-pr-1 { + padding-right: var(--bui-space-1); } -.cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); +.bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } -.cu-pr-2 { - padding-right: var(--canon-space-2); +.bui-pr-2 { + padding-right: var(--bui-space-2); } -.cu-pr-3 { - padding-right: var(--canon-space-3); +.bui-pr-3 { + padding-right: var(--bui-space-3); } -.cu-pr-4 { - padding-right: var(--canon-space-4); +.bui-pr-4 { + padding-right: var(--bui-space-4); } -.cu-pr-5 { - padding-right: var(--canon-space-5); +.bui-pr-5 { + padding-right: var(--bui-space-5); } -.cu-pr-6 { - padding-right: var(--canon-space-6); +.bui-pr-6 { + padding-right: var(--bui-space-6); } -.cu-pr-7 { - padding-right: var(--canon-space-7); +.bui-pr-7 { + padding-right: var(--bui-space-7); } -.cu-pr-8 { - padding-right: var(--canon-space-8); +.bui-pr-8 { + padding-right: var(--bui-space-8); } -.cu-pr-9 { - padding-right: var(--canon-space-9); +.bui-pr-9 { + padding-right: var(--bui-space-9); } -.cu-pr-10 { - padding-right: var(--canon-space-10); +.bui-pr-10 { + padding-right: var(--bui-space-10); } -.cu-pr-11 { - padding-right: var(--canon-space-11); +.bui-pr-11 { + padding-right: var(--bui-space-11); } -.cu-pr-12 { - padding-right: var(--canon-space-12); +.bui-pr-12 { + padding-right: var(--bui-space-12); } -.cu-pr-13 { - padding-right: var(--canon-space-13); +.bui-pr-13 { + padding-right: var(--bui-space-13); } -.cu-pr-14 { - padding-right: var(--canon-space-14); +.bui-pr-14 { + padding-right: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-pr { + .xs\:bui-pr { padding-right: var(--pr-xs); } - .xs\:cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); + .xs\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } - .xs\:cu-pr-1 { - padding-right: var(--canon-space-1); + .xs\:bui-pr-1 { + padding-right: var(--bui-space-1); } - .xs\:cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); + .xs\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } - .xs\:cu-pr-2 { - padding-right: var(--canon-space-2); + .xs\:bui-pr-2 { + padding-right: var(--bui-space-2); } - .xs\:cu-pr-3 { - padding-right: var(--canon-space-3); + .xs\:bui-pr-3 { + padding-right: var(--bui-space-3); } - .xs\:cu-pr-4 { - padding-right: var(--canon-space-4); + .xs\:bui-pr-4 { + padding-right: var(--bui-space-4); } - .xs\:cu-pr-5 { - padding-right: var(--canon-space-5); + .xs\:bui-pr-5 { + padding-right: var(--bui-space-5); } - .xs\:cu-pr-6 { - padding-right: var(--canon-space-6); + .xs\:bui-pr-6 { + padding-right: var(--bui-space-6); } - .xs\:cu-pr-7 { - padding-right: var(--canon-space-7); + .xs\:bui-pr-7 { + padding-right: var(--bui-space-7); } - .xs\:cu-pr-8 { - padding-right: var(--canon-space-8); + .xs\:bui-pr-8 { + padding-right: var(--bui-space-8); } - .xs\:cu-pr-9 { - padding-right: var(--canon-space-9); + .xs\:bui-pr-9 { + padding-right: var(--bui-space-9); } - .xs\:cu-pr-10 { - padding-right: var(--canon-space-10); + .xs\:bui-pr-10 { + padding-right: var(--bui-space-10); } - .xs\:cu-pr-11 { - padding-right: var(--canon-space-11); + .xs\:bui-pr-11 { + padding-right: var(--bui-space-11); } - .xs\:cu-pr-12 { - padding-right: var(--canon-space-12); + .xs\:bui-pr-12 { + padding-right: var(--bui-space-12); } - .xs\:cu-pr-13 { - padding-right: var(--canon-space-13); + .xs\:bui-pr-13 { + padding-right: var(--bui-space-13); } - .xs\:cu-pr-14 { - padding-right: var(--canon-space-14); + .xs\:bui-pr-14 { + padding-right: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-pr { + .sm\:bui-pr { padding-right: var(--pr-sm); } - .sm\:cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); + .sm\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } - .sm\:cu-pr-1 { - padding-right: var(--canon-space-1); + .sm\:bui-pr-1 { + padding-right: var(--bui-space-1); } - .sm\:cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); + .sm\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } - .sm\:cu-pr-2 { - padding-right: var(--canon-space-2); + .sm\:bui-pr-2 { + padding-right: var(--bui-space-2); } - .sm\:cu-pr-3 { - padding-right: var(--canon-space-3); + .sm\:bui-pr-3 { + padding-right: var(--bui-space-3); } - .sm\:cu-pr-4 { - padding-right: var(--canon-space-4); + .sm\:bui-pr-4 { + padding-right: var(--bui-space-4); } - .sm\:cu-pr-5 { - padding-right: var(--canon-space-5); + .sm\:bui-pr-5 { + padding-right: var(--bui-space-5); } - .sm\:cu-pr-6 { - padding-right: var(--canon-space-6); + .sm\:bui-pr-6 { + padding-right: var(--bui-space-6); } - .sm\:cu-pr-7 { - padding-right: var(--canon-space-7); + .sm\:bui-pr-7 { + padding-right: var(--bui-space-7); } - .sm\:cu-pr-8 { - padding-right: var(--canon-space-8); + .sm\:bui-pr-8 { + padding-right: var(--bui-space-8); } - .sm\:cu-pr-9 { - padding-right: var(--canon-space-9); + .sm\:bui-pr-9 { + padding-right: var(--bui-space-9); } - .sm\:cu-pr-10 { - padding-right: var(--canon-space-10); + .sm\:bui-pr-10 { + padding-right: var(--bui-space-10); } - .sm\:cu-pr-11 { - padding-right: var(--canon-space-11); + .sm\:bui-pr-11 { + padding-right: var(--bui-space-11); } - .sm\:cu-pr-12 { - padding-right: var(--canon-space-12); + .sm\:bui-pr-12 { + padding-right: var(--bui-space-12); } - .sm\:cu-pr-13 { - padding-right: var(--canon-space-13); + .sm\:bui-pr-13 { + padding-right: var(--bui-space-13); } - .sm\:cu-pr-14 { - padding-right: var(--canon-space-14); + .sm\:bui-pr-14 { + padding-right: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-pr { + .md\:bui-pr { padding-right: var(--pr-md); } - .md\:cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); + .md\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } - .md\:cu-pr-1 { - padding-right: var(--canon-space-1); + .md\:bui-pr-1 { + padding-right: var(--bui-space-1); } - .md\:cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); + .md\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } - .md\:cu-pr-2 { - padding-right: var(--canon-space-2); + .md\:bui-pr-2 { + padding-right: var(--bui-space-2); } - .md\:cu-pr-3 { - padding-right: var(--canon-space-3); + .md\:bui-pr-3 { + padding-right: var(--bui-space-3); } - .md\:cu-pr-4 { - padding-right: var(--canon-space-4); + .md\:bui-pr-4 { + padding-right: var(--bui-space-4); } - .md\:cu-pr-5 { - padding-right: var(--canon-space-5); + .md\:bui-pr-5 { + padding-right: var(--bui-space-5); } - .md\:cu-pr-6 { - padding-right: var(--canon-space-6); + .md\:bui-pr-6 { + padding-right: var(--bui-space-6); } - .md\:cu-pr-7 { - padding-right: var(--canon-space-7); + .md\:bui-pr-7 { + padding-right: var(--bui-space-7); } - .md\:cu-pr-8 { - padding-right: var(--canon-space-8); + .md\:bui-pr-8 { + padding-right: var(--bui-space-8); } - .md\:cu-pr-9 { - padding-right: var(--canon-space-9); + .md\:bui-pr-9 { + padding-right: var(--bui-space-9); } - .md\:cu-pr-10 { - padding-right: var(--canon-space-10); + .md\:bui-pr-10 { + padding-right: var(--bui-space-10); } - .md\:cu-pr-11 { - padding-right: var(--canon-space-11); + .md\:bui-pr-11 { + padding-right: var(--bui-space-11); } - .md\:cu-pr-12 { - padding-right: var(--canon-space-12); + .md\:bui-pr-12 { + padding-right: var(--bui-space-12); } - .md\:cu-pr-13 { - padding-right: var(--canon-space-13); + .md\:bui-pr-13 { + padding-right: var(--bui-space-13); } - .md\:cu-pr-14 { - padding-right: var(--canon-space-14); + .md\:bui-pr-14 { + padding-right: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-pr { + .lg\:bui-pr { padding-right: var(--pr-lg); } - .lg\:cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); + .lg\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } - .lg\:cu-pr-1 { - padding-right: var(--canon-space-1); + .lg\:bui-pr-1 { + padding-right: var(--bui-space-1); } - .lg\:cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); + .lg\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } - .lg\:cu-pr-2 { - padding-right: var(--canon-space-2); + .lg\:bui-pr-2 { + padding-right: var(--bui-space-2); } - .lg\:cu-pr-3 { - padding-right: var(--canon-space-3); + .lg\:bui-pr-3 { + padding-right: var(--bui-space-3); } - .lg\:cu-pr-4 { - padding-right: var(--canon-space-4); + .lg\:bui-pr-4 { + padding-right: var(--bui-space-4); } - .lg\:cu-pr-5 { - padding-right: var(--canon-space-5); + .lg\:bui-pr-5 { + padding-right: var(--bui-space-5); } - .lg\:cu-pr-6 { - padding-right: var(--canon-space-6); + .lg\:bui-pr-6 { + padding-right: var(--bui-space-6); } - .lg\:cu-pr-7 { - padding-right: var(--canon-space-7); + .lg\:bui-pr-7 { + padding-right: var(--bui-space-7); } - .lg\:cu-pr-8 { - padding-right: var(--canon-space-8); + .lg\:bui-pr-8 { + padding-right: var(--bui-space-8); } - .lg\:cu-pr-9 { - padding-right: var(--canon-space-9); + .lg\:bui-pr-9 { + padding-right: var(--bui-space-9); } - .lg\:cu-pr-10 { - padding-right: var(--canon-space-10); + .lg\:bui-pr-10 { + padding-right: var(--bui-space-10); } - .lg\:cu-pr-11 { - padding-right: var(--canon-space-11); + .lg\:bui-pr-11 { + padding-right: var(--bui-space-11); } - .lg\:cu-pr-12 { - padding-right: var(--canon-space-12); + .lg\:bui-pr-12 { + padding-right: var(--bui-space-12); } - .lg\:cu-pr-13 { - padding-right: var(--canon-space-13); + .lg\:bui-pr-13 { + padding-right: var(--bui-space-13); } - .lg\:cu-pr-14 { - padding-right: var(--canon-space-14); + .lg\:bui-pr-14 { + padding-right: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-pr { + .xl\:bui-pr { padding-right: var(--pr-xl); } - .xl\:cu-pr-0\.5 { - padding-right: var(--canon-space-0_5); + .xl\:bui-pr-0\.5 { + padding-right: var(--bui-space-0_5); } - .xl\:cu-pr-1 { - padding-right: var(--canon-space-1); + .xl\:bui-pr-1 { + padding-right: var(--bui-space-1); } - .xl\:cu-pr-1\.5 { - padding-right: var(--canon-space-1_5); + .xl\:bui-pr-1\.5 { + padding-right: var(--bui-space-1_5); } - .xl\:cu-pr-2 { - padding-right: var(--canon-space-2); + .xl\:bui-pr-2 { + padding-right: var(--bui-space-2); } - .xl\:cu-pr-3 { - padding-right: var(--canon-space-3); + .xl\:bui-pr-3 { + padding-right: var(--bui-space-3); } - .xl\:cu-pr-4 { - padding-right: var(--canon-space-4); + .xl\:bui-pr-4 { + padding-right: var(--bui-space-4); } - .xl\:cu-pr-5 { - padding-right: var(--canon-space-5); + .xl\:bui-pr-5 { + padding-right: var(--bui-space-5); } - .xl\:cu-pr-6 { - padding-right: var(--canon-space-6); + .xl\:bui-pr-6 { + padding-right: var(--bui-space-6); } - .xl\:cu-pr-7 { - padding-right: var(--canon-space-7); + .xl\:bui-pr-7 { + padding-right: var(--bui-space-7); } - .xl\:cu-pr-8 { - padding-right: var(--canon-space-8); + .xl\:bui-pr-8 { + padding-right: var(--bui-space-8); } - .xl\:cu-pr-9 { - padding-right: var(--canon-space-9); + .xl\:bui-pr-9 { + padding-right: var(--bui-space-9); } - .xl\:cu-pr-10 { - padding-right: var(--canon-space-10); + .xl\:bui-pr-10 { + padding-right: var(--bui-space-10); } - .xl\:cu-pr-11 { - padding-right: var(--canon-space-11); + .xl\:bui-pr-11 { + padding-right: var(--bui-space-11); } - .xl\:cu-pr-12 { - padding-right: var(--canon-space-12); + .xl\:bui-pr-12 { + padding-right: var(--bui-space-12); } - .xl\:cu-pr-13 { - padding-right: var(--canon-space-13); + .xl\:bui-pr-13 { + padding-right: var(--bui-space-13); } - .xl\:cu-pr-14 { - padding-right: var(--canon-space-14); + .xl\:bui-pr-14 { + padding-right: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/pt.css b/packages/canon/src/css/utilities/pt.css index 900f412892..cf557257e3 100644 --- a/packages/canon/src/css/utilities/pt.css +++ b/packages/canon/src/css/utilities/pt.css @@ -1,422 +1,422 @@ -.cu-pt { +.bui-pt { padding-top: var(--pt); } -.cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); +.bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } -.cu-pt-1 { - padding-top: var(--canon-space-1); +.bui-pt-1 { + padding-top: var(--bui-space-1); } -.cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); +.bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } -.cu-pt-2 { - padding-top: var(--canon-space-2); +.bui-pt-2 { + padding-top: var(--bui-space-2); } -.cu-pt-3 { - padding-top: var(--canon-space-3); +.bui-pt-3 { + padding-top: var(--bui-space-3); } -.cu-pt-4 { - padding-top: var(--canon-space-4); +.bui-pt-4 { + padding-top: var(--bui-space-4); } -.cu-pt-5 { - padding-top: var(--canon-space-5); +.bui-pt-5 { + padding-top: var(--bui-space-5); } -.cu-pt-6 { - padding-top: var(--canon-space-6); +.bui-pt-6 { + padding-top: var(--bui-space-6); } -.cu-pt-7 { - padding-top: var(--canon-space-7); +.bui-pt-7 { + padding-top: var(--bui-space-7); } -.cu-pt-8 { - padding-top: var(--canon-space-8); +.bui-pt-8 { + padding-top: var(--bui-space-8); } -.cu-pt-9 { - padding-top: var(--canon-space-9); +.bui-pt-9 { + padding-top: var(--bui-space-9); } -.cu-pt-10 { - padding-top: var(--canon-space-10); +.bui-pt-10 { + padding-top: var(--bui-space-10); } -.cu-pt-11 { - padding-top: var(--canon-space-11); +.bui-pt-11 { + padding-top: var(--bui-space-11); } -.cu-pt-12 { - padding-top: var(--canon-space-12); +.bui-pt-12 { + padding-top: var(--bui-space-12); } -.cu-pt-13 { - padding-top: var(--canon-space-13); +.bui-pt-13 { + padding-top: var(--bui-space-13); } -.cu-pt-14 { - padding-top: var(--canon-space-14); +.bui-pt-14 { + padding-top: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-pt { + .xs\:bui-pt { padding-top: var(--pt-xs); } - .xs\:cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); + .xs\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } - .xs\:cu-pt-1 { - padding-top: var(--canon-space-1); + .xs\:bui-pt-1 { + padding-top: var(--bui-space-1); } - .xs\:cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); + .xs\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } - .xs\:cu-pt-2 { - padding-top: var(--canon-space-2); + .xs\:bui-pt-2 { + padding-top: var(--bui-space-2); } - .xs\:cu-pt-3 { - padding-top: var(--canon-space-3); + .xs\:bui-pt-3 { + padding-top: var(--bui-space-3); } - .xs\:cu-pt-4 { - padding-top: var(--canon-space-4); + .xs\:bui-pt-4 { + padding-top: var(--bui-space-4); } - .xs\:cu-pt-5 { - padding-top: var(--canon-space-5); + .xs\:bui-pt-5 { + padding-top: var(--bui-space-5); } - .xs\:cu-pt-6 { - padding-top: var(--canon-space-6); + .xs\:bui-pt-6 { + padding-top: var(--bui-space-6); } - .xs\:cu-pt-7 { - padding-top: var(--canon-space-7); + .xs\:bui-pt-7 { + padding-top: var(--bui-space-7); } - .xs\:cu-pt-8 { - padding-top: var(--canon-space-8); + .xs\:bui-pt-8 { + padding-top: var(--bui-space-8); } - .xs\:cu-pt-9 { - padding-top: var(--canon-space-9); + .xs\:bui-pt-9 { + padding-top: var(--bui-space-9); } - .xs\:cu-pt-10 { - padding-top: var(--canon-space-10); + .xs\:bui-pt-10 { + padding-top: var(--bui-space-10); } - .xs\:cu-pt-11 { - padding-top: var(--canon-space-11); + .xs\:bui-pt-11 { + padding-top: var(--bui-space-11); } - .xs\:cu-pt-12 { - padding-top: var(--canon-space-12); + .xs\:bui-pt-12 { + padding-top: var(--bui-space-12); } - .xs\:cu-pt-13 { - padding-top: var(--canon-space-13); + .xs\:bui-pt-13 { + padding-top: var(--bui-space-13); } - .xs\:cu-pt-14 { - padding-top: var(--canon-space-14); + .xs\:bui-pt-14 { + padding-top: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-pt { + .sm\:bui-pt { padding-top: var(--pt-sm); } - .sm\:cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); + .sm\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } - .sm\:cu-pt-1 { - padding-top: var(--canon-space-1); + .sm\:bui-pt-1 { + padding-top: var(--bui-space-1); } - .sm\:cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); + .sm\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } - .sm\:cu-pt-2 { - padding-top: var(--canon-space-2); + .sm\:bui-pt-2 { + padding-top: var(--bui-space-2); } - .sm\:cu-pt-3 { - padding-top: var(--canon-space-3); + .sm\:bui-pt-3 { + padding-top: var(--bui-space-3); } - .sm\:cu-pt-4 { - padding-top: var(--canon-space-4); + .sm\:bui-pt-4 { + padding-top: var(--bui-space-4); } - .sm\:cu-pt-5 { - padding-top: var(--canon-space-5); + .sm\:bui-pt-5 { + padding-top: var(--bui-space-5); } - .sm\:cu-pt-6 { - padding-top: var(--canon-space-6); + .sm\:bui-pt-6 { + padding-top: var(--bui-space-6); } - .sm\:cu-pt-7 { - padding-top: var(--canon-space-7); + .sm\:bui-pt-7 { + padding-top: var(--bui-space-7); } - .sm\:cu-pt-8 { - padding-top: var(--canon-space-8); + .sm\:bui-pt-8 { + padding-top: var(--bui-space-8); } - .sm\:cu-pt-9 { - padding-top: var(--canon-space-9); + .sm\:bui-pt-9 { + padding-top: var(--bui-space-9); } - .sm\:cu-pt-10 { - padding-top: var(--canon-space-10); + .sm\:bui-pt-10 { + padding-top: var(--bui-space-10); } - .sm\:cu-pt-11 { - padding-top: var(--canon-space-11); + .sm\:bui-pt-11 { + padding-top: var(--bui-space-11); } - .sm\:cu-pt-12 { - padding-top: var(--canon-space-12); + .sm\:bui-pt-12 { + padding-top: var(--bui-space-12); } - .sm\:cu-pt-13 { - padding-top: var(--canon-space-13); + .sm\:bui-pt-13 { + padding-top: var(--bui-space-13); } - .sm\:cu-pt-14 { - padding-top: var(--canon-space-14); + .sm\:bui-pt-14 { + padding-top: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-pt { + .md\:bui-pt { padding-top: var(--pt-md); } - .md\:cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); + .md\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } - .md\:cu-pt-1 { - padding-top: var(--canon-space-1); + .md\:bui-pt-1 { + padding-top: var(--bui-space-1); } - .md\:cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); + .md\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } - .md\:cu-pt-2 { - padding-top: var(--canon-space-2); + .md\:bui-pt-2 { + padding-top: var(--bui-space-2); } - .md\:cu-pt-3 { - padding-top: var(--canon-space-3); + .md\:bui-pt-3 { + padding-top: var(--bui-space-3); } - .md\:cu-pt-4 { - padding-top: var(--canon-space-4); + .md\:bui-pt-4 { + padding-top: var(--bui-space-4); } - .md\:cu-pt-5 { - padding-top: var(--canon-space-5); + .md\:bui-pt-5 { + padding-top: var(--bui-space-5); } - .md\:cu-pt-6 { - padding-top: var(--canon-space-6); + .md\:bui-pt-6 { + padding-top: var(--bui-space-6); } - .md\:cu-pt-7 { - padding-top: var(--canon-space-7); + .md\:bui-pt-7 { + padding-top: var(--bui-space-7); } - .md\:cu-pt-8 { - padding-top: var(--canon-space-8); + .md\:bui-pt-8 { + padding-top: var(--bui-space-8); } - .md\:cu-pt-9 { - padding-top: var(--canon-space-9); + .md\:bui-pt-9 { + padding-top: var(--bui-space-9); } - .md\:cu-pt-10 { - padding-top: var(--canon-space-10); + .md\:bui-pt-10 { + padding-top: var(--bui-space-10); } - .md\:cu-pt-11 { - padding-top: var(--canon-space-11); + .md\:bui-pt-11 { + padding-top: var(--bui-space-11); } - .md\:cu-pt-12 { - padding-top: var(--canon-space-12); + .md\:bui-pt-12 { + padding-top: var(--bui-space-12); } - .md\:cu-pt-13 { - padding-top: var(--canon-space-13); + .md\:bui-pt-13 { + padding-top: var(--bui-space-13); } - .md\:cu-pt-14 { - padding-top: var(--canon-space-14); + .md\:bui-pt-14 { + padding-top: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-pt { + .lg\:bui-pt { padding-top: var(--pt-lg); } - .lg\:cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); + .lg\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } - .lg\:cu-pt-1 { - padding-top: var(--canon-space-1); + .lg\:bui-pt-1 { + padding-top: var(--bui-space-1); } - .lg\:cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); + .lg\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } - .lg\:cu-pt-2 { - padding-top: var(--canon-space-2); + .lg\:bui-pt-2 { + padding-top: var(--bui-space-2); } - .lg\:cu-pt-3 { - padding-top: var(--canon-space-3); + .lg\:bui-pt-3 { + padding-top: var(--bui-space-3); } - .lg\:cu-pt-4 { - padding-top: var(--canon-space-4); + .lg\:bui-pt-4 { + padding-top: var(--bui-space-4); } - .lg\:cu-pt-5 { - padding-top: var(--canon-space-5); + .lg\:bui-pt-5 { + padding-top: var(--bui-space-5); } - .lg\:cu-pt-6 { - padding-top: var(--canon-space-6); + .lg\:bui-pt-6 { + padding-top: var(--bui-space-6); } - .lg\:cu-pt-7 { - padding-top: var(--canon-space-7); + .lg\:bui-pt-7 { + padding-top: var(--bui-space-7); } - .lg\:cu-pt-8 { - padding-top: var(--canon-space-8); + .lg\:bui-pt-8 { + padding-top: var(--bui-space-8); } - .lg\:cu-pt-9 { - padding-top: var(--canon-space-9); + .lg\:bui-pt-9 { + padding-top: var(--bui-space-9); } - .lg\:cu-pt-10 { - padding-top: var(--canon-space-10); + .lg\:bui-pt-10 { + padding-top: var(--bui-space-10); } - .lg\:cu-pt-11 { - padding-top: var(--canon-space-11); + .lg\:bui-pt-11 { + padding-top: var(--bui-space-11); } - .lg\:cu-pt-12 { - padding-top: var(--canon-space-12); + .lg\:bui-pt-12 { + padding-top: var(--bui-space-12); } - .lg\:cu-pt-13 { - padding-top: var(--canon-space-13); + .lg\:bui-pt-13 { + padding-top: var(--bui-space-13); } - .lg\:cu-pt-14 { - padding-top: var(--canon-space-14); + .lg\:bui-pt-14 { + padding-top: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-pt { + .xl\:bui-pt { padding-top: var(--pt-xl); } - .xl\:cu-pt-0\.5 { - padding-top: var(--canon-space-0_5); + .xl\:bui-pt-0\.5 { + padding-top: var(--bui-space-0_5); } - .xl\:cu-pt-1 { - padding-top: var(--canon-space-1); + .xl\:bui-pt-1 { + padding-top: var(--bui-space-1); } - .xl\:cu-pt-1\.5 { - padding-top: var(--canon-space-1_5); + .xl\:bui-pt-1\.5 { + padding-top: var(--bui-space-1_5); } - .xl\:cu-pt-2 { - padding-top: var(--canon-space-2); + .xl\:bui-pt-2 { + padding-top: var(--bui-space-2); } - .xl\:cu-pt-3 { - padding-top: var(--canon-space-3); + .xl\:bui-pt-3 { + padding-top: var(--bui-space-3); } - .xl\:cu-pt-4 { - padding-top: var(--canon-space-4); + .xl\:bui-pt-4 { + padding-top: var(--bui-space-4); } - .xl\:cu-pt-5 { - padding-top: var(--canon-space-5); + .xl\:bui-pt-5 { + padding-top: var(--bui-space-5); } - .xl\:cu-pt-6 { - padding-top: var(--canon-space-6); + .xl\:bui-pt-6 { + padding-top: var(--bui-space-6); } - .xl\:cu-pt-7 { - padding-top: var(--canon-space-7); + .xl\:bui-pt-7 { + padding-top: var(--bui-space-7); } - .xl\:cu-pt-8 { - padding-top: var(--canon-space-8); + .xl\:bui-pt-8 { + padding-top: var(--bui-space-8); } - .xl\:cu-pt-9 { - padding-top: var(--canon-space-9); + .xl\:bui-pt-9 { + padding-top: var(--bui-space-9); } - .xl\:cu-pt-10 { - padding-top: var(--canon-space-10); + .xl\:bui-pt-10 { + padding-top: var(--bui-space-10); } - .xl\:cu-pt-11 { - padding-top: var(--canon-space-11); + .xl\:bui-pt-11 { + padding-top: var(--bui-space-11); } - .xl\:cu-pt-12 { - padding-top: var(--canon-space-12); + .xl\:bui-pt-12 { + padding-top: var(--bui-space-12); } - .xl\:cu-pt-13 { - padding-top: var(--canon-space-13); + .xl\:bui-pt-13 { + padding-top: var(--bui-space-13); } - .xl\:cu-pt-14 { - padding-top: var(--canon-space-14); + .xl\:bui-pt-14 { + padding-top: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/px.css b/packages/canon/src/css/utilities/px.css index 84fd30aaf8..37a41d832e 100644 --- a/packages/canon/src/css/utilities/px.css +++ b/packages/canon/src/css/utilities/px.css @@ -1,524 +1,524 @@ -.cu-px { +.bui-px { padding-left: var(--px); padding-right: var(--px); } -.cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); +.bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } -.cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); +.bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } -.cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); +.bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } -.cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); +.bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } -.cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); +.bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } -.cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); +.bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } -.cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); +.bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } -.cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); +.bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } -.cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); +.bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } -.cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); +.bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } -.cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); +.bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } -.cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); +.bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } -.cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); +.bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } -.cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); +.bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } -.cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); +.bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } -.cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); +.bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-px { + .xs\:bui-px { padding-left: var(--px-xs); padding-right: var(--px-xs); } - .xs\:cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); + .xs\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } - .xs\:cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); + .xs\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } - .xs\:cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); + .xs\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } - .xs\:cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); + .xs\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } - .xs\:cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); + .xs\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } - .xs\:cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); + .xs\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } - .xs\:cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); + .xs\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } - .xs\:cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); + .xs\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } - .xs\:cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); + .xs\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } - .xs\:cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); + .xs\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } - .xs\:cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); + .xs\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } - .xs\:cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); + .xs\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } - .xs\:cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); + .xs\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } - .xs\:cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); + .xs\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } - .xs\:cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); + .xs\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } - .xs\:cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); + .xs\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-px { + .sm\:bui-px { padding-left: var(--px-sm); padding-right: var(--px-sm); } - .sm\:cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); + .sm\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } - .sm\:cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); + .sm\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } - .sm\:cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); + .sm\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } - .sm\:cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); + .sm\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } - .sm\:cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); + .sm\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } - .sm\:cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); + .sm\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } - .sm\:cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); + .sm\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } - .sm\:cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); + .sm\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } - .sm\:cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); + .sm\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } - .sm\:cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); + .sm\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } - .sm\:cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); + .sm\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } - .sm\:cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); + .sm\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } - .sm\:cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); + .sm\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } - .sm\:cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); + .sm\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } - .sm\:cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); + .sm\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } - .sm\:cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); + .sm\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-px { + .md\:bui-px { padding-left: var(--px-md); padding-right: var(--px-md); } - .md\:cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); + .md\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } - .md\:cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); + .md\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } - .md\:cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); + .md\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } - .md\:cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); + .md\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } - .md\:cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); + .md\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } - .md\:cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); + .md\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } - .md\:cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); + .md\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } - .md\:cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); + .md\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } - .md\:cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); + .md\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } - .md\:cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); + .md\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } - .md\:cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); + .md\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } - .md\:cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); + .md\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } - .md\:cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); + .md\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } - .md\:cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); + .md\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } - .md\:cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); + .md\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } - .md\:cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); + .md\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-px { + .lg\:bui-px { padding-left: var(--px-lg); padding-right: var(--px-lg); } - .lg\:cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); + .lg\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } - .lg\:cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); + .lg\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } - .lg\:cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); + .lg\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } - .lg\:cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); + .lg\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } - .lg\:cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); + .lg\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } - .lg\:cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); + .lg\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } - .lg\:cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); + .lg\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } - .lg\:cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); + .lg\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } - .lg\:cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); + .lg\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } - .lg\:cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); + .lg\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } - .lg\:cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); + .lg\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } - .lg\:cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); + .lg\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } - .lg\:cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); + .lg\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } - .lg\:cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); + .lg\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } - .lg\:cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); + .lg\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } - .lg\:cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); + .lg\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-px { + .xl\:bui-px { padding-left: var(--px-xl); padding-right: var(--px-xl); } - .xl\:cu-px-0\.5 { - padding-left: var(--canon-space-0_5); - padding-right: var(--canon-space-0_5); + .xl\:bui-px-0\.5 { + padding-left: var(--bui-space-0_5); + padding-right: var(--bui-space-0_5); } - .xl\:cu-px-1 { - padding-left: var(--canon-space-1); - padding-right: var(--canon-space-1); + .xl\:bui-px-1 { + padding-left: var(--bui-space-1); + padding-right: var(--bui-space-1); } - .xl\:cu-px-1\.5 { - padding-left: var(--canon-space-1_5); - padding-right: var(--canon-space-1_5); + .xl\:bui-px-1\.5 { + padding-left: var(--bui-space-1_5); + padding-right: var(--bui-space-1_5); } - .xl\:cu-px-2 { - padding-left: var(--canon-space-2); - padding-right: var(--canon-space-2); + .xl\:bui-px-2 { + padding-left: var(--bui-space-2); + padding-right: var(--bui-space-2); } - .xl\:cu-px-3 { - padding-left: var(--canon-space-3); - padding-right: var(--canon-space-3); + .xl\:bui-px-3 { + padding-left: var(--bui-space-3); + padding-right: var(--bui-space-3); } - .xl\:cu-px-4 { - padding-left: var(--canon-space-4); - padding-right: var(--canon-space-4); + .xl\:bui-px-4 { + padding-left: var(--bui-space-4); + padding-right: var(--bui-space-4); } - .xl\:cu-px-5 { - padding-left: var(--canon-space-5); - padding-right: var(--canon-space-5); + .xl\:bui-px-5 { + padding-left: var(--bui-space-5); + padding-right: var(--bui-space-5); } - .xl\:cu-px-6 { - padding-left: var(--canon-space-6); - padding-right: var(--canon-space-6); + .xl\:bui-px-6 { + padding-left: var(--bui-space-6); + padding-right: var(--bui-space-6); } - .xl\:cu-px-7 { - padding-left: var(--canon-space-7); - padding-right: var(--canon-space-7); + .xl\:bui-px-7 { + padding-left: var(--bui-space-7); + padding-right: var(--bui-space-7); } - .xl\:cu-px-8 { - padding-left: var(--canon-space-8); - padding-right: var(--canon-space-8); + .xl\:bui-px-8 { + padding-left: var(--bui-space-8); + padding-right: var(--bui-space-8); } - .xl\:cu-px-9 { - padding-left: var(--canon-space-9); - padding-right: var(--canon-space-9); + .xl\:bui-px-9 { + padding-left: var(--bui-space-9); + padding-right: var(--bui-space-9); } - .xl\:cu-px-10 { - padding-left: var(--canon-space-10); - padding-right: var(--canon-space-10); + .xl\:bui-px-10 { + padding-left: var(--bui-space-10); + padding-right: var(--bui-space-10); } - .xl\:cu-px-11 { - padding-left: var(--canon-space-11); - padding-right: var(--canon-space-11); + .xl\:bui-px-11 { + padding-left: var(--bui-space-11); + padding-right: var(--bui-space-11); } - .xl\:cu-px-12 { - padding-left: var(--canon-space-12); - padding-right: var(--canon-space-12); + .xl\:bui-px-12 { + padding-left: var(--bui-space-12); + padding-right: var(--bui-space-12); } - .xl\:cu-px-13 { - padding-left: var(--canon-space-13); - padding-right: var(--canon-space-13); + .xl\:bui-px-13 { + padding-left: var(--bui-space-13); + padding-right: var(--bui-space-13); } - .xl\:cu-px-14 { - padding-left: var(--canon-space-14); - padding-right: var(--canon-space-14); + .xl\:bui-px-14 { + padding-left: var(--bui-space-14); + padding-right: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/py.css b/packages/canon/src/css/utilities/py.css index 233794f888..e3738df00c 100644 --- a/packages/canon/src/css/utilities/py.css +++ b/packages/canon/src/css/utilities/py.css @@ -1,524 +1,524 @@ -.cu-py { +.bui-py { padding-top: var(--py); padding-bottom: var(--py); } -.cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); +.bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } -.cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); +.bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } -.cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); +.bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } -.cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); +.bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } -.cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); +.bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } -.cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); +.bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } -.cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); +.bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } -.cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); +.bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } -.cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); +.bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } -.cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); +.bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } -.cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); +.bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } -.cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); +.bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } -.cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); +.bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } -.cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); +.bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } -.cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); +.bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } -.cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); +.bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-py { + .xs\:bui-py { padding-top: var(--py-xs); padding-bottom: var(--py-xs); } - .xs\:cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); + .xs\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } - .xs\:cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); + .xs\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } - .xs\:cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); + .xs\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } - .xs\:cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); + .xs\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } - .xs\:cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); + .xs\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } - .xs\:cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); + .xs\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } - .xs\:cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); + .xs\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } - .xs\:cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); + .xs\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } - .xs\:cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); + .xs\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } - .xs\:cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); + .xs\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } - .xs\:cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); + .xs\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } - .xs\:cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); + .xs\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } - .xs\:cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); + .xs\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } - .xs\:cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); + .xs\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } - .xs\:cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); + .xs\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } - .xs\:cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); + .xs\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-py { + .sm\:bui-py { padding-top: var(--py-sm); padding-bottom: var(--py-sm); } - .sm\:cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); + .sm\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } - .sm\:cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); + .sm\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } - .sm\:cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); + .sm\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } - .sm\:cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); + .sm\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } - .sm\:cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); + .sm\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } - .sm\:cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); + .sm\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } - .sm\:cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); + .sm\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } - .sm\:cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); + .sm\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } - .sm\:cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); + .sm\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } - .sm\:cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); + .sm\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } - .sm\:cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); + .sm\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } - .sm\:cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); + .sm\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } - .sm\:cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); + .sm\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } - .sm\:cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); + .sm\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } - .sm\:cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); + .sm\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } - .sm\:cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); + .sm\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-py { + .md\:bui-py { padding-top: var(--py-md); padding-bottom: var(--py-md); } - .md\:cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); + .md\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } - .md\:cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); + .md\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } - .md\:cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); + .md\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } - .md\:cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); + .md\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } - .md\:cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); + .md\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } - .md\:cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); + .md\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } - .md\:cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); + .md\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } - .md\:cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); + .md\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } - .md\:cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); + .md\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } - .md\:cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); + .md\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } - .md\:cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); + .md\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } - .md\:cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); + .md\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } - .md\:cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); + .md\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } - .md\:cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); + .md\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } - .md\:cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); + .md\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } - .md\:cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); + .md\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-py { + .lg\:bui-py { padding-top: var(--py-lg); padding-bottom: var(--py-lg); } - .lg\:cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); + .lg\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } - .lg\:cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); + .lg\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } - .lg\:cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); + .lg\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } - .lg\:cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); + .lg\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } - .lg\:cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); + .lg\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } - .lg\:cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); + .lg\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } - .lg\:cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); + .lg\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } - .lg\:cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); + .lg\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } - .lg\:cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); + .lg\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } - .lg\:cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); + .lg\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } - .lg\:cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); + .lg\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } - .lg\:cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); + .lg\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } - .lg\:cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); + .lg\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } - .lg\:cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); + .lg\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } - .lg\:cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); + .lg\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } - .lg\:cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); + .lg\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-py { + .xl\:bui-py { padding-top: var(--py-xl); padding-bottom: var(--py-xl); } - .xl\:cu-py-0\.5 { - padding-top: var(--canon-space-0_5); - padding-bottom: var(--canon-space-0_5); + .xl\:bui-py-0\.5 { + padding-top: var(--bui-space-0_5); + padding-bottom: var(--bui-space-0_5); } - .xl\:cu-py-1 { - padding-top: var(--canon-space-1); - padding-bottom: var(--canon-space-1); + .xl\:bui-py-1 { + padding-top: var(--bui-space-1); + padding-bottom: var(--bui-space-1); } - .xl\:cu-py-1\.5 { - padding-top: var(--canon-space-1_5); - padding-bottom: var(--canon-space-1_5); + .xl\:bui-py-1\.5 { + padding-top: var(--bui-space-1_5); + padding-bottom: var(--bui-space-1_5); } - .xl\:cu-py-2 { - padding-top: var(--canon-space-2); - padding-bottom: var(--canon-space-2); + .xl\:bui-py-2 { + padding-top: var(--bui-space-2); + padding-bottom: var(--bui-space-2); } - .xl\:cu-py-3 { - padding-top: var(--canon-space-3); - padding-bottom: var(--canon-space-3); + .xl\:bui-py-3 { + padding-top: var(--bui-space-3); + padding-bottom: var(--bui-space-3); } - .xl\:cu-py-4 { - padding-top: var(--canon-space-4); - padding-bottom: var(--canon-space-4); + .xl\:bui-py-4 { + padding-top: var(--bui-space-4); + padding-bottom: var(--bui-space-4); } - .xl\:cu-py-5 { - padding-top: var(--canon-space-5); - padding-bottom: var(--canon-space-5); + .xl\:bui-py-5 { + padding-top: var(--bui-space-5); + padding-bottom: var(--bui-space-5); } - .xl\:cu-py-6 { - padding-top: var(--canon-space-6); - padding-bottom: var(--canon-space-6); + .xl\:bui-py-6 { + padding-top: var(--bui-space-6); + padding-bottom: var(--bui-space-6); } - .xl\:cu-py-7 { - padding-top: var(--canon-space-7); - padding-bottom: var(--canon-space-7); + .xl\:bui-py-7 { + padding-top: var(--bui-space-7); + padding-bottom: var(--bui-space-7); } - .xl\:cu-py-8 { - padding-top: var(--canon-space-8); - padding-bottom: var(--canon-space-8); + .xl\:bui-py-8 { + padding-top: var(--bui-space-8); + padding-bottom: var(--bui-space-8); } - .xl\:cu-py-9 { - padding-top: var(--canon-space-9); - padding-bottom: var(--canon-space-9); + .xl\:bui-py-9 { + padding-top: var(--bui-space-9); + padding-bottom: var(--bui-space-9); } - .xl\:cu-py-10 { - padding-top: var(--canon-space-10); - padding-bottom: var(--canon-space-10); + .xl\:bui-py-10 { + padding-top: var(--bui-space-10); + padding-bottom: var(--bui-space-10); } - .xl\:cu-py-11 { - padding-top: var(--canon-space-11); - padding-bottom: var(--canon-space-11); + .xl\:bui-py-11 { + padding-top: var(--bui-space-11); + padding-bottom: var(--bui-space-11); } - .xl\:cu-py-12 { - padding-top: var(--canon-space-12); - padding-bottom: var(--canon-space-12); + .xl\:bui-py-12 { + padding-top: var(--bui-space-12); + padding-bottom: var(--bui-space-12); } - .xl\:cu-py-13 { - padding-top: var(--canon-space-13); - padding-bottom: var(--canon-space-13); + .xl\:bui-py-13 { + padding-top: var(--bui-space-13); + padding-bottom: var(--bui-space-13); } - .xl\:cu-py-14 { - padding-top: var(--canon-space-14); - padding-bottom: var(--canon-space-14); + .xl\:bui-py-14 { + padding-top: var(--bui-space-14); + padding-bottom: var(--bui-space-14); } } diff --git a/packages/canon/src/css/utilities/sm.css b/packages/canon/src/css/utilities/sm.css index 01603f0218..61baaa6b0d 100644 --- a/packages/canon/src/css/utilities/sm.css +++ b/packages/canon/src/css/utilities/sm.css @@ -16,176 +16,176 @@ @layer utilities { @media (min-width: 640px) { - .cu-sm-block { + .bui-sm-block { display: block; } - .cu-sm-border-base { - border-color: var(--canon-border); + .bui-sm-border-base { + border-color: var(--bui-border); border-width: 1px; border-style: solid; } - .cu-sm-border-error { - border-color: var(--canon-border-danger); + .bui-sm-border-error { + border-color: var(--bui-border-danger); border-width: 1px; border-style: solid; } - .cu-sm-border-none { + .bui-sm-border-none { border-color: transparent; border-width: 0; } - .cu-sm-border-selected { - border-color: var(--canon-border-pressed); + .bui-sm-border-selected { + border-color: var(--bui-border-pressed); border-width: 1px; border-style: solid; } - .cu-sm-border-warning { - border-color: var(--canon-border-warning); + .bui-sm-border-warning { + border-color: var(--bui-border-warning); border-width: 1px; border-style: solid; } - .cu-sm-flex { + .bui-sm-flex { display: flex; } - .cu-sm-flex-col { + .bui-sm-flex-col { flex-direction: column; } - .cu-sm-flex-nowrap { + .bui-sm-flex-nowrap { flex-wrap: nowrap; } - .cu-sm-flex-row { + .bui-sm-flex-row { flex-direction: row; } - .cu-sm-flex-wrap { + .bui-sm-flex-wrap { flex-wrap: wrap; } - .cu-sm-flex-wrap-reverse { + .bui-sm-flex-wrap-reverse { flex-wrap: wrap-reverse; } - .cu-sm-gap-2xl { - gap: var(--canon-space-12); + .bui-sm-gap-2xl { + gap: var(--bui-space-12); } - .cu-sm-gap-lg { - gap: var(--canon-space-8); + .bui-sm-gap-lg { + gap: var(--bui-space-8); } - .cu-sm-gap-md { - gap: var(--canon-space-6); + .bui-sm-gap-md { + gap: var(--bui-space-6); } - .cu-sm-gap-none { + .bui-sm-gap-none { gap: 0; } - .cu-sm-gap-sm { - gap: var(--canon-space-4); + .bui-sm-gap-sm { + gap: var(--bui-space-4); } - .cu-sm-gap-xl { - gap: var(--canon-space-10); + .bui-sm-gap-xl { + gap: var(--bui-space-10); } - .cu-sm-gap-xs { - gap: var(--canon-space-3); + .bui-sm-gap-xs { + gap: var(--bui-space-3); } - .cu-sm-hidden { + .bui-sm-hidden { display: none; } - .cu-sm-inline { + .bui-sm-inline { display: inline; } - .cu-sm-inline-block { + .bui-sm-inline-block { display: inline-block; } - .cu-sm-items-center { + .bui-sm-items-center { align-items: center; } - .cu-sm-items-end { + .bui-sm-items-end { align-items: flex-end; } - .cu-sm-items-start { + .bui-sm-items-start { align-items: flex-start; } - .cu-sm-items-stretch { + .bui-sm-items-stretch { align-items: stretch; } - .cu-sm-justify-around { + .bui-sm-justify-around { justify-content: space-around; } - .cu-sm-justify-between { + .bui-sm-justify-between { justify-content: space-between; } - .cu-sm-justify-center { + .bui-sm-justify-center { justify-content: center; } - .cu-sm-justify-end { + .bui-sm-justify-end { justify-content: flex-end; } - .cu-sm-justify-start { + .bui-sm-justify-start { justify-content: flex-start; } - .cu-sm-justify-stretch { + .bui-sm-justify-stretch { justify-content: stretch; } - .cu-sm-rounded-2xl { - border-radius: var(--canon-radius-full); + .bui-sm-rounded-2xl { + border-radius: var(--bui-radius-full); } - .cu-sm-rounded-2xs { - border-radius: var(--canon-radius-1); + .bui-sm-rounded-2xs { + border-radius: var(--bui-radius-1); } - .cu-sm-rounded-lg { - border-radius: var(--canon-radius-5); + .bui-sm-rounded-lg { + border-radius: var(--bui-radius-5); } - .cu-sm-rounded-md { - border-radius: var(--canon-radius-4); + .bui-sm-rounded-md { + border-radius: var(--bui-radius-4); } - .cu-sm-rounded-none { + .bui-sm-rounded-none { border-radius: 0; } - .cu-sm-rounded-sm { - border-radius: var(--canon-radius-3); + .bui-sm-rounded-sm { + border-radius: var(--bui-radius-3); } - .cu-sm-rounded-xl { - border-radius: var(--canon-radius-6); + .bui-sm-rounded-xl { + border-radius: var(--bui-radius-6); } - .cu-sm-rounded-xs { - border-radius: var(--canon-radius-2); + .bui-sm-rounded-xs { + border-radius: var(--bui-radius-2); } - .cu-sm-wrap-reverse { + .bui-sm-wrap-reverse { flex-wrap: wrap-reverse; } } diff --git a/packages/canon/src/css/utilities/width.css b/packages/canon/src/css/utilities/width.css index f4f10c9416..6dad08814a 100644 --- a/packages/canon/src/css/utilities/width.css +++ b/packages/canon/src/css/utilities/width.css @@ -1,86 +1,86 @@ -.cu-w { +.bui-w { width: var(--width); } -.cu-min-w { +.bui-min-w { min-width: var(--min-width); } -.cu-max-w { +.bui-max-w { max-width: var(--max-width); } /* Breakpoint xs */ @media (min-width: 640px) { - .xs\:cu-w { + .xs\:bui-w { width: var(--width); } - .xs\:cu-min-w { + .xs\:bui-min-w { min-width: var(--min-width); } - .xs\:cu-max-w { + .xs\:bui-max-w { max-width: var(--max-width); } } /* Breakpoint sm */ @media (min-width: 768px) { - .sm\:cu-w { + .sm\:bui-w { width: var(--width); } - .sm\:cu-min-w { + .sm\:bui-min-w { min-width: var(--min-width); } - .sm\:cu-max-w { + .sm\:bui-max-w { max-width: var(--max-width); } } /* Breakpoint md */ @media (min-width: 1024px) { - .md\:cu-w { + .md\:bui-w { width: var(--width); } - .md\:cu-min-w { + .md\:bui-min-w { min-width: var(--min-width); } - .md\:cu-max-w { + .md\:bui-max-w { max-width: var(--max-width); } } /* Breakpoint lg */ @media (min-width: 1280px) { - .lg\:cu-w { + .lg\:bui-w { width: var(--width); } - .lg\:cu-min-w { + .lg\:bui-min-w { min-width: var(--min-width); } - .lg\:cu-max-w { + .lg\:bui-max-w { max-width: var(--max-width); } } /* Breakpoint xl */ @media (min-width: 1536px) { - .xl\:cu-w { + .xl\:bui-w { width: var(--width); } - .xl\:cu-min-w { + .xl\:bui-min-w { min-width: var(--min-width); } - .xl\:cu-max-w { + .xl\:bui-max-w { max-width: var(--max-width); } } diff --git a/packages/canon/src/css/utilities/xs.css b/packages/canon/src/css/utilities/xs.css index 5470355fdb..27adb7c7e4 100644 --- a/packages/canon/src/css/utilities/xs.css +++ b/packages/canon/src/css/utilities/xs.css @@ -15,148 +15,148 @@ */ @layer utilities { - .cu-block { + .bui-block { display: block; } - .cu-border-base { - border-color: var(--canon-border); + .bui-border-base { + border-color: var(--bui-border); border-width: 1px; border-style: solid; } - .cu-border-error { - border-color: var(--canon-border-danger); + .bui-border-error { + border-color: var(--bui-border-danger); border-width: 1px; border-style: solid; } - .cu-border-none { + .bui-border-none { border-color: transparent; border-width: 0; } - .cu-border-selected { - border-color: var(--canon-border-pressed); + .bui-border-selected { + border-color: var(--bui-border-pressed); border-width: 1px; border-style: solid; } - .cu-border-warning { - border-color: var(--canon-border-warning); + .bui-border-warning { + border-color: var(--bui-border-warning); border-width: 1px; border-style: solid; } - .cu-flex { + .bui-flex { display: flex; } - .cu-flex-col { + .bui-flex-col { flex-direction: column; } - .cu-flex-nowrap { + .bui-flex-nowrap { flex-wrap: nowrap; } - .cu-flex-row { + .bui-flex-row { flex-direction: row; } - .cu-flex-wrap { + .bui-flex-wrap { flex-wrap: wrap; } - .cu-flex-wrap-reverse { + .bui-flex-wrap-reverse { flex-wrap: wrap-reverse; } - .cu-hidden { + .bui-hidden { display: none; } - .cu-inline { + .bui-inline { display: inline; } - .cu-inline-block { + .bui-inline-block { display: inline-block; } - .cu-items-center { + .bui-items-center { align-items: center; } - .cu-items-end { + .bui-items-end { align-items: flex-end; } - .cu-items-start { + .bui-items-start { align-items: flex-start; } - .cu-items-stretch { + .bui-items-stretch { align-items: stretch; } - .cu-justify-around { + .bui-justify-around { justify-content: space-around; } - .cu-justify-between { + .bui-justify-between { justify-content: space-between; } - .cu-justify-center { + .bui-justify-center { justify-content: center; } - .cu-justify-end { + .bui-justify-end { justify-content: flex-end; } - .cu-justify-start { + .bui-justify-start { justify-content: flex-start; } - .cu-justify-stretch { + .bui-justify-stretch { justify-content: stretch; } - .cu-rounded-2xl { - border-radius: var(--canon-radius-full); + .bui-rounded-2xl { + border-radius: var(--bui-radius-full); } - .cu-rounded-2xs { - border-radius: var(--canon-radius-1); + .bui-rounded-2xs { + border-radius: var(--bui-radius-1); } - .cu-rounded-lg { - border-radius: var(--canon-radius-5); + .bui-rounded-lg { + border-radius: var(--bui-radius-5); } - .cu-rounded-md { - border-radius: var(--canon-radius-4); + .bui-rounded-md { + border-radius: var(--bui-radius-4); } - .cu-rounded-none { + .bui-rounded-none { border-radius: 0; } - .cu-rounded-sm { - border-radius: var(--canon-radius-3); + .bui-rounded-sm { + border-radius: var(--bui-radius-3); } - .cu-rounded-xl { - border-radius: var(--canon-radius-6); + .bui-rounded-xl { + border-radius: var(--bui-radius-6); } - .cu-rounded-xs { - border-radius: var(--canon-radius-2); + .bui-rounded-xs { + border-radius: var(--bui-radius-2); } - .cu-wrap-reverse { + .bui-wrap-reverse { flex-wrap: wrap-reverse; } } diff --git a/packages/canon/src/hooks/useResponsiveValue.ts b/packages/canon/src/hooks/useResponsiveValue.ts deleted file mode 100644 index 206306566f..0000000000 --- a/packages/canon/src/hooks/useResponsiveValue.ts +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 type { Breakpoint } from '../types'; -import { useBreakpoint, breakpoints } from './useBreakpoint'; - -type ResponsiveValue = string | Partial>; - -export const useResponsiveValue = (value: ResponsiveValue) => { - const { breakpoint } = useBreakpoint(); - - if (typeof value === 'object') { - const index = breakpoints.findIndex(b => b.id === breakpoint); - - for (let i = index; i >= 0; i--) { - if (value[breakpoints[i].id]) { - return value[breakpoints[i].id] as string; - } - } - - // If no value is found for the current or smaller breakpoints, check from the smallest - for (let i = 0; i < breakpoints.length; i++) { - if (value[breakpoints[i].id]) { - return value[breakpoints[i].id] as string; - } - } - } - - return value; -}; diff --git a/packages/canon/src/hooks/useStyles.ts b/packages/canon/src/hooks/useStyles.ts new file mode 100644 index 0000000000..19f9823236 --- /dev/null +++ b/packages/canon/src/hooks/useStyles.ts @@ -0,0 +1,92 @@ +/* + * 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 { useBreakpoint, breakpoints } from './useBreakpoint'; +import { componentDefinitions } from '../utils/componentDefinitions'; +import type { ComponentDefinitionName, ComponentClassNames } from '../types'; + +/** + * Resolve a responsive value based on the current breakpoint + * @param value - The responsive value (string or object with breakpoint keys) + * @param breakpoint - The current breakpoint + * @returns The resolved value for the current breakpoint + */ +function resolveResponsiveValue( + value: string | Record, + breakpoint: string, +): string | undefined { + if (typeof value === 'string') { + return value; + } + + if (typeof value === 'object' && value !== null) { + const index = breakpoints.findIndex(b => b.id === breakpoint); + + // Look for value at current breakpoint or smaller + for (let i = index; i >= 0; i--) { + if (value[breakpoints[i].id]) { + return value[breakpoints[i].id]; + } + } + + // If no value found, check from smallest breakpoint up + for (let i = 0; i < breakpoints.length; i++) { + if (value[breakpoints[i].id]) { + return value[breakpoints[i].id]; + } + } + } + + return undefined; +} + +/** + * React hook to get class names and data attributes for a component with responsive support + * @param componentName - The name of the component + * @param props - Object with prop values (can be responsive) + * @returns Object with classNames and dataAttributes + */ +export function useStyles( + componentName: T, + props: Record = {}, +): { + classNames: ComponentClassNames; + dataAttributes: Record; + resolvedProps: Record; +} { + const { breakpoint } = useBreakpoint(); + const classNames = componentDefinitions[componentName] + .classNames as ComponentClassNames; + + // Resolve responsive values and generate data attributes + const dataAttributes: Record = {}; + const resolvedProps: Record = {}; + + for (const [key, value] of Object.entries(props)) { + if (value !== undefined && value !== null) { + const resolvedValue = resolveResponsiveValue(value, breakpoint); + if (resolvedValue !== undefined) { + resolvedProps[key] = resolvedValue; + dataAttributes[`data-${key}`] = resolvedValue; + } + } + } + + return { + classNames, + dataAttributes, + resolvedProps, // Also return resolved props for convenience + }; +} diff --git a/packages/canon/src/index.ts b/packages/canon/src/index.ts index cab4cc4acb..1dc6aa74c0 100644 --- a/packages/canon/src/index.ts +++ b/packages/canon/src/index.ts @@ -34,19 +34,28 @@ export * from './components/Heading'; // UI components export * from './components/Avatar'; export * from './components/Button'; +export * from './components/Card'; export * from './components/Collapsible'; export * from './components/DataTable'; +export * from './components/FieldLabel'; +export * from './components/Header'; +export * from './components/HeaderPage'; export * from './components/Icon'; -export * from './components/IconButton'; +export * from './components/ButtonIcon'; +export * from './components/ButtonLink'; export * from './components/Checkbox'; +export * from './components/RadioGroup'; export * from './components/Table'; export * from './components/Tabs'; export * from './components/TextField'; export * from './components/Tooltip'; export * from './components/Menu'; export * from './components/ScrollArea'; +export * from './components/SearchField'; export * from './components/Link'; export * from './components/Select'; +export * from './components/Skeleton'; +export * from './components/Switch'; // Types export * from './types'; @@ -54,3 +63,6 @@ export * from './props'; // Hooks export { useBreakpoint } from './hooks/useBreakpoint'; + +// Component Definitions +export * from './utils/componentDefinitions'; diff --git a/packages/canon/src/props/display.props.ts b/packages/canon/src/props/display.props.ts index a71557f05b..4a18aea910 100644 --- a/packages/canon/src/props/display.props.ts +++ b/packages/canon/src/props/display.props.ts @@ -22,7 +22,7 @@ const displayValues = ['none', 'inline', 'inline-block', 'block'] as const; const displayPropDefs = { display: { type: 'enum', - className: 'cu-display', + className: 'bui-display', values: displayValues, responsive: true, }, diff --git a/packages/canon/src/props/gap-props.ts b/packages/canon/src/props/gap-props.ts index 68a03a9f51..2701473004 100644 --- a/packages/canon/src/props/gap-props.ts +++ b/packages/canon/src/props/gap-props.ts @@ -21,7 +21,7 @@ import { spacingValues } from './spacing.props'; const gapPropDefs = { gap: { type: 'enum | string', - className: 'cu-gap', + className: 'bui-gap', customProperties: ['--gap'], values: spacingValues, responsive: true, diff --git a/packages/canon/src/props/height.props.ts b/packages/canon/src/props/height.props.ts index b42f965e54..d6195c1fae 100644 --- a/packages/canon/src/props/height.props.ts +++ b/packages/canon/src/props/height.props.ts @@ -19,19 +19,19 @@ import type { PropDef, GetPropDefTypes } from './prop-def'; const heightPropDefs = { height: { type: 'string', - className: 'cu-h', + className: 'bui-h', customProperties: ['--height'], responsive: true, }, minHeight: { type: 'string', - className: 'cu-min-h', + className: 'bui-min-h', customProperties: ['--min-height'], responsive: true, }, maxHeight: { type: 'string', - className: 'cu-max-h', + className: 'bui-max-h', customProperties: ['--max-height'], responsive: true, }, diff --git a/packages/canon/src/props/margin.props.ts b/packages/canon/src/props/margin.props.ts index 12cb0989e2..31ebf54747 100644 --- a/packages/canon/src/props/margin.props.ts +++ b/packages/canon/src/props/margin.props.ts @@ -21,49 +21,49 @@ const marginPropDefs = (spacingValues: string[]) => m: { type: 'enum | string', values: spacingValues, - className: 'cu-m', + className: 'bui-m', customProperties: ['--m'], responsive: true, }, mx: { type: 'enum | string', values: spacingValues, - className: 'cu-mx', + className: 'bui-mx', customProperties: ['--mx'], responsive: true, }, my: { type: 'enum | string', values: spacingValues, - className: 'cu-my', + className: 'bui-my', customProperties: ['--my'], responsive: true, }, mt: { type: 'enum | string', values: spacingValues, - className: 'cu-mt', + className: 'bui-mt', customProperties: ['--mt'], responsive: true, }, mr: { type: 'enum | string', values: spacingValues, - className: 'cu-mr', + className: 'bui-mr', customProperties: ['--mr'], responsive: true, }, mb: { type: 'enum | string', values: spacingValues, - className: 'cu-mb', + className: 'bui-mb', customProperties: ['--mb'], responsive: true, }, ml: { type: 'enum | string', values: spacingValues, - className: 'cu-ml', + className: 'bui-ml', customProperties: ['--ml'], responsive: true, }, diff --git a/packages/canon/src/props/padding.props.ts b/packages/canon/src/props/padding.props.ts index ea42fcd4f1..16d13cf957 100644 --- a/packages/canon/src/props/padding.props.ts +++ b/packages/canon/src/props/padding.props.ts @@ -21,49 +21,49 @@ const paddingPropDefs = (spacingValues: string[]) => ({ p: { type: 'enum | string', - className: 'cu-p', + className: 'bui-p', customProperties: ['--p'], values: spacingValues, responsive: true, }, px: { type: 'enum | string', - className: 'cu-px', + className: 'bui-px', customProperties: ['--px'], values: spacingValues, responsive: true, }, py: { type: 'enum | string', - className: 'cu-py', + className: 'bui-py', customProperties: ['--py'], values: spacingValues, responsive: true, }, pt: { type: 'enum | string', - className: 'cu-pt', + className: 'bui-pt', customProperties: ['--pt'], values: spacingValues, responsive: true, }, pr: { type: 'enum | string', - className: 'cu-pr', + className: 'bui-pr', customProperties: ['--pr'], values: spacingValues, responsive: true, }, pb: { type: 'enum | string', - className: 'cu-pb', + className: 'bui-pb', customProperties: ['--pb'], values: spacingValues, responsive: true, }, pl: { type: 'enum | string', - className: 'cu-pl', + className: 'bui-pl', customProperties: ['--pl'], values: spacingValues, responsive: true, diff --git a/packages/canon/src/props/position.props.ts b/packages/canon/src/props/position.props.ts index 048e3b13ce..027b5e2a63 100644 --- a/packages/canon/src/props/position.props.ts +++ b/packages/canon/src/props/position.props.ts @@ -28,7 +28,7 @@ const positionValues = [ const positionPropDefs = { position: { type: 'enum', - className: 'cu-position', + className: 'bui-position', values: positionValues, responsive: true, }, diff --git a/packages/canon/src/props/width.props.ts b/packages/canon/src/props/width.props.ts index fdd8f3fdd4..019ec978ba 100644 --- a/packages/canon/src/props/width.props.ts +++ b/packages/canon/src/props/width.props.ts @@ -19,19 +19,19 @@ import type { GetPropDefTypes, PropDef } from './prop-def'; const widthPropDefs = { width: { type: 'string', - className: 'cu-w', + className: 'bui-w', customProperties: ['--width'], responsive: true, }, minWidth: { type: 'string', - className: 'cu-min-w', + className: 'bui-min-w', customProperties: ['--min-width'], responsive: true, }, maxWidth: { type: 'string', - className: 'cu-max-w', + className: 'bui-max-w', customProperties: ['--max-width'], responsive: true, }, diff --git a/packages/canon/src/stories/Form.stories.tsx b/packages/canon/src/stories/Form.stories.tsx deleted file mode 100644 index 3bffca6554..0000000000 --- a/packages/canon/src/stories/Form.stories.tsx +++ /dev/null @@ -1,195 +0,0 @@ -/* - * 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 { useState } from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; -import { useForm, SubmitHandler, Controller } from 'react-hook-form'; -import { TextField } from '../components/TextField'; -import { Button } from '../components/Button'; -import { Select } from '../components/Select'; - -const meta = { - title: 'Examples/Form', -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -type Inputs = { - firstname: string; - lastname: string; - city: string; -}; - -export const Uncontrolled: Story = { - render: () => { - const { - register, - handleSubmit, - control, - formState: { errors }, - } = useForm(); - - const onSubmit: SubmitHandler = data => console.log(data); - - return ( -
- - - { - return ( - - ); - }} - /> - - - ); - }, -}; diff --git a/packages/canon/src/types.ts b/packages/canon/src/types.ts index ee5d954576..073aa0d6cb 100644 --- a/packages/canon/src/types.ts +++ b/packages/canon/src/types.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { componentDefinitions } from './utils/componentDefinitions'; + /** @public */ export type AsProps = | 'div' @@ -131,3 +133,43 @@ export interface UtilityProps extends SpaceProps { justifyContent?: Responsive; rowSpan?: Responsive; } + +/** + * Base type for the component styles structure + * @public + */ +export type ClassNamesMap = Record; + +/** + * Base type for the component styles structure + * @public + */ +export type DataAttributeValues = readonly (string | number | boolean)[]; + +/** + * Base type for the component styles structure + * @public + */ +export type DataAttributesMap = Record; + +/** + * Base type for the component styles structure + * @public + */ +export interface ComponentDefinition { + classNames: ClassNamesMap; + dataAttributes?: DataAttributesMap; +} + +/** + * Type utilities for extracting information from the component styles + * @public + */ +export type ComponentDefinitionName = keyof typeof componentDefinitions; + +/** + * Helper type to extract class names for a component + * @public + */ +export type ComponentClassNames = + (typeof componentDefinitions)[T]['classNames']; diff --git a/packages/canon/src/utils/componentDefinitions.ts b/packages/canon/src/utils/componentDefinitions.ts new file mode 100644 index 0000000000..86aacb7d58 --- /dev/null +++ b/packages/canon/src/utils/componentDefinitions.ts @@ -0,0 +1,291 @@ +/* + * 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 { ComponentDefinition } from '../types'; + +/** + * Component definitions for the Backstage UI library + * @public + */ +export const componentDefinitions = { + Avatar: { + classNames: { + root: 'bui-AvatarRoot', + image: 'bui-AvatarImage', + fallback: 'bui-AvatarFallback', + }, + dataAttributes: { + size: ['small', 'medium', 'large'] as const, + }, + }, + Box: { + classNames: { + root: 'bui-Box', + }, + }, + Button: { + classNames: { + root: 'bui-Button', + }, + dataAttributes: { + size: ['small', 'medium', 'large'] as const, + variant: ['primary', 'secondary', 'ghost'] as const, + }, + }, + ButtonIcon: { + classNames: { + root: 'bui-ButtonIcon', + }, + }, + ButtonLink: { + classNames: { + root: 'bui-ButtonLink', + }, + }, + Card: { + classNames: { + root: 'bui-Card', + header: 'bui-CardHeader', + body: 'bui-CardBody', + footer: 'bui-CardFooter', + }, + }, + Checkbox: { + classNames: { + root: 'bui-CheckboxRoot', + label: 'bui-CheckboxLabel', + indicator: 'bui-CheckboxIndicator', + }, + dataAttributes: { + checked: [true, false] as const, + }, + }, + Collapsible: { + classNames: { + root: 'bui-CollapsibleRoot', + trigger: 'bui-CollapsibleTrigger', + panel: 'bui-CollapsiblePanel', + }, + }, + Container: { + classNames: { + root: 'bui-Container', + }, + }, + FieldLabel: { + classNames: { + root: 'bui-FieldLabelWrapper', + label: 'bui-FieldLabel', + secondaryLabel: 'bui-FieldSecondaryLabel', + description: 'bui-FieldDescription', + }, + }, + Flex: { + classNames: { + root: 'bui-Flex', + }, + }, + Grid: { + classNames: { + root: 'bui-Grid', + item: 'bui-GridItem', + }, + }, + Header: { + classNames: { + toolbar: 'bui-HeaderToolbar', + toolbarWrapper: 'bui-HeaderToolbarWrapper', + toolbarContent: 'bui-HeaderToolbarContent', + toolbarControls: 'bui-HeaderToolbarControls', + toolbarIcon: 'bui-HeaderToolbarIcon', + toolbarName: 'bui-HeaderToolbarName', + breadcrumbs: 'bui-HeaderBreadcrumbs', + breadcrumb: 'bui-HeaderBreadcrumb', + breadcrumbLink: 'bui-HeaderBreadcrumbLink', + breadcrumbSeparator: 'bui-HeaderBreadcrumbSeparator', + tabsWrapper: 'bui-HeaderTabsWrapper', + }, + }, + HeaderPage: { + classNames: { + root: 'bui-HeaderPage', + content: 'bui-HeaderPageContent', + tabsWrapper: 'bui-HeaderPageTabsWrapper', + controls: 'bui-HeaderPageControls', + }, + }, + Heading: { + classNames: { + root: 'bui-Heading', + }, + dataAttributes: { + variant: ['title1', 'title2', 'title3', 'subtitle'] as const, + color: ['primary', 'secondary', 'muted'] as const, + truncate: [true, false] as const, + }, + }, + Icon: { + classNames: { + root: 'bui-Icon', + }, + }, + Link: { + classNames: { + root: 'bui-Link', + }, + dataAttributes: { + variant: ['subtitle', 'body', 'caption', 'label'] as const, + weight: ['regular', 'bold'] as const, + }, + }, + List: { + classNames: { + root: 'bui-List', + row: 'bui-ListRow', + label: 'bui-ListLabel', + }, + }, + Menu: { + classNames: { + trigger: 'bui-MenuTrigger', + backdrop: 'bui-MenuBackdrop', + positioner: 'bui-MenuPositioner', + popup: 'bui-MenuPopup', + arrow: 'bui-MenuArrow', + item: 'bui-MenuItem', + group: 'bui-MenuGroup', + groupLabel: 'bui-MenuGroupLabel', + radioGroup: 'bui-MenuRadioGroup', + radioItem: 'bui-MenuRadioItem', + radioItemIndicator: 'bui-MenuRadioItemIndicator', + checkboxItem: 'bui-MenuCheckboxItem', + checkboxItemIndicator: 'bui-MenuCheckboxItemIndicator', + submenuTrigger: 'bui-MenuSubmenuTrigger', + separator: 'bui-MenuSeparator', + }, + }, + Popover: { + classNames: { + root: 'bui-Popover', + }, + }, + RadioGroup: { + classNames: { + root: 'bui-RadioGroup', + content: 'bui-RadioGroupContent', + radio: 'bui-Radio', + }, + }, + ScrollArea: { + classNames: { + root: 'bui-ScrollAreaRoot', + viewport: 'bui-ScrollAreaViewport', + scrollbar: 'bui-ScrollAreaScrollbar', + thumb: 'bui-ScrollAreaThumb', + }, + }, + SearchField: { + classNames: { + root: 'bui-SearchField', + clear: 'bui-InputClear', + }, + }, + Select: { + classNames: { + root: 'bui-Select', + trigger: 'bui-SelectTrigger', + value: 'bui-SelectValue', + icon: 'bui-SelectIcon', + list: 'bui-SelectList', + item: 'bui-SelectItem', + itemIndicator: 'bui-SelectItemIndicator', + itemLabel: 'bui-SelectItemLabel', + }, + dataAttributes: { + size: ['small', 'medium'] as const, + }, + }, + Skeleton: { + classNames: { + root: 'bui-Skeleton', + }, + }, + Switch: { + classNames: { + root: 'bui-Switch', + indicator: 'bui-SwitchIndicator', + }, + }, + Table: { + classNames: { + root: 'bui-TableRoot', + header: 'bui-TableHeader', + body: 'bui-TableBody', + row: 'bui-TableRow', + head: 'bui-TableHead', + caption: 'bui-TableCaption', + cell: 'bui-TableCell', + cellText: 'bui-TableCellText', + cellLink: 'bui-TableCellLink', + cellProfile: 'bui-TableCellProfile', + cellProfileAvatar: 'bui-TableCellProfileAvatar', + cellProfileAvatarImage: 'bui-TableCellProfileAvatarImage', + cellProfileAvatarFallback: 'bui-TableCellProfileAvatarFallback', + cellProfileName: 'bui-TableCellProfileName', + cellProfileLink: 'bui-TableCellProfileLink', + }, + }, + Tabs: { + classNames: { + tabs: 'bui-Tabs', + tabList: 'bui-TabList', + tabListWrapper: 'bui-TabListWrapper', + tab: 'bui-Tab', + tabActive: 'bui-TabActive', + tabHovered: 'bui-TabHovered', + panel: 'bui-TabPanel', + }, + }, + Text: { + classNames: { + root: 'bui-Text', + }, + dataAttributes: { + variant: ['subtitle', 'body', 'caption', 'label'] as const, + weight: ['regular', 'bold'] as const, + color: ['primary', 'secondary', 'danger', 'warning', 'success'] as const, + truncate: [true, false] as const, + }, + }, + TextField: { + classNames: { + root: 'bui-TextField', + inputWrapper: 'bui-InputWrapper', + input: 'bui-Input', + inputIcon: 'bui-InputIcon', + }, + dataAttributes: { + invalid: [true, false] as const, + disabled: [true, false] as const, + }, + }, + Tooltip: { + classNames: { + tooltip: 'bui-Tooltip', + arrow: 'bui-TooltipArrow', + }, + }, +} as const satisfies Record; diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index d521818703..7897e44582 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/catalog-client +## 1.10.2-next.0 + +### Patch Changes + +- 6fb4143: allow arrays in the InMemoryCatalogClient to filter entities +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + +## 1.10.1 + +### Patch Changes + +- 22fad0d: Fixed `CatalogClient` error responses for `refreshEntity` and `addLocation`. +- Updated dependencies + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + ## 1.10.1-next.0 ### Patch Changes diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index b87a3d4348..93f45f8611 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "1.10.1-next.0", + "version": "1.10.2-next.0", "description": "An isomorphic client for the catalog backend", "backstage": { "role": "common-library" diff --git a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts index b77fcd0676..c5d1f3abc8 100644 --- a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts +++ b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.test.ts @@ -26,12 +26,12 @@ const entity1: Entity = { name: 'e1', uid: 'u1', }, - relations: [{ type: 'relatedTo', targetRef: 'customkind:default/e2' }], + relations: [{ type: 'relatedTo', targetRef: 'secondcustomkind:default/e2' }], }; const entity2: Entity = { apiVersion: 'v1', - kind: 'CustomKind', + kind: 'SecondCustomKind', metadata: { namespace: 'default', name: 'e2', @@ -67,9 +67,21 @@ describe('InMemoryCatalogClient', () => { await expect( client.getEntities({ - filter: { 'relations.relatedTo': 'customkind:default/e2' }, + filter: { 'relations.relatedTo': 'secondcustomkind:default/e2' }, }), ).resolves.toEqual({ items: [entity1] }); + + await expect( + client.getEntities({ + filter: { kind: ['not-existing', 'CustomKind'] }, + }), + ).resolves.toEqual({ items: [entity1] }); + + await expect( + client.getEntities({ + filter: { kind: ['SecondCustomKind', 'also-not-existing'] }, + }), + ).resolves.toEqual({ items: [entity2] }); }); it('getEntitiesByRefs', async () => { @@ -77,7 +89,7 @@ describe('InMemoryCatalogClient', () => { await expect( client.getEntitiesByRefs({ entityRefs: [ - 'customkind:default/e2', + 'secondcustomkind:default/e2', 'customkind:missing/missing', 'customkind:default/e1', ], @@ -86,7 +98,7 @@ describe('InMemoryCatalogClient', () => { await expect( client.getEntitiesByRefs({ entityRefs: [ - 'customkind:default/e2', + 'secondcustomkind:default/e2', 'customkind:missing/missing', 'customkind:default/e1', ], @@ -114,9 +126,9 @@ describe('InMemoryCatalogClient', () => { it('getEntityAncestors', async () => { const client = new InMemoryCatalogClient({ entities }); await expect( - client.getEntityAncestors({ entityRef: 'customkind:default/e2' }), + client.getEntityAncestors({ entityRef: 'secondcustomkind:default/e2' }), ).resolves.toEqual({ - rootEntityRef: 'customkind:default/e2', + rootEntityRef: 'secondcustomkind:default/e2', items: [{ entity: entity2, parentEntityRefs: [] }], }); }); @@ -124,7 +136,7 @@ describe('InMemoryCatalogClient', () => { it('getEntityByRef', async () => { const client = new InMemoryCatalogClient({ entities }); await expect( - client.getEntityByRef('customkind:default/e2'), + client.getEntityByRef('secondcustomkind:default/e2'), ).resolves.toEqual(entity2); await expect( client.getEntityByRef('customkind:missing/missing'), @@ -147,7 +159,7 @@ describe('InMemoryCatalogClient', () => { it('refreshEntity', async () => { const client = new InMemoryCatalogClient({ entities }); await expect( - client.refreshEntity('customkind:default/e2'), + client.refreshEntity('secondcustomkind:default/e2'), ).resolves.toBeUndefined(); }); }); diff --git a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts index a7dd333729..746871a1b3 100644 --- a/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts +++ b/packages/catalog-client/src/testUtils/InMemoryCatalogClient.ts @@ -106,6 +106,11 @@ function createFilter( if (expectedValue === CATALOG_FILTER_EXISTS) { continue; } + if (Array.isArray(expectedValue)) { + return expectedValue.some(value => + searchValues?.includes(String(value).toLocaleLowerCase('en-US')), + ); + } if ( !searchValues?.includes( String(expectedValue).toLocaleLowerCase('en-US'), diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 59df385827..e51ac917bc 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/catalog-model +## 1.7.5-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup + ## 1.7.4 ### Patch Changes diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 6d20f69531..95d612c3bd 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "1.7.4", + "version": "1.7.5-next.0", "description": "Types and validators that help describe the model of a Backstage Catalog", "backstage": { "role": "common-library" diff --git a/packages/catalog-model/src/kinds/types.ts b/packages/catalog-model/src/kinds/types.ts index f4bc151384..f4096b4876 100644 --- a/packages/catalog-model/src/kinds/types.ts +++ b/packages/catalog-model/src/kinds/types.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Entity } from '../entity'; +import { Entity } from '../entity/Entity'; /** * Validates entities of a certain kind. diff --git a/packages/catalog-model/src/location/helpers.ts b/packages/catalog-model/src/location/helpers.ts index a2f4d84f6f..a7eec97196 100644 --- a/packages/catalog-model/src/location/helpers.ts +++ b/packages/catalog-model/src/location/helpers.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { Entity, stringifyEntityRef } from '../entity'; +import { Entity } from '../entity/Entity'; +import { stringifyEntityRef } from '../entity/ref'; import { ANNOTATION_LOCATION, ANNOTATION_SOURCE_LOCATION } from './annotation'; // See https://github.com/facebook/react/blob/f0cf832e1d0c8544c36aa8b310960885a11a847c/packages/react-dom-bindings/src/shared/sanitizeURL.js diff --git a/packages/catalog-model/src/validation/entityKindSchemaValidator.ts b/packages/catalog-model/src/validation/entityKindSchemaValidator.ts index dc5aa586aa..bfeb6f0d0c 100644 --- a/packages/catalog-model/src/validation/entityKindSchemaValidator.ts +++ b/packages/catalog-model/src/validation/entityKindSchemaValidator.ts @@ -15,7 +15,7 @@ */ import { Schema } from 'ajv'; -import { Entity } from '../entity'; +import { Entity } from '../entity/Entity'; import { compileAjvSchema, throwAjvError } from './ajv'; /** diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 7e03186e23..736aa7282f 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,107 @@ # @backstage/cli +## 0.33.1-next.2 + +### Patch Changes + +- 50f0ce6: Fixes a module not found error when running `backstage-cli info`. + +## 0.33.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.2-next.0 + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/integration@1.17.1-next.1 + +## 0.33.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/cli-node@0.2.13 + - @backstage/config-loader@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.11 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.1 + +## 0.33.0 + +### Minor Changes + +- eef0e83: Internal update to promote the modular CLI entrypoint to stable. + +### Patch Changes + +- d07fe35: Added user feedback when opening config docs in browser. The command now clearly indicates what it's doing and provides fallback instructions if the browser fails to open. +- ce70439: The `BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE` flag has been removed. Existing users are encouraged to switch to `EXPERIMENTAL_RSPACK` instead. +- 1d8f00b: Switched to using the `ModuleFederationPlugin` from `@module-federation/enhanced/rspack` for Rspack, rather than the built-in one. +- 2b9633f: The experimental `FORCE_REACT_DEVELOPMENT` flag has been removed. +- d8c4a54: Only use the caching Jest module loader for frontend packages in order to avoid breaking real ESM module imports. +- d6d63c7: Updating the scaffolder action boilerplate to use new `zod` schema +- e36e855: Added `backstage.pluginId` field in `package.json` to all default plugin package templates for the `new` command. +- 1bab255: Internal refactor to combine alpha `build` and `start` modules. +- 713e957: fix: merge eslint reports when using json format +- 8a0164c: Fix an issue where some commands were not usable because of missing dist files +- Updated dependencies + - @backstage/eslint-plugin@0.1.11 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.1 + +## 0.33.0-next.2 + +### Patch Changes + +- 8a0164c: Fix an issue where some commands were not usable because of missing dist files +- Updated dependencies + - @backstage/eslint-plugin@0.1.11-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.1 + +## 0.33.0-next.1 + +### Minor Changes + +- eef0e83: Internal update to promote the modular CLI entrypoint to stable. + +### Patch Changes + +- d07fe35: Added user feedback when opening config docs in browser. The command now clearly indicates what it's doing and provides fallback instructions if the browser fails to open. +- d6d63c7: Updating the scaffolder action boilerplate to use new `zod` schema +- e36e855: Added `backstage.pluginId` field in `package.json` to all default plugin package templates for the `new` command. +- Updated dependencies + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/eslint-plugin@0.1.10 + - @backstage/integration@1.17.0 + - @backstage/release-manifests@0.0.13 + - @backstage/types@1.2.1 + ## 0.32.2-next.0 ### Patch Changes diff --git a/packages/cli/cli-report.backstage-cli.md b/packages/cli/cli-report.backstage-cli.md deleted file mode 100644 index ed6ae9049e..0000000000 --- a/packages/cli/cli-report.backstage-cli.md +++ /dev/null @@ -1,521 +0,0 @@ -## CLI Report file for "@backstage/cli" - -> Do not edit this file. It is a report generated by `yarn build:api-reports` - -### `backstage-cli` - -``` -Usage: backstage-cli [options] [command] - -Options: - -V, --version - -h, --help - -Commands: - build-workspace [options] [packages...] - config:check [options] - config:docs [options] - config:print [options] - config:schema [options] - create-github-app - help [command] - info - migrate [command] - new [options] - package [command] - repo [command] - versions:bump [options] - versions:migrate [options] -``` - -### `backstage-cli build-workspace` - -``` -Usage: backstage-cli build-workspace [options] [packages...] - -Options: - --alwaysPack - -h, --help -``` - -### `backstage-cli config:check` - -``` -Usage: backstage-cli config:check [options] - -Options: - --config - --deprecated - --frontend - --lax - --package - --strict - -h, --help -``` - -### `backstage-cli config:docs` - -``` -Usage: backstage-cli config:docs [options] - -Options: - --package - -h, --help -``` - -### `backstage-cli config:print` - -``` -Usage: backstage-cli config:print [options] - -Options: - --config - --format - --frontend - --lax - --package - --with-secrets - -h, --help -``` - -### `backstage-cli config:schema` - -``` -Usage: backstage-cli config:schema [options] - -Options: - --format - --merge - --no-merge - --package - -h, --help -``` - -### `backstage-cli create-github-app` - -``` -Usage: backstage-cli create-github-app [options] - -Options: - -h, --help -``` - -### `backstage-cli info` - -``` -Usage: backstage-cli info [options] - -Options: - -h, --help -``` - -### `backstage-cli migrate` - -``` -Usage: backstage-cli migrate [options] [command] [command] - -Options: - -h, --help - -Commands: - help [command] - package-exports - package-lint-configs - package-roles - package-scripts - react-router-deps -``` - -### `backstage-cli migrate package-exports` - -``` -Usage: backstage-cli migrate package-exports [options] - -Options: - -h, --help -``` - -### `backstage-cli migrate package-lint-configs` - -``` -Usage: backstage-cli migrate package-lint-configs [options] - -Options: - -h, --help -``` - -### `backstage-cli migrate package-roles` - -``` -Usage: backstage-cli migrate package-roles [options] - -Options: - -h, --help -``` - -### `backstage-cli migrate package-scripts` - -``` -Usage: backstage-cli migrate package-scripts [options] - -Options: - -h, --help -``` - -### `backstage-cli migrate react-router-deps` - -``` -Usage: backstage-cli migrate react-router-deps [options] - -Options: - -h, --help -``` - -### `backstage-cli new` - -``` -Usage: backstage-cli new [options] - -Options: - --baseVersion - --license - --no-private - --npm-registry - --option = - --scope - --select - --skip-install - -h, --help -``` - -### `backstage-cli package` - -``` -Usage: backstage-cli package [options] [command] [command] - -Options: - -h, --help - -Commands: - build [options] - clean - help [command] - lint [options] [directories...] - postpack - prepack - start [options] - test -``` - -### `backstage-cli package build` - -``` -Usage: backstage-cli package build [options] - -Options: - --config - --minify - --role - --skip-build-dependencies - --stats - -h, --help -``` - -### `backstage-cli package clean` - -``` -Usage: backstage-cli package clean [options] - -Options: - -h, --help -``` - -### `backstage-cli package lint` - -``` -Usage: backstage-cli package lint [options] [directories...] - -Options: - --fix - --format - --max-warnings - --output-file - -h, --help -``` - -### `backstage-cli package postpack` - -``` -Usage: backstage-cli package postpack [options] - -Options: - -h, --help -``` - -### `backstage-cli package prepack` - -``` -Usage: backstage-cli package prepack [options] - -Options: - -h, --help -``` - -### `backstage-cli package start` - -``` -Usage: backstage-cli package start [options] - -Options: - --check - --config - --inspect [host] - --inspect-brk [host] - --link - --require - --role - -h, --help -``` - -### `backstage-cli package test` - -``` -Usage: backstage-cli [--config=] [TestPathPattern] - -Options: - --all - --automock - --cache - --cacheDirectory - --changedFilesWithAncestor - --changedSince - --ci - --clearCache - --clearMocks - --collectCoverage - --collectCoverageFrom - --color - --colors - --coverage - --coverageDirectory - --coveragePathIgnorePatterns - --coverageProvider - --coverageReporters - --coverageThreshold - --debug - --detectLeaks - --detectOpenHandles - --env - --errorOnDeprecated - --filter - --findRelatedTests - --forceExit - --globalSetup - --globalTeardown - --globals - --haste - --ignoreProjects - --init - --injectGlobals - --json - --lastCommit - --listTests - --logHeapUsage - --maxConcurrency - --moduleDirectories - --moduleFileExtensions - --moduleNameMapper - --modulePathIgnorePatterns - --modulePaths - --noStackTrace - --notify - --notifyMode - --openHandlesTimeout - --outputFile - --passWithNoTests - --preset - --prettierPath - --projects - --randomize - --reporters - --resetMocks - --resetModules - --resolver - --restoreMocks - --rootDir - --roots - --runTestsByPath - --runner - --seed - --selectProjects - --setupFiles - --setupFilesAfterEnv - --shard - --showConfig - --showSeed - --silent - --skipFilter - --snapshotSerializers - --testEnvironment - --testEnvironmentOptions - --testFailureExitCode - --testLocationInResults - --testMatch - --testPathIgnorePatterns - --testPathPattern - --testRegex - --testResultsProcessor - --testRunner - --testSequencer - --testTimeout - --transform - --transformIgnorePatterns - --unmockedModulePathPatterns - --useStderr - --verbose - --version - --watch - --watchAll - --watchPathIgnorePatterns - --watchman - --workerThreads - -b, --bail - -c, --config - -e, --expand - -f, --onlyFailures - -h, --help - -i, --runInBand - -o, --onlyChanged - -t, --testNamePattern - -u, --updateSnapshot - -w, --maxWorkers -``` - -### `backstage-cli repo` - -``` -Usage: backstage-cli repo [options] [command] [command] - -Options: - -h, --help - -Commands: - build [options] - clean - fix [options] - help [command] - lint [options] - list-deprecations [options] - start [options] [packageNameOrPath...] - test [options] -``` - -### `backstage-cli repo build` - -``` -Usage: backstage-cli repo build [options] - -Options: - --all - --minify - --since - -h, --help -``` - -### `backstage-cli repo clean` - -``` -Usage: backstage-cli repo clean [options] - -Options: - -h, --help -``` - -### `backstage-cli repo fix` - -``` -Usage: backstage-cli repo fix [options] - -Options: - --check - --publish - -h, --help -``` - -### `backstage-cli repo lint` - -``` -Usage: backstage-cli repo lint [options] - -Options: - --fix - --format - --max-warnings - --output-file - --since - --successCache - --successCacheDir - -h, --help -``` - -### `backstage-cli repo list-deprecations` - -``` -Usage: backstage-cli repo list-deprecations [options] - -Options: - --json - -h, --help -``` - -### `backstage-cli repo start` - -``` -Usage: backstage-cli repo start [options] [packageNameOrPath...] - -Options: - --config - --inspect [host] - --inspect-brk [host] - --link - --plugin - --require - -h, --help -``` - -### `backstage-cli repo test` - -``` -Usage: backstage-cli repo test [options] - -Options: - --jest-help - --since - --successCache - --successCacheDir - -h, --help -``` - -### `backstage-cli versions:bump` - -``` -Usage: backstage-cli versions:bump [options] - -Options: - --pattern - --release - --skip-install - --skip-migrate - -h, --help -``` - -### `backstage-cli versions:migrate` - -``` -Usage: backstage-cli versions:migrate [options] - -Options: - --pattern - --skip-code-changes - -h, --help -``` diff --git a/packages/cli/cli-report.backstage-cli-alpha.md b/packages/cli/cli-report.md similarity index 79% rename from packages/cli/cli-report.backstage-cli-alpha.md rename to packages/cli/cli-report.md index 331385f6f6..e2f5bb9cc6 100644 --- a/packages/cli/cli-report.backstage-cli-alpha.md +++ b/packages/cli/cli-report.md @@ -2,7 +2,7 @@ > Do not edit this file. It is a report generated by `yarn build:api-reports` -### `backstage-cli-alpha` +### `backstage-cli` ``` Usage: backstage-cli [options] [command] @@ -29,7 +29,7 @@ Commands: versions:migrate ``` -### `backstage-cli-alpha build-workspace` +### `backstage-cli build-workspace` ``` Usage: program [options] [packages...] @@ -39,7 +39,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha config` +### `backstage-cli config` ``` Usage: backstage-cli config [options] [command] [command] @@ -53,7 +53,7 @@ Commands: schema ``` -### `backstage-cli-alpha config docs` +### `backstage-cli config docs` ``` Usage: backstage-cli config docs [options] @@ -63,7 +63,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha config schema` +### `backstage-cli config schema` ``` Usage: @@ -77,7 +77,7 @@ Options: --version ``` -### `backstage-cli-alpha config:check` +### `backstage-cli config:check` ``` Usage: @@ -93,7 +93,7 @@ Options: --version ``` -### `backstage-cli-alpha config:docs` +### `backstage-cli config:docs` ``` Usage: program [options] @@ -103,7 +103,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha config:print` +### `backstage-cli config:print` ``` Usage: @@ -119,7 +119,7 @@ Options: --with-secrets ``` -### `backstage-cli-alpha config:schema` +### `backstage-cli config:schema` ``` Usage: @@ -133,7 +133,7 @@ Options: --version ``` -### `backstage-cli-alpha create-github-app` +### `backstage-cli create-github-app` ``` Usage: program [options] @@ -142,7 +142,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha info` +### `backstage-cli info` ``` Usage: @@ -152,7 +152,7 @@ Options: --version ``` -### `backstage-cli-alpha migrate` +### `backstage-cli migrate` ``` Usage: backstage-cli migrate [options] [command] [command] @@ -169,7 +169,7 @@ Commands: react-router-deps ``` -### `backstage-cli-alpha migrate package-exports` +### `backstage-cli migrate package-exports` ``` Usage: program [options] @@ -178,7 +178,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha migrate package-lint-configs` +### `backstage-cli migrate package-lint-configs` ``` Usage: program [options] @@ -187,7 +187,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha migrate package-roles` +### `backstage-cli migrate package-roles` ``` Usage: program [options] @@ -196,7 +196,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha migrate package-scripts` +### `backstage-cli migrate package-scripts` ``` Usage: program [options] @@ -205,7 +205,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha migrate react-router-deps` +### `backstage-cli migrate react-router-deps` ``` Usage: program [options] @@ -214,7 +214,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha new` +### `backstage-cli new` ``` Usage: program [options] @@ -231,7 +231,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package` +### `backstage-cli package` ``` Usage: backstage-cli package [options] [command] [command] @@ -250,7 +250,7 @@ Commands: test ``` -### `backstage-cli-alpha package build` +### `backstage-cli package build` ``` Usage: program [options] @@ -264,7 +264,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package clean` +### `backstage-cli package clean` ``` Usage: program [options] @@ -273,7 +273,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package lint` +### `backstage-cli package lint` ``` Usage: program [options] [directories...] @@ -286,7 +286,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package postpack` +### `backstage-cli package postpack` ``` Usage: program [options] @@ -295,7 +295,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package prepack` +### `backstage-cli package prepack` ``` Usage: program [options] @@ -304,7 +304,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha package start` +### `backstage-cli package start` ``` Usage: program [options] @@ -320,10 +320,10 @@ Options: -h, --help ``` -### `backstage-cli-alpha package test` +### `backstage-cli package test` ``` -Usage: backstage-cli-alpha [--config=] [TestPathPattern] +Usage: backstage-cli [--config=] [TestPathPattern] Options: --all @@ -434,7 +434,7 @@ Options: -w, --maxWorkers ``` -### `backstage-cli-alpha repo` +### `backstage-cli repo` ``` Usage: backstage-cli repo [options] [command] [command] @@ -453,7 +453,7 @@ Commands: test ``` -### `backstage-cli-alpha repo build` +### `backstage-cli repo build` ``` Usage: program [options] [command] @@ -465,7 +465,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo clean` +### `backstage-cli repo clean` ``` Usage: program [options] @@ -474,7 +474,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo fix` +### `backstage-cli repo fix` ``` Usage: program [options] @@ -485,10 +485,10 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo lint` +### `backstage-cli repo lint` ``` -Usage: program [options] +Usage: program [options] [command] Options: --fix @@ -501,7 +501,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo list-deprecations` +### `backstage-cli repo list-deprecations` ``` Usage: program [options] @@ -511,10 +511,10 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo start` +### `backstage-cli repo start` ``` -Usage: program [options] [...packageNameOrPath] +Usage: program [options] [packageNameOrPath...] Options: --config @@ -526,7 +526,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha repo test` +### `backstage-cli repo test` ``` Usage: program [options] @@ -539,7 +539,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha versions:bump` +### `backstage-cli versions:bump` ``` Usage: program [options] @@ -552,7 +552,7 @@ Options: -h, --help ``` -### `backstage-cli-alpha versions:migrate` +### `backstage-cli versions:migrate` ``` Usage: program [options] diff --git a/packages/cli/package.json b/packages/cli/package.json index 603d1a4e1a..200558e2e3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/cli", - "version": "0.32.2-next.0", + "version": "0.33.1-next.2", "description": "CLI for developing Backstage plugins and apps", "backstage": { "role": "cli" @@ -20,8 +20,7 @@ "license": "Apache-2.0", "main": "dist/index.cjs.js", "bin": { - "backstage-cli": "bin/backstage-cli", - "backstage-cli-alpha": "bin/backstage-cli-alpha" + "backstage-cli": "bin/backstage-cli" }, "files": [ "asset-types", diff --git a/packages/cli/src/alpha.ts b/packages/cli/src/alpha.ts deleted file mode 100644 index f7333073d8..0000000000 --- a/packages/cli/src/alpha.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 { CliInitializer } from './wiring/CliInitializer'; -import chalk from 'chalk'; - -(async () => { - // Logging as stdout to prevent failures from stderr during api-report generation. - console.log( - chalk.yellow( - 'THIS ENTRYPOINT IS IN ALPHA AND MAY CHANGE IN THE FUTURE - DO NOT USE THIS FOR NORMAL DEVELOPMENT', - ), - ); - const initializer = new CliInitializer(); - 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/lint/alpha')); - initializer.add(import('./modules/maintenance/alpha')); - initializer.add(import('./modules/migrate/alpha')); - initializer.add(import('./modules/new/alpha')); - initializer.add(import('./modules/test/alpha')); - await initializer.run(); -})(); diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts deleted file mode 100644 index 8cfad48b14..0000000000 --- a/packages/cli/src/commands/index.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2020 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 { Command } from 'commander'; -import { registerCommands as registerConfigCommands } from '../modules/config'; -import { - registerPackageCommands as registerPackageBuildCommands, - registerRepoCommands as registerRepoBuildCommands, - registerCommands as registerBuildCommands, -} from '../modules/build'; -import { registerCommands as registerInfoCommands } from '../modules/info'; -import { registerCommands as registerMigrateCommand } from '../modules/migrate'; -import { - registerRepoCommands as registerRepoTestCommands, - registerPackageCommands as registerPackageTestCommands, -} from '../modules/test'; -import { - registerPackageCommands as registerPackageLintCommands, - registerRepoCommands as registerRepoLintCommands, -} from '../modules/lint'; -import { - registerPackageCommands as registerMaintenancePackageCommands, - registerRepoCommands as registerMaintenanceRepoCommands, -} from '../modules/maintenance'; -import { removed } from '../lib/removed'; -import { registerCommands as registerNewCommands } from '../modules/new'; -import { registerCommands as registerCreateGithubAppCommands } from '../modules/create-github-app'; - -export function registerRepoCommand(program: Command) { - const command = program - .command('repo [command]') - .description('Command that run across an entire Backstage project'); - - registerRepoBuildCommands(command); - registerRepoTestCommands(command); - registerRepoLintCommands(command); - registerMaintenanceRepoCommands(command); -} - -export function registerScriptCommand(program: Command) { - const command = program - .command('package [command]') - .description('Lifecycle scripts for individual packages'); - - registerPackageBuildCommands(command); - registerPackageTestCommands(command); - registerMaintenancePackageCommands(command); - registerPackageLintCommands(command); -} - -export function registerCommands(program: Command) { - registerConfigCommands(program); - registerRepoCommand(program); - registerScriptCommand(program); - registerMigrateCommand(program); - registerBuildCommands(program); - registerInfoCommands(program); - registerNewCommands(program); - registerCreateGithubAppCommands(program); - // Notifications for removed commands - - program - .command('plugin:diff') - .allowUnknownOption(true) - .action(removed("use 'backstage-cli fix' instead")); - program - .command('test') - .allowUnknownOption(true) - .action( - removed( - "use 'backstage-cli repo test' or 'backstage-cli package test' instead", - ), - ); - program - .command('clean') - .allowUnknownOption(true) - .action(removed("use 'backstage-cli package clean' instead")); - program - .command('versions:check') - .allowUnknownOption(true) - .action(removed("use 'yarn dedupe' or 'yarn-deduplicate' instead")); - program.command('install').allowUnknownOption(true).action(removed()); - program.command('onboard').allowUnknownOption(true).action(removed()); -} diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index d1ca1bcd04..ece1e413a0 100644 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -14,40 +14,18 @@ * limitations under the License. */ -/** - * CLI for developing Backstage plugins and apps - * - * @packageDocumentation - */ +import { CliInitializer } from './wiring/CliInitializer'; -import { program } from 'commander'; -import chalk from 'chalk'; -import { exitWithError } from './lib/errors'; -import { version } from './lib/version'; -import { registerCommands } from './commands'; - -const main = (argv: string[]) => { - program.name('backstage-cli').version(version); - - registerCommands(program); - - program.on('command:*', () => { - console.log(); - console.log(chalk.red(`Invalid command: ${program.args.join(' ')}`)); - console.log(); - program.outputHelp(); - process.exit(1); - }); - - program.parse(argv); -}; - -process.on('unhandledRejection', rejection => { - if (rejection instanceof Error) { - exitWithError(rejection); - } else { - exitWithError(new Error(`Unknown rejection: '${rejection}'`)); - } -}); - -main(process.argv); +(async () => { + const initializer = new CliInitializer(); + initializer.add(import('./modules/build')); + initializer.add(import('./modules/config')); + initializer.add(import('./modules/create-github-app')); + initializer.add(import('./modules/info')); + initializer.add(import('./modules/lint')); + initializer.add(import('./modules/maintenance')); + initializer.add(import('./modules/migrate')); + initializer.add(import('./modules/new')); + initializer.add(import('./modules/test')); + await initializer.run(); +})(); diff --git a/packages/cli/src/modules/build/alpha.ts b/packages/cli/src/modules/build/alpha.ts deleted file mode 100644 index fb9365d7c5..0000000000 --- a/packages/cli/src/modules/build/alpha.ts +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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 { Command, Option } from 'commander'; -import { createCliPlugin } from '../../wiring/factory'; -import { lazy } from '../../lib/lazy'; -import { registerPackageCommands } from '.'; -import { configOption } from '../config'; - -export const buildPlugin = createCliPlugin({ - pluginId: 'build', - init: async reg => { - reg.addCommand({ - path: ['package', 'build'], - description: 'Build a package for production deployment or publishing', - execute: async ({ args }) => { - const command = new Command(); - - const defaultCommand = command - .option( - '--role ', - 'Run the command with an explicit package role', - ) - .option( - '--minify', - 'Minify the generated code. Does not apply to app package (app is minified by default).', - ) - .option( - '--skip-build-dependencies', - 'Skip the automatic building of local dependencies. Applies to backend packages only.', - ) - .option( - '--stats', - 'If bundle stats are available, write them to the output directory. Applies to app packages only.', - ) - .option( - '--config ', - 'Config files to load instead of app-config.yaml. Applies to app packages only.', - (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), - Array(), - ) - .action(lazy(() => import('./commands/package/build'), 'command')); - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'build'], - description: - 'Build packages in the project, excluding bundled app and backend packages.', - execute: async ({ args }) => { - const command = new Command(); - - // This command expect `package build` to be registered, as its used to parse - // individual plugins' package build scripts. - registerPackageCommands(command.command('package')); - - const defaultCommand = command - .option( - '--all', - 'Build all packages, including bundled app and backend packages.', - ) - .option( - '--since ', - 'Only build packages and their dev dependents that changed since the specified ref', - ) - .option( - '--minify', - 'Minify the generated code. Does not apply to app package (app is minified by default).', - ) - .action(lazy(() => import('./commands/repo/build'), 'command')); - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['package', 'start'], - description: 'Start a package for local development', - execute: async ({ args }) => { - const command = new Command(); - - const defaultCommand = command - .option(...configOption) - .option( - '--role ', - 'Run the command with an explicit package role', - ) - .option('--check', 'Enable type checking and linting if available') - .option('--inspect [host]', 'Enable debugger in Node.js environments') - .option( - '--inspect-brk [host]', - 'Enable debugger in Node.js environments, breaking before code starts', - ) - .option( - '--require ', - 'Add a --require argument to the node process', - ) - .option( - '--link ', - 'Link an external workspace for module resolution', - ) - .action(lazy(() => import('./commands/package/start'), 'command')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'start'], - description: 'Starts packages in the repo for local development', - execute: async ({ args }) => { - const command = new Command(); - - const defaultCommand = command - .argument( - '[...packageNameOrPath]', - 'Run the specified package instead of the defaults.', - ) - .option( - '--plugin ', - 'Start the dev entry-point for any matching plugin package in the repo', - (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), - Array(), - ) - .option(...configOption) - .option( - '--inspect [host]', - 'Enable debugger in Node.js environments. Applies to backend package only', - ) - .option( - '--inspect-brk [host]', - 'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only', - ) - .option( - '--require ', - 'Add a --require argument to the node process. Applies to backend package only', - ) - .option( - '--link ', - 'Link an external workspace for module resolution', - ) - .action( - lazy(() => import('../build/commands/repo/start'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['build-workspace'], - description: - 'Builds a temporary dist workspace from the provided packages', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .arguments(' [packages...]') - .addOption( - new Option( - '--alwaysYarnPack', - 'Alias for --alwaysPack for backwards compatibility.', - ) - .implies({ alwaysPack: true }) - .hideHelp(true), - ) - .option( - '--alwaysPack', - 'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)', - ) - .action(lazy(() => import('./commands/buildWorkspace'), 'default')); - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); - -export default buildPlugin; diff --git a/packages/cli/src/modules/build/index.ts b/packages/cli/src/modules/build/index.ts index c859985975..fd31a6c782 100644 --- a/packages/cli/src/modules/build/index.ts +++ b/packages/cli/src/modules/build/index.ts @@ -13,60 +13,12 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Command, Option } from 'commander'; +import { createCliPlugin } from '../../wiring/factory'; import { lazy } from '../../lib/lazy'; import { configOption } from '../config'; -export function registerRepoCommands(command: Command) { - command - .command('build') - .description( - 'Build packages in the project, excluding bundled app and backend packages.', - ) - .option( - '--all', - 'Build all packages, including bundled app and backend packages.', - ) - .option( - '--since ', - 'Only build packages and their dev dependents that changed since the specified ref', - ) - .option( - '--minify', - 'Minify the generated code. Does not apply to app package (app is minified by default).', - ) - .action(lazy(() => import('./commands/repo/build'), 'command')); - - command - .command('start') - .description('Starts packages in the repo for local development') - .argument( - '[packageNameOrPath...]', - 'Run the specified package instead of the defaults.', - ) - .option( - '--plugin ', - 'Start the dev entry-point for any matching plugin package in the repo', - (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), - Array(), - ) - .option(...configOption) - .option( - '--inspect [host]', - 'Enable debugger in Node.js environments. Applies to backend package only', - ) - .option( - '--inspect-brk [host]', - 'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only', - ) - .option( - '--require ', - 'Add a --require argument to the node process. Applies to backend package only', - ) - .option('--link ', 'Link an external workspace for module resolution') - .action(lazy(() => import('../build/commands/repo/start'), 'command')); -} - export function registerPackageCommands(command: Command) { command .command('build') @@ -91,41 +43,173 @@ export function registerPackageCommands(command: Command) { Array(), ) .action(lazy(() => import('./commands/package/build'), 'command')); - - command - .command('start') - .description('Start a package for local development') - .option(...configOption) - .option('--role ', 'Run the command with an explicit package role') - .option('--check', 'Enable type checking and linting if available') - .option('--inspect [host]', 'Enable debugger in Node.js environments') - .option( - '--inspect-brk [host]', - 'Enable debugger in Node.js environments, breaking before code starts', - ) - .option( - '--require ', - 'Add a --require argument to the node process', - ) - .option('--link ', 'Link an external workspace for module resolution') - .action(lazy(() => import('./commands/package/start'), 'command')); } -export function registerCommands(program: Command) { - program - .command('build-workspace [packages...]') - .addOption( - new Option( - '--alwaysYarnPack', - 'Alias for --alwaysPack for backwards compatibility.', - ) - .implies({ alwaysPack: true }) - .hideHelp(true), - ) - .option( - '--alwaysPack', - 'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)', - ) - .description('Builds a temporary dist workspace from the provided packages') - .action(lazy(() => import('./commands/buildWorkspace'), 'default')); -} +export const buildPlugin = createCliPlugin({ + pluginId: 'build', + init: async reg => { + reg.addCommand({ + path: ['package', 'build'], + description: 'Build a package for production deployment or publishing', + execute: async ({ args }) => { + const command = new Command(); + + const defaultCommand = command + .option( + '--role ', + 'Run the command with an explicit package role', + ) + .option( + '--minify', + 'Minify the generated code. Does not apply to app package (app is minified by default).', + ) + .option( + '--skip-build-dependencies', + 'Skip the automatic building of local dependencies. Applies to backend packages only.', + ) + .option( + '--stats', + 'If bundle stats are available, write them to the output directory. Applies to app packages only.', + ) + .option( + '--config ', + 'Config files to load instead of app-config.yaml. Applies to app packages only.', + (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), + Array(), + ) + .action(lazy(() => import('./commands/package/build'), 'command')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'build'], + description: + 'Build packages in the project, excluding bundled app and backend packages.', + execute: async ({ args }) => { + const command = new Command(); + + // This command expect `package build` to be registered, as its used to parse + // individual plugins' package build scripts. + registerPackageCommands(command.command('package')); + + const defaultCommand = command + .option( + '--all', + 'Build all packages, including bundled app and backend packages.', + ) + .option( + '--since ', + 'Only build packages and their dev dependents that changed since the specified ref', + ) + .option( + '--minify', + 'Minify the generated code. Does not apply to app package (app is minified by default).', + ) + .action(lazy(() => import('./commands/repo/build'), 'command')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['package', 'start'], + description: 'Start a package for local development', + execute: async ({ args }) => { + const command = new Command(); + + const defaultCommand = command + .option(...configOption) + .option( + '--role ', + 'Run the command with an explicit package role', + ) + .option('--check', 'Enable type checking and linting if available') + .option('--inspect [host]', 'Enable debugger in Node.js environments') + .option( + '--inspect-brk [host]', + 'Enable debugger in Node.js environments, breaking before code starts', + ) + .option( + '--require ', + 'Add a --require argument to the node process', + ) + .option( + '--link ', + 'Link an external workspace for module resolution', + ) + .action(lazy(() => import('./commands/package/start'), 'command')); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'start'], + description: 'Starts packages in the repo for local development', + execute: async ({ args }) => { + const command = new Command(); + + const defaultCommand = command + .argument( + '[packageNameOrPath...]', + 'Run the specified package instead of the defaults.', + ) + .option( + '--plugin ', + 'Start the dev entry-point for any matching plugin package in the repo', + (opt: string, opts: string[]) => (opts ? [...opts, opt] : [opt]), + Array(), + ) + .option(...configOption) + .option( + '--inspect [host]', + 'Enable debugger in Node.js environments. Applies to backend package only', + ) + .option( + '--inspect-brk [host]', + 'Enable debugger in Node.js environments, breaking before code starts. Applies to backend package only', + ) + .option( + '--require ', + 'Add a --require argument to the node process. Applies to backend package only', + ) + .option( + '--link ', + 'Link an external workspace for module resolution', + ) + .action( + lazy(() => import('../build/commands/repo/start'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['build-workspace'], + description: + 'Builds a temporary dist workspace from the provided packages', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .arguments(' [packages...]') + .addOption( + new Option( + '--alwaysYarnPack', + 'Alias for --alwaysPack for backwards compatibility.', + ) + .implies({ alwaysPack: true }) + .hideHelp(true), + ) + .option( + '--alwaysPack', + 'Force workspace output to be a result of running `yarn pack` on each package (warning: very slow)', + ) + .action(lazy(() => import('./commands/buildWorkspace'), 'default')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); + +export default buildPlugin; diff --git a/packages/cli/src/modules/config/alpha.ts b/packages/cli/src/modules/config/alpha.ts deleted file mode 100644 index 43bce858a1..0000000000 --- a/packages/cli/src/modules/config/alpha.ts +++ /dev/null @@ -1,137 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import yargs from 'yargs'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'config', - init: async reg => { - reg.addCommand({ - path: ['config:docs'], - description: 'Browse the configuration reference documentation', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .option( - '--package ', - 'Only include the schema that applies to the given package', - ) - .description('Browse the configuration reference documentation') - .action(lazy(() => import('./commands/docs'), 'default')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - reg.addCommand({ - path: ['config', 'docs'], - description: 'Browse the configuration reference documentation', - execute: async ({ args, info }) => { - await new Command(info.usage) - .option( - '--package ', - 'Only include the schema that applies to the given package', - ) - .description(info.description) - .action(lazy(() => import('./commands/docs'), 'default')) - .parseAsync(args, { from: 'user' }); - }, - }); - reg.addCommand({ - path: ['config:print'], - description: 'Print the app configuration for the current package', - execute: async ({ args, info }) => { - const argv = await yargs - .options({ - package: { type: 'string' }, - lax: { type: 'boolean' }, - frontend: { type: 'boolean' }, - 'with-secrets': { type: 'boolean' }, - format: { type: 'string' }, - config: { type: 'string', array: true, default: [] }, - }) - .usage('$0', info.description) - .help() - .parse(args); - const m = - (await require('./commands/print')) as typeof import('./commands/print'); - await m.default(argv); - }, - }); - reg.addCommand({ - path: ['config:check'], - description: - 'Validate that the given configuration loads and matches schema', - execute: async ({ args }) => { - const argv = await yargs - .options({ - package: { type: 'string' }, - lax: { type: 'boolean' }, - frontend: { type: 'boolean' }, - deprecated: { type: 'boolean' }, - strict: { type: 'boolean', required: true }, - config: { - type: 'string', - array: true, - default: [], - }, - }) - .help() - .parse(args); - const m = - (await require('./commands/validate')) as typeof import('./commands/validate'); - await m.default(argv); - }, - }); - - reg.addCommand({ - path: ['config:schema'], - description: 'Print the JSON schema for the given configuration', - execute: async ({ args }) => { - const argv = await yargs - .options({ - package: { type: 'string' }, - format: { type: 'string' }, - merge: { type: 'boolean' }, - 'no-merge': { type: 'boolean' }, - }) - .help() - .parse(args); - const m = await import('./commands/schema'); - await m.default(argv); - }, - }); - - reg.addCommand({ - path: ['config', 'schema'], - description: 'Print the JSON schema for the given configuration', - execute: async ({ args }) => { - const argv = await yargs - .options({ - package: { type: 'string' }, - format: { type: 'string' }, - merge: { type: 'boolean' }, - 'no-merge': { type: 'boolean' }, - }) - .help() - .parse(args); - const m = await import('./commands/schema'); - await m.default(argv); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/config/index.ts b/packages/cli/src/modules/config/index.ts index a1436d0c21..d62dbd4d26 100644 --- a/packages/cli/src/modules/config/index.ts +++ b/packages/cli/src/modules/config/index.ts @@ -13,7 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { createCliPlugin } from '../../wiring/factory'; +import yargs from 'yargs'; import { Command } from 'commander'; import { lazy } from '../../lib/lazy'; @@ -24,64 +25,114 @@ export const configOption = [ Array(), ] as const; -export function registerCommands(program: Command) { - program - .command('config:docs') - .option( - '--package ', - 'Only include the schema that applies to the given package', - ) - .description('Browse the configuration reference documentation') - .action(lazy(() => import('./commands/docs'), 'default')); +export default createCliPlugin({ + pluginId: 'config', + init: async reg => { + reg.addCommand({ + path: ['config:docs'], + description: 'Browse the configuration reference documentation', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .option( + '--package ', + 'Only include the schema that applies to the given package', + ) + .description('Browse the configuration reference documentation') + .action(lazy(() => import('./commands/docs'), 'default')); - program - .command('config:print') - .option( - '--package ', - 'Only load config schema that applies to the given package', - ) - .option('--lax', 'Do not require environment variables to be set') - .option('--frontend', 'Print only the frontend configuration') - .option('--with-secrets', 'Include secrets in the printed configuration') - .option( - '--format ', - 'Format to print the configuration in, either json or yaml [yaml]', - ) - .option(...configOption) - .description('Print the app configuration for the current package') - .action(lazy(() => import('./commands/print'), 'default')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + reg.addCommand({ + path: ['config', 'docs'], + description: 'Browse the configuration reference documentation', + execute: async ({ args, info }) => { + await new Command(info.usage) + .option( + '--package ', + 'Only include the schema that applies to the given package', + ) + .description(info.description) + .action(lazy(() => import('./commands/docs'), 'default')) + .parseAsync(args, { from: 'user' }); + }, + }); + reg.addCommand({ + path: ['config:print'], + description: 'Print the app configuration for the current package', + execute: async ({ args, info }) => { + const argv = await yargs + .options({ + package: { type: 'string' }, + lax: { type: 'boolean' }, + frontend: { type: 'boolean' }, + 'with-secrets': { type: 'boolean' }, + format: { type: 'string' }, + config: { type: 'string', array: true, default: [] }, + }) + .usage('$0', info.description) + .help() + .parse(args); + await lazy(() => import('./commands/print'), 'default')(argv); + }, + }); + reg.addCommand({ + path: ['config:check'], + description: + 'Validate that the given configuration loads and matches schema', + execute: async ({ args }) => { + const argv = await yargs + .options({ + package: { type: 'string' }, + lax: { type: 'boolean' }, + frontend: { type: 'boolean' }, + deprecated: { type: 'boolean' }, + strict: { type: 'boolean' }, + config: { + type: 'string', + array: true, + default: [], + }, + }) + .help() + .parse(args); + await lazy(() => import('./commands/validate'), 'default')(argv); + }, + }); - program - .command('config:check') - .option( - '--package ', - 'Only load config schema that applies to the given package', - ) - .option('--lax', 'Do not require environment variables to be set') - .option('--frontend', 'Only validate the frontend configuration') - .option('--deprecated', 'Output deprecated configuration settings') - .option( - '--strict', - 'Enable strict config validation, forbidding errors and unknown keys', - ) - .option(...configOption) - .description( - 'Validate that the given configuration loads and matches schema', - ) - .action(lazy(() => import('./commands/validate'), 'default')); + reg.addCommand({ + path: ['config:schema'], + description: 'Print the JSON schema for the given configuration', + execute: async ({ args }) => { + const argv = await yargs + .options({ + package: { type: 'string' }, + format: { type: 'string' }, + merge: { type: 'boolean' }, + 'no-merge': { type: 'boolean' }, + }) + .help() + .parse(args); + await lazy(() => import('./commands/schema'), 'default')(argv); + }, + }); - program - .command('config:schema') - .option( - '--package ', - 'Only output config schema that applies to the given package', - ) - .option( - '--format ', - 'Format to print the schema in, either json or yaml [yaml]', - ) - .option('--merge', 'Print the config schemas merged', true) - .option('--no-merge', 'Print the config schemas not merged') - .description('Print configuration schema') - .action(lazy(() => import('./commands/schema'), 'default')); -} + reg.addCommand({ + path: ['config', 'schema'], + description: 'Print the JSON schema for the given configuration', + execute: async ({ args }) => { + const argv = await yargs + .options({ + package: { type: 'string' }, + format: { type: 'string' }, + merge: { type: 'boolean' }, + 'no-merge': { type: 'boolean' }, + }) + .help() + .parse(args); + await lazy(() => import('./commands/schema'), 'default')(argv); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/create-github-app/alpha.ts b/packages/cli/src/modules/create-github-app/alpha.ts deleted file mode 100644 index da1e04aaac..0000000000 --- a/packages/cli/src/modules/create-github-app/alpha.ts +++ /dev/null @@ -1,38 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'new', - init: async reg => { - reg.addCommand({ - path: ['create-github-app'], - description: 'Create new GitHub App in your organization.', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .argument('') - .action( - lazy(() => import('./commands/create-github-app'), 'default'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/create-github-app/index.ts b/packages/cli/src/modules/create-github-app/index.ts index f69230cd77..da1e04aaac 100644 --- a/packages/cli/src/modules/create-github-app/index.ts +++ b/packages/cli/src/modules/create-github-app/index.ts @@ -13,17 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createCliPlugin } from '../../wiring/factory'; import { Command } from 'commander'; import { lazy } from '../../lib/lazy'; -export function registerCommands(program: Command) { - program - .command('create-github-app ') - .description('Create new GitHub App in your organization.') - .action( - lazy( - () => import('../create-github-app/commands/create-github-app'), - 'default', - ), - ); -} +export default createCliPlugin({ + pluginId: 'new', + init: async reg => { + reg.addCommand({ + path: ['create-github-app'], + description: 'Create new GitHub App in your organization.', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .argument('') + .action( + lazy(() => import('./commands/create-github-app'), 'default'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/info/index.ts b/packages/cli/src/modules/info/index.ts index af43af652c..e93f008905 100644 --- a/packages/cli/src/modules/info/index.ts +++ b/packages/cli/src/modules/info/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * 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. @@ -13,13 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import yargs from 'yargs'; +import { createCliPlugin } from '../../wiring/factory'; import { lazy } from '../../lib/lazy'; -import { Command } from 'commander'; -export function registerCommands(program: Command) { - program - .command('info') - .description('Show helpful information for debugging and reporting bugs') - .action(lazy(() => import('./commands/info'), 'default')); -} +export default createCliPlugin({ + pluginId: 'info', + init: async reg => { + reg.addCommand({ + path: ['info'], + description: 'Show helpful information for debugging and reporting bugs', + execute: async ({ args }) => { + yargs().parse(args); + await lazy(() => import('./commands/info'), 'default')(args); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/lint/alpha.ts b/packages/cli/src/modules/lint/alpha.ts deleted file mode 100644 index 617f71d5c9..0000000000 --- a/packages/cli/src/modules/lint/alpha.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'lint', - init: async reg => { - reg.addCommand({ - path: ['package', 'lint'], - description: 'Lint a package', - execute: async ({ args }) => { - const command = new Command(); - command.arguments('[directories...]'); - command.option('--fix', 'Attempt to automatically fix violations'); - command.option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ); - command.option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ); - command.option( - '--max-warnings ', - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - ); - command.description('Lint a package'); - command.action( - lazy(() => import('./commands/package/lint'), 'default'), - ); - - await command.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'lint'], - description: 'Lint a repository', - execute: async ({ args }) => { - const command = new Command(); - command.option('--fix', 'Attempt to automatically fix violations'); - command.option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ); - command.option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ); - command.option( - '--successCache', - 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', - ); - command.option( - '--successCacheDir ', - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - ); - command.option( - '--since ', - 'Only lint packages that changed since the specified ref', - ); - command.option( - '--max-warnings ', - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - ); - command.description('Lint a repository'); - command.action(lazy(() => import('./commands/repo/lint'), 'command')); - - await command.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/lint/index.ts b/packages/cli/src/modules/lint/index.ts index dfa0916efb..6cba232da9 100644 --- a/packages/cli/src/modules/lint/index.ts +++ b/packages/cli/src/modules/lint/index.ts @@ -13,59 +13,83 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createCliPlugin } from '../../wiring/factory'; import { Command } from 'commander'; import { lazy } from '../../lib/lazy'; -export function registerPackageCommands(command: Command) { - command - .command('lint [directories...]') - .option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ) - .option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ) - .option('--fix', 'Attempt to automatically fix violations') - .option( - '--max-warnings ', - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - ) - .description('Lint a package') - .action(lazy(() => import('./commands/package/lint'), 'default')); +export function registerPackageLintCommand(command: Command) { + command.arguments('[directories...]'); + command.option('--fix', 'Attempt to automatically fix violations'); + command.option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ); + command.option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ); + command.option( + '--max-warnings ', + 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', + ); + command.description('Lint a package'); + command.action(lazy(() => import('./commands/package/lint'), 'default')); } -export function registerRepoCommands(command: Command) { - command - .command('lint') - .description('Lint all packages in the project') - .option( - '--format ', - 'Lint report output format', - 'eslint-formatter-friendly', - ) - .option( - '--output-file ', - 'Write the lint report to a file instead of stdout', - ) - .option( - '--since ', - 'Only lint packages that changed since the specified ref', - ) - .option( - '--successCache', - 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', - ) - .option( - '--successCacheDir ', - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - ) - .option( - '--max-warnings ', - 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', - ) - .option('--fix', 'Attempt to automatically fix violations') - .action(lazy(() => import('./commands/repo/lint'), 'command')); -} +export default createCliPlugin({ + pluginId: 'lint', + init: async reg => { + reg.addCommand({ + path: ['package', 'lint'], + description: 'Lint a package', + execute: async ({ args }) => { + const command = new Command(); + registerPackageLintCommand(command); + + await command.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'lint'], + description: 'Lint a repository', + execute: async ({ args }) => { + const command = new Command(); + + registerPackageLintCommand(command.command('package').command('lint')); + + command.option('--fix', 'Attempt to automatically fix violations'); + command.option( + '--format ', + 'Lint report output format', + 'eslint-formatter-friendly', + ); + command.option( + '--output-file ', + 'Write the lint report to a file instead of stdout', + ); + command.option( + '--successCache', + 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', + ); + command.option( + '--successCacheDir ', + 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', + ); + command.option( + '--since ', + 'Only lint packages that changed since the specified ref', + ); + command.option( + '--max-warnings ', + 'Fail if more than this number of warnings. -1 allows warnings. (default: -1)', + ); + command.description('Lint a repository'); + command.action(lazy(() => import('./commands/repo/lint'), 'command')); + + await command.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/maintenance/alpha.ts b/packages/cli/src/modules/maintenance/alpha.ts deleted file mode 100644 index 41192afdc4..0000000000 --- a/packages/cli/src/modules/maintenance/alpha.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* - * 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 { Command } from 'commander'; -import { createCliPlugin } from '../../wiring/factory'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'maintenance', - init: async reg => { - reg.addCommand({ - path: ['package', 'clean'], - description: 'Delete cache directories', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/package/clean'), 'default'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['package', 'prepack'], - description: 'Prepares a package for packaging before publishing', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/package/pack'), 'pre'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['package', 'postpack'], - description: 'Restores the changes made by the prepack command', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/package/pack'), 'post'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'fix'], - description: 'Automatically fix packages in the project', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .option( - '--publish', - 'Enable additional fixes that only apply when publishing packages', - ) - .option( - '--check', - 'Fail if any packages would have been changed by the command', - ) - .action(lazy(() => import('./commands/repo/fix'), 'command')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'clean'], - description: 'Delete cache and output directories', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/repo/clean'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['repo', 'list-deprecations'], - description: 'List deprecations', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .option('--json', 'Output as JSON') - .action( - lazy(() => import('./commands/repo/list-deprecations'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/maintenance/index.ts b/packages/cli/src/modules/maintenance/index.ts index 48669e9c34..41192afdc4 100644 --- a/packages/cli/src/modules/maintenance/index.ts +++ b/packages/cli/src/modules/maintenance/index.ts @@ -13,49 +13,98 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { Command } from 'commander'; +import { createCliPlugin } from '../../wiring/factory'; import { lazy } from '../../lib/lazy'; -export function registerPackageCommands(command: Command) { - command - .command('clean') - .description('Delete cache directories') - .action(lazy(() => import('./commands/package/clean'), 'default')); +export default createCliPlugin({ + pluginId: 'maintenance', + init: async reg => { + reg.addCommand({ + path: ['package', 'clean'], + description: 'Delete cache directories', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/package/clean'), 'default'), + ); - command - .command('prepack') - .description('Prepares a package for packaging before publishing') - .action(lazy(() => import('./commands/package/pack'), 'pre')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); - command - .command('postpack') - .description('Restores the changes made by the prepack command') - .action(lazy(() => import('./commands/package/pack'), 'post')); -} + reg.addCommand({ + path: ['package', 'prepack'], + description: 'Prepares a package for packaging before publishing', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/package/pack'), 'pre'), + ); -export function registerRepoCommands(command: Command) { - command - .command('fix') - .description('Automatically fix packages in the project') - .option( - '--publish', - 'Enable additional fixes that only apply when publishing packages', - ) - .option( - '--check', - 'Fail if any packages would have been changed by the command', - ) - .action(lazy(() => import('./commands/repo/fix'), 'command')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); - command - .command('clean') - .description('Delete cache and output directories') - .action(lazy(() => import('./commands/repo/clean'), 'command')); + reg.addCommand({ + path: ['package', 'postpack'], + description: 'Restores the changes made by the prepack command', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/package/pack'), 'post'), + ); - command - .command('list-deprecations') - .description('List deprecations') - .option('--json', 'Output as JSON') - .action(lazy(() => import('./commands/repo/list-deprecations'), 'command')); -} + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'fix'], + description: 'Automatically fix packages in the project', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .option( + '--publish', + 'Enable additional fixes that only apply when publishing packages', + ) + .option( + '--check', + 'Fail if any packages would have been changed by the command', + ) + .action(lazy(() => import('./commands/repo/fix'), 'command')); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'clean'], + description: 'Delete cache and output directories', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/repo/clean'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['repo', 'list-deprecations'], + description: 'List deprecations', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .option('--json', 'Output as JSON') + .action( + lazy(() => import('./commands/repo/list-deprecations'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/migrate/alpha.ts b/packages/cli/src/modules/migrate/alpha.ts deleted file mode 100644 index ac56c06b7a..0000000000 --- a/packages/cli/src/modules/migrate/alpha.ts +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'migrate', - init: async reg => { - reg.addCommand({ - path: ['versions:migrate'], - description: - 'Migrate any plugins that have been moved to the @backstage-community namespace automatically', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .option( - '--pattern ', - 'Override glob for matching packages to upgrade', - ) - .option( - '--skip-code-changes', - 'Skip code changes and only update package.json files', - ) - .action(lazy(() => import('./commands/versions/migrate'), 'default')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['versions:bump'], - description: 'Bump Backstage packages to the latest versions', - execute: async ({ args }) => { - const command = new Command(); - - const defaultCommand = command - .option( - '--pattern ', - 'Override glob for matching packages to upgrade', - ) - .option( - '--release ', - 'Bump to a specific Backstage release line or version', - 'main', - ) - .option('--skip-install', 'Skips yarn install step') - .option('--skip-migrate', 'Skips migration of any moved packages') - .action(lazy(() => import('./commands/versions/bump'), 'default')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['migrate', 'package-roles'], - description: `Add package role field to packages that don't have it`, - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/packageRole'), 'default'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['migrate', 'package-scripts'], - description: 'Set package scripts according to each package role', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/packageScripts'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['migrate', 'package-exports'], - description: 'Synchronize package subpath export definitions', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/packageExports'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['migrate', 'package-lint-configs'], - description: - 'Migrates all packages to use @backstage/cli/config/eslint-factory', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/packageLintConfigs'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['migrate', 'react-router-deps'], - description: - 'Migrates the react-router dependencies for all packages to be peer dependencies', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command.action( - lazy(() => import('./commands/reactRouterDeps'), 'command'), - ); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/migrate/index.ts b/packages/cli/src/modules/migrate/index.ts index 10e96ad173..ac56c06b7a 100644 --- a/packages/cli/src/modules/migrate/index.ts +++ b/packages/cli/src/modules/migrate/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2025 The Backstage Authors + * 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. @@ -13,72 +13,123 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { lazy } from '../../lib/lazy'; +import { createCliPlugin } from '../../wiring/factory'; import { Command } from 'commander'; +import { lazy } from '../../lib/lazy'; -export function registerCommands(program: Command) { - program - .command('versions:bump') - .option( - '--pattern ', - 'Override glob for matching packages to upgrade', - ) - .option( - '--release ', - 'Bump to a specific Backstage release line or version', - 'main', - ) - .option('--skip-install', 'Skips yarn install step') - .option('--skip-migrate', 'Skips migration of any moved packages') - .description('Bump Backstage packages to the latest versions') - .action(lazy(() => import('./commands/versions/bump'), 'default')); +export default createCliPlugin({ + pluginId: 'migrate', + init: async reg => { + reg.addCommand({ + path: ['versions:migrate'], + description: + 'Migrate any plugins that have been moved to the @backstage-community namespace automatically', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .option( + '--pattern ', + 'Override glob for matching packages to upgrade', + ) + .option( + '--skip-code-changes', + 'Skip code changes and only update package.json files', + ) + .action(lazy(() => import('./commands/versions/migrate'), 'default')); - program - .command('versions:migrate') - .option( - '--pattern ', - 'Override glob for matching packages to upgrade', - ) - .option( - '--skip-code-changes', - 'Skip code changes and only update package.json files', - ) - .description( - 'Migrate any plugins that have been moved to the @backstage-community namespace automatically', - ) - .action(lazy(() => import('./commands/versions/migrate'), 'default')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); - const command = program - .command('migrate [command]') - .description('Migration utilities'); + reg.addCommand({ + path: ['versions:bump'], + description: 'Bump Backstage packages to the latest versions', + execute: async ({ args }) => { + const command = new Command(); - command - .command('package-roles') - .description(`Add package role field to packages that don't have it`) - .action(lazy(() => import('./commands/packageRole'), 'default')); + const defaultCommand = command + .option( + '--pattern ', + 'Override glob for matching packages to upgrade', + ) + .option( + '--release ', + 'Bump to a specific Backstage release line or version', + 'main', + ) + .option('--skip-install', 'Skips yarn install step') + .option('--skip-migrate', 'Skips migration of any moved packages') + .action(lazy(() => import('./commands/versions/bump'), 'default')); - command - .command('package-scripts') - .description('Set package scripts according to each package role') - .action(lazy(() => import('./commands/packageScripts'), 'command')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); - command - .command('package-exports') - .description('Synchronize package subpath export definitions') - .action(lazy(() => import('./commands/packageExports'), 'command')); + reg.addCommand({ + path: ['migrate', 'package-roles'], + description: `Add package role field to packages that don't have it`, + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/packageRole'), 'default'), + ); - command - .command('package-lint-configs') - .description( - 'Migrates all packages to use @backstage/cli/config/eslint-factory', - ) - .action(lazy(() => import('./commands/packageLintConfigs'), 'command')); + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); - command - .command('react-router-deps') - .description( - 'Migrates the react-router dependencies for all packages to be peer dependencies', - ) - .action(lazy(() => import('./commands/reactRouterDeps'), 'command')); -} + reg.addCommand({ + path: ['migrate', 'package-scripts'], + description: 'Set package scripts according to each package role', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/packageScripts'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['migrate', 'package-exports'], + description: 'Synchronize package subpath export definitions', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/packageExports'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['migrate', 'package-lint-configs'], + description: + 'Migrates all packages to use @backstage/cli/config/eslint-factory', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/packageLintConfigs'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['migrate', 'react-router-deps'], + description: + 'Migrates the react-router dependencies for all packages to be peer dependencies', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command.action( + lazy(() => import('./commands/reactRouterDeps'), 'command'), + ); + + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/new/alpha.ts b/packages/cli/src/modules/new/alpha.ts deleted file mode 100644 index 6802077507..0000000000 --- a/packages/cli/src/modules/new/alpha.ts +++ /dev/null @@ -1,86 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; -import { removed } from '../../lib/removed'; - -export default createCliPlugin({ - pluginId: 'new', - init: async reg => { - reg.addCommand({ - path: ['new'], - description: - 'Open up an interactive guide to creating new things in your app', - execute: async ({ args }) => { - const command = new Command(); - const defaultCommand = command - .storeOptionsAsProperties(false) - .description( - 'Open up an interactive guide to creating new things in your app', - ) - .option( - '--select ', - 'Select the thing you want to be creating upfront', - ) - .option( - '--option =', - 'Pre-fill options for the creation process', - (opt, arr: string[]) => [...arr, opt], - [], - ) - .option( - '--skip-install', - `Skips running 'yarn install' and 'yarn lint --fix'`, - ) - .option('--scope ', 'The scope to use for new packages') - .option( - '--npm-registry ', - 'The package registry to use for new packages', - ) - .option( - '--baseVersion ', - 'The version to use for any new packages (default: 0.1.0)', - ) - .option( - '--license ', - 'The license to use for any new packages (default: Apache-2.0)', - ) - .option('--no-private', 'Do not mark new packages as private') - .action(lazy(() => import('./commands/new'), 'default')); - - await defaultCommand.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['create'], - description: 'Create a new Backstage app', - deprecated: true, - execute: async () => { - removed("use 'backstage-cli new' instead")(); - }, - }); - reg.addCommand({ - path: ['create-plugin'], - description: 'Create a new Backstage plugin', - deprecated: true, - execute: async () => { - removed("use 'backstage-cli new' instead")(); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/new/index.ts b/packages/cli/src/modules/new/index.ts index fa288f5f46..6802077507 100644 --- a/packages/cli/src/modules/new/index.ts +++ b/packages/cli/src/modules/new/index.ts @@ -13,53 +13,74 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { createCliPlugin } from '../../wiring/factory'; import { Command } from 'commander'; import { lazy } from '../../lib/lazy'; import { removed } from '../../lib/removed'; -export function registerCommands(program: Command) { - program - .command('new') - .storeOptionsAsProperties(false) - .description( - 'Open up an interactive guide to creating new things in your app', - ) - .option( - '--select ', - 'Select the thing you want to be creating upfront', - ) - .option( - '--option =', - 'Pre-fill options for the creation process', - (opt, arr: string[]) => [...arr, opt], - [], - ) - .option( - '--skip-install', - `Skips running 'yarn install' and 'yarn lint --fix'`, - ) - .option('--scope ', 'The scope to use for new packages') - .option( - '--npm-registry ', - 'The package registry to use for new packages', - ) - .option( - '--baseVersion ', - 'The version to use for any new packages (default: 0.1.0)', - ) - .option( - '--license ', - 'The license to use for any new packages (default: Apache-2.0)', - ) - .option('--no-private', 'Do not mark new packages as private') - .action(lazy(() => import('./commands/new'), 'default')); +export default createCliPlugin({ + pluginId: 'new', + init: async reg => { + reg.addCommand({ + path: ['new'], + description: + 'Open up an interactive guide to creating new things in your app', + execute: async ({ args }) => { + const command = new Command(); + const defaultCommand = command + .storeOptionsAsProperties(false) + .description( + 'Open up an interactive guide to creating new things in your app', + ) + .option( + '--select ', + 'Select the thing you want to be creating upfront', + ) + .option( + '--option =', + 'Pre-fill options for the creation process', + (opt, arr: string[]) => [...arr, opt], + [], + ) + .option( + '--skip-install', + `Skips running 'yarn install' and 'yarn lint --fix'`, + ) + .option('--scope ', 'The scope to use for new packages') + .option( + '--npm-registry ', + 'The package registry to use for new packages', + ) + .option( + '--baseVersion ', + 'The version to use for any new packages (default: 0.1.0)', + ) + .option( + '--license ', + 'The license to use for any new packages (default: Apache-2.0)', + ) + .option('--no-private', 'Do not mark new packages as private') + .action(lazy(() => import('./commands/new'), 'default')); - program - .command('create') - .allowUnknownOption(true) - .action(removed("use 'backstage-cli new' instead")); - program - .command('create-plugin') - .allowUnknownOption(true) - .action(removed("use 'backstage-cli new' instead")); -} + await defaultCommand.parseAsync(args, { from: 'user' }); + }, + }); + + reg.addCommand({ + path: ['create'], + description: 'Create a new Backstage app', + deprecated: true, + execute: async () => { + removed("use 'backstage-cli new' instead")(); + }, + }); + reg.addCommand({ + path: ['create-plugin'], + description: 'Create a new Backstage plugin', + deprecated: true, + execute: async () => { + removed("use 'backstage-cli new' instead")(); + }, + }); + }, +}); diff --git a/packages/cli/src/modules/test/alpha.ts b/packages/cli/src/modules/test/alpha.ts deleted file mode 100644 index 1583c74997..0000000000 --- a/packages/cli/src/modules/test/alpha.ts +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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 { createCliPlugin } from '../../wiring/factory'; -import { Command } from 'commander'; -import { lazy } from '../../lib/lazy'; - -export default createCliPlugin({ - pluginId: 'test', - init: async reg => { - reg.addCommand({ - path: ['repo', 'test'], - description: - 'Run tests, forwarding args to Jest, defaulting to watch mode', - execute: async ({ args }) => { - const command = new Command(); - command.allowUnknownOption(true); - command.option( - '--since ', - 'Only test packages that changed since the specified ref', - ); - command.option('--successCache', 'Enable success caching'); - command.option( - '--successCacheDir ', - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - ); - command.option( - '--jest-help', - 'Show help for Jest CLI options, which are passed through', - ); - command.action(lazy(() => import('./commands/repo/test'), 'command')); - await command.parseAsync(args, { from: 'user' }); - }, - }); - - reg.addCommand({ - path: ['package', 'test'], - description: - 'Run tests, forwarding args to Jest, defaulting to watch mode', - execute: async ({ args }) => { - const command = new Command(); - - command.allowUnknownOption(true); - command.helpOption(', --backstage-cli-help'); - command.action( - lazy(() => import('./commands/package/test'), 'default'), - ); - await command.parseAsync(args, { from: 'user' }); - }, - }); - }, -}); diff --git a/packages/cli/src/modules/test/index.ts b/packages/cli/src/modules/test/index.ts index 8e4720109f..1583c74997 100644 --- a/packages/cli/src/modules/test/index.ts +++ b/packages/cli/src/modules/test/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2025 The Backstage Authors + * 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. @@ -13,39 +13,52 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +import { createCliPlugin } from '../../wiring/factory'; import { Command } from 'commander'; import { lazy } from '../../lib/lazy'; -export function registerRepoCommands(command: Command) { - command - .command('test') - .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args - .option( - '--since ', - 'Only test packages that changed since the specified ref', - ) - .option( - '--successCache', - 'Enable success caching, which skips running tests for unchanged packages that were successful in the previous run', - ) - .option( - '--successCacheDir ', - 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', - ) - .option( - '--jest-help', - 'Show help for Jest CLI options, which are passed through', - ) - .description('Run tests, forwarding args to Jest, defaulting to watch mode') - .action(lazy(() => import('./commands/repo/test'), 'command')); -} +export default createCliPlugin({ + pluginId: 'test', + init: async reg => { + reg.addCommand({ + path: ['repo', 'test'], + description: + 'Run tests, forwarding args to Jest, defaulting to watch mode', + execute: async ({ args }) => { + const command = new Command(); + command.allowUnknownOption(true); + command.option( + '--since ', + 'Only test packages that changed since the specified ref', + ); + command.option('--successCache', 'Enable success caching'); + command.option( + '--successCacheDir ', + 'Set the success cache location, (default: node_modules/.cache/backstage-cli)', + ); + command.option( + '--jest-help', + 'Show help for Jest CLI options, which are passed through', + ); + command.action(lazy(() => import('./commands/repo/test'), 'command')); + await command.parseAsync(args, { from: 'user' }); + }, + }); -export function registerPackageCommands(command: Command) { - command - .command('test') - .allowUnknownOption(true) // Allows the command to run, but we still need to parse raw args - .helpOption(', --backstage-cli-help') // Let Jest handle help - .description('Run tests, forwarding args to Jest, defaulting to watch mode') - .action(lazy(() => import('./commands/package/test'), 'default')); -} + reg.addCommand({ + path: ['package', 'test'], + description: + 'Run tests, forwarding args to Jest, defaulting to watch mode', + execute: async ({ args }) => { + const command = new Command(); + + command.allowUnknownOption(true); + command.helpOption(', --backstage-cli-help'); + command.action( + lazy(() => import('./commands/package/test'), 'default'), + ); + await command.parseAsync(args, { from: 'user' }); + }, + }); + }, +}); diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index 3b933aeeea..16e6a7bfc0 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/config-loader +## 1.10.2-next.0 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- Updated dependencies + - @backstage/config@1.3.3-next.0 + ## 1.10.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 364d1a2bc8..66a8a7867e 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config-loader", - "version": "1.10.1", + "version": "1.10.2-next.0", "description": "Config loading functionality used by Backstage backend, and CLI", "backstage": { "role": "node-library" diff --git a/packages/config-loader/src/sources/EnvConfigSource.ts b/packages/config-loader/src/sources/EnvConfigSource.ts index 5b4590e107..d724f00c6d 100644 --- a/packages/config-loader/src/sources/EnvConfigSource.ts +++ b/packages/config-loader/src/sources/EnvConfigSource.ts @@ -85,7 +85,7 @@ export class EnvConfigSource implements ConfigSource { const ENV_PREFIX = 'APP_CONFIG_'; // Update the same pattern in config package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; /** * Read runtime configuration from the environment. diff --git a/packages/config/CHANGELOG.md b/packages/config/CHANGELOG.md index 00c940e206..eac07507ac 100644 --- a/packages/config/CHANGELOG.md +++ b/packages/config/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/config +## 1.3.3-next.0 + +### Patch Changes + +- ff23618: Loosen the requirements for a key to be considered valid config. +- 3507fcd: Just some more circular dep cleanup + ## 1.3.2 ### Patch Changes diff --git a/packages/config/package.json b/packages/config/package.json index e4ace9bdd8..101d45820d 100644 --- a/packages/config/package.json +++ b/packages/config/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/config", - "version": "1.3.2", + "version": "1.3.3-next.0", "description": "Config API used by Backstage core, backend, and CLI", "backstage": { "role": "common-library" diff --git a/packages/config/report.api.md b/packages/config/report.api.md index ea9617b2a3..848687d0cc 100644 --- a/packages/config/report.api.md +++ b/packages/config/report.api.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { Config as Config_2 } from '@backstage/config'; import { HumanDuration } from '@backstage/types'; import type { JsonArray as JsonArray_2 } from '@backstage/types'; import { JsonObject as JsonObject_2 } from '@backstage/types'; @@ -85,7 +84,7 @@ export type JsonValue = JsonValue_2; // @public export function readDurationFromConfig( - config: Config_2, + config: Config, options?: { key?: string; }, diff --git a/packages/config/src/readDurationFromConfig.ts b/packages/config/src/readDurationFromConfig.ts index e02651084e..6ee0d39145 100644 --- a/packages/config/src/readDurationFromConfig.ts +++ b/packages/config/src/readDurationFromConfig.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { Config } from '@backstage/config'; import { InputError, stringifyError } from '@backstage/errors'; import { HumanDuration } from '@backstage/types'; import ms from 'ms'; +import { Config } from './types'; export const propsOfHumanDuration = [ 'years', diff --git a/packages/config/src/reader.test.ts b/packages/config/src/reader.test.ts index 97982ac265..3f6b9ac40a 100644 --- a/packages/config/src/reader.test.ts +++ b/packages/config/src/reader.test.ts @@ -171,6 +171,7 @@ describe('ConfigReader', () => { expect(config.getOptionalString('x_x')).toBeUndefined(); expect(config.getOptionalString('x-X')).toBeUndefined(); expect(config.getOptionalString('x0')).toBeUndefined(); + expect(config.getOptionalString('x-2')).toBeUndefined(); expect(config.getOptionalString('X-x2')).toBeUndefined(); expect(config.getOptionalString('x0_x0')).toBeUndefined(); expect(config.getOptionalString('x_x-x_x')).toBeUndefined(); diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 52c33f8618..2e83de9bf2 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -18,7 +18,7 @@ import { JsonValue, JsonObject } from '@backstage/types'; import { AppConfig, Config } from './types'; // Update the same pattern in config-loader package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i; +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; function isObject(value: JsonValue | undefined): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index 6b5a5727e8..9db59601ce 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/core-app-api +## 1.18.0-next.1 + +### Minor Changes + +- 5ddc0fe: if session exists and refresh fails, then create a new session if not instant popup + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.9-next.0 + +## 1.17.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 1.17.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 1.17.0 ### Minor Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index 73fef45b06..82101dc33d 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.17.0", + "version": "1.18.0-next.1", "description": "Core app API used by Backstage apps", "backstage": { "role": "web-library" diff --git a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts index 9afc710bdd..d5e72a2365 100644 --- a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts +++ b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts @@ -138,7 +138,7 @@ describe('RefreshingAuthSessionManager', () => { expect(refreshSession).toHaveBeenCalledWith({ scopes: new Set() }); }); - it('should forward option to instantly show auth popup and not attempt refresh', async () => { + it('should forward option to instantly show auth popup after attempting refresh', async () => { const createSession = jest.fn(); const refreshSession = jest.fn().mockRejectedValue(new Error('NOPE')); const manager = new RefreshingAuthSessionManager({ @@ -152,7 +152,7 @@ describe('RefreshingAuthSessionManager', () => { scopes: new Set(), instantPopup: true, }); - expect(refreshSession).toHaveBeenCalledTimes(0); + expect(refreshSession).toHaveBeenCalledTimes(1); }); it('should remove session straight away', async () => { @@ -275,4 +275,26 @@ describe('RefreshingAuthSessionManager', () => { expect(refreshSession).toHaveBeenCalledTimes(1); expect(createSession).toHaveBeenCalledTimes(1); }); + + it('should create a new session if refresh fails with existing expired session', async () => { + const createSession = jest.fn(); + const refreshSession = jest.fn().mockRejectedValue(new Error('NOPE')); + const manager = new RefreshingAuthSessionManager({ + connector: { createSession, refreshSession }, + ...defaultOptions, + } as any); + + createSession.mockResolvedValue({ + scopes: new Set(['a']), + expired: true, + }); + await manager.getSession({ scopes: new Set(['a']) }); + expect(refreshSession).toHaveBeenCalledTimes(1); + expect(createSession).toHaveBeenCalledTimes(1); + + await manager.getSession({ scopes: new Set(['a']) }); + // call refresh session only once + expect(refreshSession).toHaveBeenCalledTimes(2); + expect(createSession).toHaveBeenCalledTimes(2); + }); }); diff --git a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts index 216409a528..fbb57b4a5e 100644 --- a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts +++ b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts @@ -64,6 +64,7 @@ export class RefreshingAuthSessionManager implements SessionManager { } async getSession(options: GetSessionOptions): Promise { + let alreadyTriedToRefreshSession = false; if ( this.helper.sessionExistsAndHasScope(this.currentSession, options.scopes) ) { @@ -72,6 +73,8 @@ export class RefreshingAuthSessionManager implements SessionManager { return this.currentSession!; } + alreadyTriedToRefreshSession = true; + try { const refreshedSession = await this.collapsedSessionRefresh( options.scopes, @@ -83,27 +86,31 @@ export class RefreshingAuthSessionManager implements SessionManager { } return refreshedSession; } catch (error) { + this.removeLocalSession(); + if (options.optional) { return undefined; } - throw error; + // If the refresh attempt fails we assume we don't have a session, so continue to create one } } // The user may still have a valid refresh token in their cookies. Attempt to // initiate a fresh session through the backend using that refresh token. // - // We skip this check if an instant login popup is requested, as we need to - // stay in a synchronous call stack from the user interaction. The downside - // is that the user will sometimes be requested to log in even if they - // already had an existing session. - if (!options.instantPopup) { + // We can still try to refresh even if client requested instant popup. + // With instant popup option, the client is responsible for providing the user login prompt modal window. + // If control flow executes this code and client requested instant popup, it means that + // must have clicked sign in on the login prompt. The browser allows asynchronous code to open a popup + // if it is caused by a user interaction, clicking on a sign-in button, for example. + if (!alreadyTriedToRefreshSession) { try { const newSession = await this.collapsedSessionRefresh(options.scopes); this.currentSession = newSession; // The session might not have the scopes requested so go back and check again return this.getSession(options); } catch { + this.removeLocalSession(); // If the refresh attempt fails we assume we don't have a session, so continue to create one. } } @@ -122,6 +129,18 @@ export class RefreshingAuthSessionManager implements SessionManager { return this.currentSession; } + /** + * Sets `undefined` to this.{@link currentSession} and tells this.{@link stateTracker}, session state tracker, + * that a user has signed out. + * + * Does not propagate session removal to the connector like {@link removeSession}(). + * + */ + removeLocalSession() { + this.currentSession = undefined; + this.stateTracker.setIsSignedIn(false); + } + async removeSession() { this.currentSession = undefined; await this.connector.removeSession(); diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md index e1919518ea..2adfc18200 100644 --- a/packages/core-compat-api/CHANGELOG.md +++ b/packages/core-compat-api/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/core-compat-api +## 0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/version-bridge@1.0.11 + +## 0.4.3 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/version-bridge@1.0.11 + +## 0.4.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-plugin-api@1.10.7 + - @backstage/version-bridge@1.0.11 + ## 0.4.3-next.1 ### Patch Changes diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index db865fbec9..78857ab64b 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.3-next.1", + "version": "0.4.4-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 735fd7b84c..e7c2db51e7 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/core-components +## 0.17.4-next.2 + +### Patch Changes + +- aa3b054: Added `signIn` and `signOut` analytic events to the `@backstage/core-components` of sign in and sign out. +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.17.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.17.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/version-bridge@1.0.11 + +## 0.17.3 + +### Patch Changes + +- 6232160: table actions header support i18n +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/version-bridge@1.0.11 + +## 0.17.3-next.0 + +### Patch Changes + +- 6232160: table actions header support i18n +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/version-bridge@1.0.11 + ## 0.17.2 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 47641194af..bb918481ec 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-components", - "version": "0.17.2", + "version": "0.17.4-next.2", "description": "Core components used by Backstage plugins and apps", "backstage": { "role": "web-library" diff --git a/packages/core-components/report-alpha.api.md b/packages/core-components/report-alpha.api.md index 67c4226322..32bd2f4939 100644 --- a/packages/core-components/report-alpha.api.md +++ b/packages/core-components/report-alpha.api.md @@ -50,6 +50,7 @@ export const coreComponentsTranslationRef: TranslationRef< readonly 'supportConfig.default.title': 'Support Not Configured'; readonly 'supportConfig.default.linkTitle': 'Add `app.support` config key'; readonly 'errorBoundary.title': 'Please contact {{slackChannel}} for help.'; + readonly 'oauthRequestDialog.message': 'Sign-in to allow {{appTitle}} access to {{provider}} APIs and identities.'; readonly 'oauthRequestDialog.title': 'Login Required'; readonly 'oauthRequestDialog.authRedirectTitle': 'This will trigger a http redirect to OAuth Login.'; readonly 'oauthRequestDialog.login': 'Log in'; diff --git a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx index 7c85b1608f..328ab50a34 100644 --- a/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx +++ b/packages/core-components/src/components/OAuthRequestDialog/LoginRequestListItem.tsx @@ -22,16 +22,24 @@ import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; import { useState } from 'react'; import { isError } from '@backstage/errors'; -import { PendingOAuthRequest } from '@backstage/core-plugin-api'; +import { + configApiRef, + PendingOAuthRequest, + useApi, +} from '@backstage/core-plugin-api'; import { coreComponentsTranslationRef } from '../../translation'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; +import Box from '@material-ui/core/Box'; export type LoginRequestListItemClassKey = 'root'; const useItemStyles = makeStyles( theme => ({ root: { - paddingLeft: theme.spacing(3), + paddingLeft: theme.spacing(2), + }, + button: { + marginLeft: theme.spacing(2), }, }), { name: 'BackstageLoginRequestListItem' }, @@ -47,6 +55,7 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { const classes = useItemStyles(); const [error, setError] = useState(); const { t } = useTranslationRef(coreComponentsTranslationRef); + const configApi = useApi(configApiRef); const handleContinue = async () => { setBusy(true); @@ -60,19 +69,43 @@ const LoginRequestListItem = ({ request, busy, setBusy }: RowProps) => { }; const IconComponent = request.provider.icon; + const message = + request.provider.message ?? + t('oauthRequestDialog.message', { + appTitle: configApi.getString('app.title'), + provider: request.provider.title, + }); return ( - {error}} - /> - + + + + {message && ( + + {message} + + )} + {error && {error}} + + } + /> + + + ); }; diff --git a/packages/core-components/src/layout/SignInPage/SignInPage.tsx b/packages/core-components/src/layout/SignInPage/SignInPage.tsx index d9617c5c4b..24de4d4647 100644 --- a/packages/core-components/src/layout/SignInPage/SignInPage.tsx +++ b/packages/core-components/src/layout/SignInPage/SignInPage.tsx @@ -18,6 +18,7 @@ import { BackstageIdentityResponse, configApiRef, SignInPageProps, + useAnalytics, useApi, } from '@backstage/core-plugin-api'; import { UserIdentity } from './UserIdentity'; @@ -116,6 +117,7 @@ export const SingleSignInPage = ({ const authApi = useApi(provider.apiRef); const configApi = useApi(configApiRef); const { t } = useTranslationRef(coreComponentsTranslationRef); + const analytics = useAnalytics(); const [error, setError] = useState(); @@ -167,6 +169,7 @@ export const SingleSignInPage = ({ profile, }), ); + analytics.captureEvent('signIn', 'success'); } catch (err: any) { // User closed the sign-in modal setError(err); diff --git a/packages/core-components/src/layout/SignInPage/providers.tsx b/packages/core-components/src/layout/SignInPage/providers.tsx index 179c7a4542..e9e43e9c7e 100644 --- a/packages/core-components/src/layout/SignInPage/providers.tsx +++ b/packages/core-components/src/layout/SignInPage/providers.tsx @@ -21,6 +21,7 @@ import { useApiHolder, errorApiRef, IdentityApi, + useAnalytics, } from '@backstage/core-plugin-api'; import { IdentityProviders, @@ -92,6 +93,7 @@ export const useSignInProviders = ( const errorApi = useApi(errorApiRef); const apiHolder = useApiHolder(); const [loading, setLoading] = useState(true); + const analytics = useAnalytics(); const { t } = useTranslationRef(coreComponentsTranslationRef); // User was redirected back to sign in page with error from auth redirect flow @@ -108,18 +110,20 @@ export const useSignInProviders = ( // This decorates the result with sign out logic from this hook const handleWrappedResult = useCallback( - (identityApi: IdentityApi) => { + async (identityApi: IdentityApi) => { onSignInSuccess( IdentityApiSignOutProxy.from({ identityApi, signOut: async () => { localStorage.removeItem(PROVIDER_STORAGE_KEY); await identityApi.signOut?.(); + analytics.captureEvent('signOut', 'success'); }, }), ); + analytics.captureEvent('signIn', 'success'); }, - [onSignInSuccess], + [onSignInSuccess, analytics], ); // In this effect we check if the user has already selected an existing login diff --git a/packages/core-components/src/translation.ts b/packages/core-components/src/translation.ts index 8313b75a40..b884f0af92 100644 --- a/packages/core-components/src/translation.ts +++ b/packages/core-components/src/translation.ts @@ -79,6 +79,8 @@ export const coreComponentsTranslationRef = createTranslationRef({ authRedirectTitle: 'This will trigger a http redirect to OAuth Login.', login: 'Log in', rejectAll: 'Reject All', + message: + 'Sign-in to allow {{appTitle}} access to {{provider}} APIs and identities.', }, supportButton: { title: 'Support', diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index 6aee788421..921953a981 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/core-plugin-api +## 1.10.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + +## 1.10.8 + +### Patch Changes + +- c83cd8b: Fixed some circular or otherwise unclear imports +- 0169b23: Internal tweak to avoid circular dependencies +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 1.10.7 ### Patch Changes diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 3671d16609..dbc82560d9 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/core-plugin-api", - "version": "1.10.7", + "version": "1.10.9-next.0", "description": "Core API used by Backstage plugins", "backstage": { "role": "web-library" diff --git a/packages/core-plugin-api/report.api.md b/packages/core-plugin-api/report.api.md index d29c611abb..54bbfb94cf 100644 --- a/packages/core-plugin-api/report.api.md +++ b/packages/core-plugin-api/report.api.md @@ -197,6 +197,7 @@ export type AuthProviderInfo = { id: string; title: string; icon: IconComponent; + message?: string; }; // @public diff --git a/packages/core-plugin-api/src/analytics/Tracker.ts b/packages/core-plugin-api/src/analytics/Tracker.ts index 130410e2a2..4ae31b43fd 100644 --- a/packages/core-plugin-api/src/analytics/Tracker.ts +++ b/packages/core-plugin-api/src/analytics/Tracker.ts @@ -20,7 +20,7 @@ import { AnalyticsEventAttributes, AnalyticsTracker, } from '../apis'; -import { AnalyticsContextValue } from './'; +import { AnalyticsContextValue } from './types'; type TempGlobalEvents = { /** diff --git a/packages/core-plugin-api/src/apis/definitions/TranslationApi.ts b/packages/core-plugin-api/src/apis/definitions/TranslationApi.ts index 7c3c3f8e83..5cfb14abb2 100644 --- a/packages/core-plugin-api/src/apis/definitions/TranslationApi.ts +++ b/packages/core-plugin-api/src/apis/definitions/TranslationApi.ts @@ -16,7 +16,7 @@ import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; import { Expand, ExpandRecursive, Observable } from '@backstage/types'; -import { TranslationRef } from '../../translation'; +import { TranslationRef } from '../../translation/TranslationRef'; import { JSX } from 'react'; /** diff --git a/packages/core-plugin-api/src/apis/definitions/auth.ts b/packages/core-plugin-api/src/apis/definitions/auth.ts index d89544cf68..9339a3e18a 100644 --- a/packages/core-plugin-api/src/apis/definitions/auth.ts +++ b/packages/core-plugin-api/src/apis/definitions/auth.ts @@ -54,6 +54,11 @@ export type AuthProviderInfo = { * Icon for the auth provider. */ icon: IconComponent; + + /** + * Optional user friendly messaage to display for the auth provider. + */ + message?: string; }; /** diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 2876e57911..7fc50c826c 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,64 @@ # @backstage/create-app +## 0.7.1-next.2 + +### Patch Changes + +- c4a0830: Removed deprecated 'allowedHosts' field from GitHub Publish action input in default template files to fix validation errors with strict Zod validation. + +## 0.7.1-next.1 + +### Patch Changes + +- Bumped create-app version. + +## 0.7.1-next.0 + +### Patch Changes + +- 2339363: **BREAKING:** The experimental `catalog.useUrlReadersSearch` configuration flag (introduced in v1.36) has been removed. + + The `UrlReaderProcessor` now always uses the `search` method of `UrlReaders`. Built-in `UrlReaderService` implementations have been updated accordingly. + If you use custom `UrlReaderService` implementations, you need to adapt their `search` method to correctly handle both specific URLs and potential + search patterns (see changes on built-in readers [in the original PR](https://github.com/backstage/backstage/pull/28379/files#diff-68b0452f173ee54bdd40f7b5e047a9cb8bb59200425622c212c217b76dac1d1b)). + + Previous behavior was to call the `search` method only if the parsed Git URL's filename contained a wildcard and use `readUrl` otherwise. Each `UrlReaderService` must implement this logic in the `search` method instead. + + This allows each `UrlReaderService` implementation to check whether it's a search URL (that contains a wildcard pattern) or not using logic that is specific to each provider. + +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## 0.7.0 + +### Minor Changes + +- 30474c4: Add .cache directory to shipped gitignore. + +### Patch Changes + +- d57a6f7: Bumped create-app version. +- f628f44: Bumped create-app version. +- 9c9faf2: Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## 0.7.0-next.3 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + +## 0.7.0-next.2 + +### Patch Changes + +- Bumped create-app version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + ## 0.7.0-next.1 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 735d08cc64..d56bcecc54 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/create-app", - "version": "0.7.0-next.1", + "version": "0.7.1-next.2", "description": "A CLI that helps you create your own Backstage app", "backstage": { "role": "cli" diff --git a/packages/create-app/templates/default-app/app-config.yaml.hbs b/packages/create-app/templates/default-app/app-config.yaml.hbs index fc6918acea..ca52ec530c 100644 --- a/packages/create-app/templates/default-app/app-config.yaml.hbs +++ b/packages/create-app/templates/default-app/app-config.yaml.hbs @@ -103,9 +103,6 @@ catalog: # target: https://github.com/backstage/backstage/blob/master/packages/catalog-model/examples/acme-corp.yaml # rules: # - allow: [User, Group] - # Experimental: Always use the search method in UrlReaderProcessor. - # New adopters are encouraged to enable it as this behavior will be the default in a future release. - useUrlReadersSearch: true kubernetes: # see https://backstage.io/docs/features/kubernetes/configuration for kubernetes configuration options diff --git a/packages/create-app/templates/default-app/examples/template/template.yaml b/packages/create-app/templates/default-app/examples/template/template.yaml index cfba98e878..f04c3241c5 100644 --- a/packages/create-app/templates/default-app/examples/template/template.yaml +++ b/packages/create-app/templates/default-app/examples/template/template.yaml @@ -54,7 +54,6 @@ spec: name: Publish action: publish:github input: - allowedHosts: ['github.com'] description: This is ${{ parameters.name }} repoUrl: ${{ parameters.repoUrl }} defaultBranch: 'main' diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 572ba74a94..54faacd73a 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,72 @@ # @backstage/dev-utils +## 1.1.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/app-defaults@1.6.4-next.2 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + +## 1.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/app-defaults@1.6.4-next.1 + +## 1.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.2.9-next.0 + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/app-defaults@1.6.4-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.1 + - @backstage/core-plugin-api@1.10.8 + +## 1.1.11 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/app-defaults@1.6.3 + - @backstage/integration-react@1.2.8 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.1 + - @backstage/theme@0.6.6 + +## 1.1.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/app-defaults@1.6.3-next.0 + - @backstage/integration-react@1.2.7 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + ## 1.1.11-next.1 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index 545f249f06..afc430f14b 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/dev-utils", - "version": "1.1.11-next.1", + "version": "1.1.12-next.2", "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 4b4da9d410..780eb5857f 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,41 @@ # e2e-test +## 0.2.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.1-next.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.2.29 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.0 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.2.29-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.0-next.3 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.2.29-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.7.0-next.2 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + ## 0.2.29-next.1 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index 5f890a8b5e..50640d5522 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,6 +1,6 @@ { "name": "e2e-test", - "version": "0.2.29-next.1", + "version": "0.2.30-next.0", "description": "E2E test for verifying Backstage packages", "backstage": { "role": "cli" diff --git a/packages/eslint-plugin/CHANGELOG.md b/packages/eslint-plugin/CHANGELOG.md index 12aed8093f..e12e91f97a 100644 --- a/packages/eslint-plugin/CHANGELOG.md +++ b/packages/eslint-plugin/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/eslint-plugin +## 0.1.11 + +### Patch Changes + +- 098ef95: Fix custom rules package scanning performance. +- 063b2d3: Added new eslint rule to restrict mixed plugin imports. + + New rule `@backstage/no-mixed-plugin-imports` disallows mixed imports between plugins that are mixing + the backstage architecture. This rule forces that: + + - No imports from frontend plugins to backend plugins or other frontend plugins. + - No imports from backend plugins to frontend plugins or other backend plugins. + - No imports from common plugins to frontend or backend plugins. + + The current recommended configuration is giving a warning for mixed imports. This is to be changed in + the future to an error so please adjust your workspace accordingly. + +## 0.1.11-next.0 + +### Patch Changes + +- 098ef95: Fix custom rules package scanning performance. + ## 0.1.10 ### Patch Changes diff --git a/packages/eslint-plugin/README.md b/packages/eslint-plugin/README.md index 600b3268e1..e30b9e5de3 100644 --- a/packages/eslint-plugin/README.md +++ b/packages/eslint-plugin/README.md @@ -41,3 +41,4 @@ The following rules are provided by this plugin: | [@backstage/no-relative-monorepo-imports](./docs/rules/no-relative-monorepo-imports.md) | Forbid relative imports that reach outside of the package in a monorepo. | | [@backstage/no-undeclared-imports](./docs/rules/no-undeclared-imports.md) | Forbid imports of external packages that have not been declared in the appropriate dependencies field in `package.json`. | | [@backstage/no-top-level-material-ui-4-imports](./docs/rules/no-top-level-material-ui-4-imports.md) | Forbid top level import from Material UI v4 packages. | +| [@backstage/no-mixed-plugin-imports](./docs/rules/no-mixed-plugin-imports.md) | Disallow mixed plugin imports. | diff --git a/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md new file mode 100644 index 0000000000..e8570054e4 --- /dev/null +++ b/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md @@ -0,0 +1,67 @@ +# @backstage/no-mixed-plugin-imports + +Disallow mixed imports between backstage plugins. + +## Usage + +Add the rules as follows, it has no options: + +```js +"@backstage/no-mixed-plugin-imports": ["error"] +``` + +## Rule Details + +Given the following two target packages: + +```json +{ + "name": "@backstage/plugin-foo", + "backstage": { + "role": "frontend-plugin" + } +} +``` + +```json +{ + "name": "@backstage/plugin-bar", + "backstage": { + "role": "frontend-plugin" + } +} +``` + +### Fail + +```ts +import { FooCard } from '@backstage/plugin-foo'; +``` + +### Pass + +```ts +import { FooCard } from '@backstage/plugin-foo-react'; +``` + +## Options + +You can ignore specific target packages or files by adding them to the options in the `.eslintrc.js` file: + +```js +{ + rules: { + '@backstage/no-mixed-plugin-imports': [ + 'error', + { + excludedTargetPackages: [ + '@backstage/plugin-foo', + ], + excludedFiles: [ + '**/*.{test,spec}.[jt]s?(x)' + ], + } + ] + } +} +``` diff --git a/packages/eslint-plugin/index.js b/packages/eslint-plugin/index.js index af1a8891c3..58b597a438 100644 --- a/packages/eslint-plugin/index.js +++ b/packages/eslint-plugin/index.js @@ -22,6 +22,7 @@ module.exports = { '@backstage/no-forbidden-package-imports': 'error', '@backstage/no-relative-monorepo-imports': 'error', '@backstage/no-undeclared-imports': 'error', + '@backstage/no-mixed-plugin-imports': 'warn', }, }, }, @@ -30,5 +31,6 @@ module.exports = { 'no-relative-monorepo-imports': require('./rules/no-relative-monorepo-imports'), 'no-undeclared-imports': require('./rules/no-undeclared-imports'), 'no-top-level-material-ui-4-imports': require('./rules/no-top-level-material-ui-4-imports'), + 'no-mixed-plugin-imports': require('./rules/no-mixed-plugin-imports'), }, }; diff --git a/packages/eslint-plugin/lib/getPackages.js b/packages/eslint-plugin/lib/getPackages.js index 3b46f41a54..e90536ff52 100644 --- a/packages/eslint-plugin/lib/getPackages.js +++ b/packages/eslint-plugin/lib/getPackages.js @@ -21,7 +21,7 @@ const manypkg = require('@manypkg/get-packages'); /** * @typedef ExtendedPackage - * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean } }}} packageJson + * @type {import('@manypkg/get-packages').Package & { packageJson: { exports?: Record, files?: Array, backstage?: { inline?: boolean, role?: string } }}} packageJson */ /** diff --git a/packages/eslint-plugin/package.json b/packages/eslint-plugin/package.json index 3a67ce8cee..8a44cfd1e8 100644 --- a/packages/eslint-plugin/package.json +++ b/packages/eslint-plugin/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/eslint-plugin", - "version": "0.1.10", + "version": "0.1.11", "description": "Backstage ESLint plugin", "publishConfig": { "access": "public" diff --git a/packages/eslint-plugin/rules/no-mixed-plugin-imports.js b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js new file mode 100644 index 0000000000..b5e79b509e --- /dev/null +++ b/packages/eslint-plugin/rules/no-mixed-plugin-imports.js @@ -0,0 +1,249 @@ +/* + * Copyright 2023 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. + */ + +// @ts-check + +const visitImports = require('../lib/visitImports'); +const getPackages = require('../lib/getPackages'); +const minimatch = require('minimatch'); + +/** @typedef {import('../lib/getPackages.js').ExtendedPackage} ExtendedPackage */ + +/** + * @param {string} pattern + * @param {string} filePath + * @returns {boolean} + */ +const matchesPattern = (pattern, filePath) => { + return new minimatch.Minimatch(pattern).match(filePath); +}; + +const roleRules = [ + { + sourceRole: ['frontend-plugin', 'web-library'], + targetRole: [ + 'backend-plugin', + 'node-library', + 'backend-plugin-module', + 'frontend-plugin', + ], + }, + { + sourceRole: ['backend-plugin', 'node-library', 'backend-plugin-module'], + targetRole: ['frontend-plugin', 'web-library', 'backend-plugin'], + }, + { + sourceRole: ['common-library'], + targetRole: [ + 'frontend-plugin', + 'web-library', + 'backend-plugin', + 'node-library', + 'backend-plugin-module', + ], + }, +]; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + type: 'problem', + messages: { + forbidden: + '{{sourcePackage}} ({{sourceRole}}) uses forbidden import from {{targetPackage}} ({{targetRole}}).', + useReactPlugin: + 'Use web library {{targetPackage}}-react or common library instead.', + useNodePlugin: + 'Use node library {{targetPackage}}-node or common library instead.', + useCommonPlugin: 'Use common library {{targetPackage}}-common instead.', + removeImport: + 'Remove this import to avoid mixed plugin imports. Fix the code by refactoring it to use the correct plugin type.', + }, + docs: { + description: 'Disallow mixed plugin imports.', + url: 'https://github.com/backstage/backstage/blob/master/packages/eslint-plugin/docs/rules/no-mixed-plugin-imports.md', + }, + hasSuggestions: true, + schema: [ + { + type: 'object', + properties: { + excludedTargetPackages: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + excludedFiles: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + includedFiles: { + type: 'array', + items: { type: 'string' }, + uniqueItems: true, + }, + }, + additionalProperties: false, + }, + ], + }, + create(context) { + const packages = getPackages(context.cwd); + if (!packages) { + return {}; + } + + const filePath = context.physicalFilename + ? context.physicalFilename + : context.filename; + + /** @type {ExtendedPackage | undefined} */ + const pkg = packages.byPath(filePath); + if (!pkg) { + return {}; + } + + const options = context.options[0] || {}; + /** @type {string[]} */ + const ignoreTargetPackages = options.excludedTargetPackages || []; + /** @type {string[]} */ + const excludePatterns = options.excludedFiles || []; + /** @type {string[]} */ + const includePatterns = options.includedFiles || ['**/src/**']; + + if ( + !includePatterns.some(pattern => matchesPattern(pattern, filePath)) || + excludePatterns.some(pattern => matchesPattern(pattern, filePath)) + ) { + return {}; + } + + return visitImports(context, (node, imp) => { + if (imp.type !== 'internal') { + return; + } + + /** @type {ExtendedPackage | undefined} */ + const targetPackage = imp.package; + const targetName = targetPackage?.packageJson.name; + const sourceName = pkg.packageJson.name; + if (sourceName === targetName) { + return; + } + + const sourceRole = pkg.packageJson.backstage?.role; + const targetRole = targetPackage.packageJson.backstage?.role; + if (!sourceRole || !targetRole) { + return; + } + + if ( + roleRules.some( + rule => + rule.sourceRole.includes(sourceRole) && + rule.targetRole.includes(targetRole) && + !ignoreTargetPackages.includes(targetName), + ) + ) { + const suggest = []; + + if ( + (sourceRole === 'frontend-plugin' || sourceRole === 'web-library') && + targetRole === 'frontend-plugin' + ) { + suggest.push({ + messageId: 'useReactPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/'$/, "-react'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + suggest.push({ + messageId: 'useCommonPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/'$/, "-common'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + } else if ( + (sourceRole === 'backend-plugin' || + sourceRole === 'backend-plugin-module') && + targetRole === 'backend-plugin' + ) { + suggest.push({ + messageId: 'useNodePlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/-backend'$/, "-node'"); + return fixer.replaceText(imp.node, newImport); + }, + }); + suggest.push({ + messageId: 'useCommonPlugin', + data: { + targetPackage: targetName, + }, + /** @param {import('eslint').Rule.RuleFixer} fixer */ + fix(fixer) { + const source = context.sourceCode; + const nodeSource = source.getText(imp.node); + const newImport = nodeSource.replace(/-backend'$/, '-common'); + return fixer.replaceText(imp.node, newImport); + }, + }); + } else { + suggest.push({ + messageId: 'removeImport', + /** @param {import('eslint').Rule.RuleFixer} _fixer */ + fix(_fixer) { + // Not a fixable case, just give a suggestion to remove the import + }, + }); + } + + context.report({ + node: node, + messageId: 'forbidden', + data: { + sourcePackage: pkg.packageJson.name || imp.package.dir, + sourceRole, + targetPackage: targetPackage.packageJson.name || imp.package.dir, + targetRole, + }, + suggest, + }); + } + }); + }, +}; diff --git a/packages/eslint-plugin/rules/no-undeclared-imports.js b/packages/eslint-plugin/rules/no-undeclared-imports.js index b770ded365..17331d2fce 100644 --- a/packages/eslint-plugin/rules/no-undeclared-imports.js +++ b/packages/eslint-plugin/rules/no-undeclared-imports.js @@ -319,6 +319,7 @@ module.exports = { if (importsToAdd.length > 0) { addMissingImports(importsToAdd, packages, localPkg); + packages.clearCache(); // This switches all import directives back to the original import. for (const added of importsToAdd) { context.report({ @@ -336,6 +337,7 @@ module.exports = { removeInlineImports(importsToInline, localPkg); addForwardedInlineImports(importsToInline, localPkg); + packages.clearCache(); for (const inlined of importsToInline) { context.report({ node: inlined.node, @@ -350,8 +352,6 @@ module.exports = { } importsToInline.length = 0; } - - packages.clearCache(); }, ...visitImports(context, (node, imp) => { // We leave checking of type imports to the repo-tools check, diff --git a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json index 563ec35120..e8e4d4ba70 100644 --- a/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json +++ b/packages/eslint-plugin/src/__fixtures__/monorepo/packages/foo/package.json @@ -1,5 +1,12 @@ { "name": "@internal/foo", + "backstage": { + "role": "frontend-plugin" + }, + "files": [ + "dist", + "type-utils" + ], "dependencies": { "@internal/bar": "1.0.0" }, @@ -8,9 +15,5 @@ }, "peerDependencies": { "react": "*" - }, - "files": [ - "dist", - "type-utils" - ] + } } diff --git a/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts b/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts new file mode 100644 index 0000000000..45764ca71a --- /dev/null +++ b/packages/eslint-plugin/src/no-mixed-plugin-imports.test.ts @@ -0,0 +1,68 @@ +/* + * Copyright 2023 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 { RuleTester } from 'eslint'; +import path from 'path'; +import rule from '../rules/no-mixed-plugin-imports'; + +const RULE = 'no-mixed-plugin-imports'; +const FIXTURE = path.resolve(__dirname, '__fixtures__/monorepo'); + +const ERR = ( + sourcePackage: string, + sourceRole: string, + targetPackage: string, + targetRole: string, +) => ({ + message: `${sourcePackage} (${sourceRole}) uses forbidden import from ${targetPackage} (${targetRole}).`, +}); + +// cwd must be restored +const origDir = process.cwd(); +afterAll(() => { + process.chdir(origDir); +}); +process.chdir(FIXTURE); + +const ruleTester = new RuleTester({ + parserOptions: { + sourceType: 'module', + ecmaVersion: 2021, + }, +}); + +ruleTester.run(RULE, rule, { + valid: [ + { + code: `import '@internal/inline'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + }, + ], + invalid: [ + { + code: `import '@internal/foo'`, + filename: path.join(FIXTURE, 'packages/bar/src/index.ts'), + errors: [ + ERR( + '@internal/bar', + 'frontend-plugin', + '@internal/foo', + 'frontend-plugin', + ), + ], + }, + ], +}); diff --git a/packages/eslint-plugin/src/no-undeclared-imports.test.ts b/packages/eslint-plugin/src/no-undeclared-imports.test.ts index ee56c34926..7127d2f344 100644 --- a/packages/eslint-plugin/src/no-undeclared-imports.test.ts +++ b/packages/eslint-plugin/src/no-undeclared-imports.test.ts @@ -24,6 +24,7 @@ jest.mock('child_process', () => ({ const RULE = 'no-undeclared-imports'; const FIXTURE = joinPath(__dirname, '__fixtures__/monorepo'); +// const ERR_UNDECLARED = ( name: string, @@ -246,26 +247,14 @@ ruleTester.run(RULE, rule, { }, { code: `import 'react-dom'`, - output: `import 'directive:add-import:dependencies:react-dom'`, + output: `import 'directive:add-import:peerDependencies:react-dom'`, filename: joinPath(FIXTURE, 'packages/foo/src/index.ts'), errors: [ ERR_UNDECLARED( 'react-dom', - 'dependencies', + 'peerDependencies', joinPath('packages', 'foo'), - ), - ], - }, - { - code: `import 'react-dom'`, - output: `import 'directive:add-import:devDependencies:react-dom'`, - filename: joinPath(FIXTURE, 'packages/foo/src/index.test.ts'), - errors: [ - ERR_UNDECLARED( - 'react-dom', - 'devDependencies', - joinPath('packages', 'foo'), - '--dev', + '--peer', ), ], }, diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index c7e7c4cd46..3dc5512d58 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,69 @@ # @backstage/frontend-app-api +## 0.11.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.11.4-next.1 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-defaults@0.2.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.11.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.2.4-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.11.3 + +### Patch Changes + +- 0169b23: Internal tweak to avoid circular dependencies +- c38c9e8: Implemented support for the `plugin.info()` method in specialized apps with a default resolved for `package.json` and `catalog-info.yaml`. The default resolution logic can be overridden via the `pluginInfoResolver` option to `createSpecializedApp`, and plugin-specific overrides can be applied via the new `app.pluginOverrides` key in static configuration. +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/frontend-defaults@0.2.3 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.11.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/frontend-defaults@0.2.3-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.11.3-next.0 ### Patch Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index cc5a7fd807..0f808965bf 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.3-next.0", + "version": "0.11.4-next.2", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-app-api/src/routing/collectRouteIds.ts b/packages/frontend-app-api/src/routing/collectRouteIds.ts index 8b23e9e80b..6b0527f670 100644 --- a/packages/frontend-app-api/src/routing/collectRouteIds.ts +++ b/packages/frontend-app-api/src/routing/collectRouteIds.ts @@ -29,7 +29,7 @@ import { toInternalExternalRouteRef } from '../../../frontend-plugin-api/src/rou // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalSubRouteRef } from '../../../frontend-plugin-api/src/routing/SubRouteRef'; import { OpaqueFrontendPlugin } from '@internal/frontend'; -import { FrontendFeature } from '../wiring'; +import { FrontendFeature } from '../wiring/types'; /** @internal */ export interface RouteRefsById { diff --git a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts index 399e6ccb7b..5e27d571b5 100644 --- a/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts +++ b/packages/frontend-app-api/src/tree/resolveAppNodeSpecs.ts @@ -25,7 +25,7 @@ import { } from '../../../frontend-plugin-api/src/wiring/createFrontendModule'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExtension } from '../../../frontend-plugin-api/src/wiring/resolveExtensionDefinition'; -import { FrontendFeature } from '../wiring'; +import { FrontendFeature } from '../wiring/types'; /** @internal */ export function resolveAppNodeSpecs(options: { diff --git a/packages/frontend-defaults/CHANGELOG.md b/packages/frontend-defaults/CHANGELOG.md index a255a1bff7..3dc6cf3b68 100644 --- a/packages/frontend-defaults/CHANGELOG.md +++ b/packages/frontend-defaults/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/frontend-defaults +## 0.2.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/frontend-app-api@0.11.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-app@0.1.11-next.1 + +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-app@0.1.11-next.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.11.4-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.2.3 + +### Patch Changes + +- fa5650c: Forwarded the new `pluginInfoResolver` option for `createApp`. +- Updated dependencies + - @backstage/plugin-app@0.1.10 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/frontend-app-api@0.11.3 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/plugin-app@0.1.10-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/frontend-app-api@0.11.3-next.1 + ## 0.2.3-next.0 ### Patch Changes diff --git a/packages/frontend-defaults/package.json b/packages/frontend-defaults/package.json index a30accf222..f2dcd0a975 100644 --- a/packages/frontend-defaults/package.json +++ b/packages/frontend-defaults/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-defaults", - "version": "0.2.3-next.0", + "version": "0.2.4-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-dynamic-feature-loader/CHANGELOG.md b/packages/frontend-dynamic-feature-loader/CHANGELOG.md index a93a11364c..77c70c6405 100644 --- a/packages/frontend-dynamic-feature-loader/CHANGELOG.md +++ b/packages/frontend-dynamic-feature-loader/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/frontend-dynamic-feature-loader +## 0.1.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/config@1.3.2 + +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/config@1.3.2 + ## 0.1.2-next.0 ### Patch Changes diff --git a/packages/frontend-dynamic-feature-loader/package.json b/packages/frontend-dynamic-feature-loader/package.json index 7c9a7c45bf..e060a08791 100644 --- a/packages/frontend-dynamic-feature-loader/package.json +++ b/packages/frontend-dynamic-feature-loader/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-dynamic-feature-loader", - "version": "0.1.2-next.0", + "version": "0.1.3-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-internal/CHANGELOG.md b/packages/frontend-internal/CHANGELOG.md index d3c097ed8b..f6530f2471 100644 --- a/packages/frontend-internal/CHANGELOG.md +++ b/packages/frontend-internal/CHANGELOG.md @@ -1,5 +1,32 @@ # @internal/frontend +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.0.10 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.0.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.0.10-next.0 ### Patch Changes diff --git a/packages/frontend-internal/package.json b/packages/frontend-internal/package.json index 931cbc8eb3..c368de16b9 100644 --- a/packages/frontend-internal/package.json +++ b/packages/frontend-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/frontend", - "version": "0.0.10-next.0", + "version": "0.0.11-next.0", "backstage": { "role": "web-library", "inline": true diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 4bad433789..fce93ecd57 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,71 @@ # @backstage/frontend-plugin-api +## 0.10.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.10.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.10.3 + +### Patch Changes + +- 0169b23: Internal tweak to avoid circular dependencies +- 9e3868f: Added a new optional `info` option to `createFrontendPlugin` that lets you provide a loaders for different sources of metadata information about the plugin. + + There are two available loaders. The first one is `info.packageJson`, which can be used to point to a `package.json` file for the plugin. This is recommended for any plugin that is defined within its own package, especially all plugins that are published to a package registry. Typical usage looks like this: + + ```ts + export default createFrontendPlugin({ + pluginId: '...', + info: { + packageJson: () => import('../package.json'), + }, + }); + ``` + + The second loader is `info.manifest`, which can be used to point to an opaque plugin manifest. This **MUST ONLY** be used by plugins that are intended for use within a single organization. Plugins that are published to an open package registry should **NOT** use this loader. The loader is useful for adding additional internal metadata associated with the plugin, and it is up to the Backstage app to decide how these manifests are parsed and used. The default manifest parser in an app created with `createApp` from `@backstage/frontend-defaults` is able to parse the default `catalog-info.yaml` format and built-in fields such as `spec.owner`. + + Typical usage looks like this: + + ```ts + export default createFrontendPlugin({ + pluginId: '...', + info: { + manifest: () => import('../catalog-info.yaml'), + }, + }); + ``` + +- 6f48f71: Added a new `useAppNode` hook, which can be used to get a reference to the `AppNode` from by the closest `ExtensionBoundary`. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.10.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.10.3-next.0 ### Patch Changes diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index bdfd60c39d..5ae07f073f 100644 --- a/packages/frontend-plugin-api/package.json +++ b/packages/frontend-plugin-api/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/frontend-plugin-api", - "version": "0.10.3-next.0", + "version": "0.10.4-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-plugin-api/report.api.md b/packages/frontend-plugin-api/report.api.md index 9ac89d8331..6101f3e7c9 100644 --- a/packages/frontend-plugin-api/report.api.md +++ b/packages/frontend-plugin-api/report.api.md @@ -8,6 +8,7 @@ import { alertApiRef } from '@backstage/core-plugin-api'; import { AlertMessage } from '@backstage/core-plugin-api'; import { AnyApiFactory } from '@backstage/core-plugin-api'; import { AnyApiRef } from '@backstage/core-plugin-api'; +import { AnyRouteRefParams as AnyRouteRefParams_2 } from '@backstage/frontend-plugin-api'; import { ApiFactory } from '@backstage/core-plugin-api'; import { ApiHolder } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; @@ -392,7 +393,7 @@ export const coreExtensionData: { >; routePath: ConfigurableExtensionDataRef; routeRef: ConfigurableExtensionDataRef< - RouteRef, + RouteRef, 'core.routing.ref', {} >; diff --git a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts index cb48cb8df3..18466d8196 100644 --- a/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts +++ b/packages/frontend-plugin-api/src/wiring/coreExtensionData.ts @@ -15,7 +15,7 @@ */ import { JSX } from 'react'; -import { RouteRef } from '../routing'; +import { RouteRef } from '../routing/RouteRef'; import { createExtensionDataRef } from './createExtensionDataRef'; /** @public */ diff --git a/packages/frontend-test-utils/CHANGELOG.md b/packages/frontend-test-utils/CHANGELOG.md index 1bc3a595b6..7d8c3add84 100644 --- a/packages/frontend-test-utils/CHANGELOG.md +++ b/packages/frontend-test-utils/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/frontend-test-utils +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/frontend-app-api@0.11.4-next.1 + - @backstage/test-utils@1.7.10-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-app@0.1.11-next.1 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-app@0.1.11-next.0 + - @backstage/test-utils@1.7.10-next.0 + - @backstage/config@1.3.2 + - @backstage/frontend-app-api@0.11.4-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-app@0.1.10 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/frontend-app-api@0.11.3 + - @backstage/config@1.3.2 + - @backstage/test-utils@1.7.9 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + +## 0.3.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/plugin-app@0.1.10-next.1 + - @backstage/config@1.3.2 + - @backstage/frontend-app-api@0.11.3-next.1 + - @backstage/test-utils@1.7.8 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + ## 0.3.3-next.0 ### Patch Changes diff --git a/packages/frontend-test-utils/package.json b/packages/frontend-test-utils/package.json index 607260ed01..e76d798464 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.3-next.0", + "version": "0.3.4-next.1", "backstage": { "role": "web-library" }, diff --git a/packages/frontend-test-utils/src/app/renderInTestApp.test.tsx b/packages/frontend-test-utils/src/app/renderInTestApp.test.tsx index cee6273cb1..8e1b1e1ea0 100644 --- a/packages/frontend-test-utils/src/app/renderInTestApp.test.tsx +++ b/packages/frontend-test-utils/src/app/renderInTestApp.test.tsx @@ -21,6 +21,7 @@ import { TestApiProvider, } from '@backstage/frontend-test-utils'; import { analyticsApiRef, useAnalytics } from '@backstage/frontend-plugin-api'; +import { Routes, Route } from 'react-router-dom'; import { renderInTestApp } from './renderInTestApp'; describe('renderInTestApp', () => { @@ -65,4 +66,18 @@ describe('renderInTestApp', () => { ]), ); }); + + it('should support setting different locations in the history stack', async () => { + renderInTestApp( + + Index Page} /> + Second Page} /> + , + { + initialRouteEntries: ['/second-page'], + }, + ); + + expect(screen.getByText('Second Page')).toBeInTheDocument(); + }); }); diff --git a/packages/integration-aws-node/CHANGELOG.md b/packages/integration-aws-node/CHANGELOG.md index 75d79f45d3..ba52627480 100644 --- a/packages/integration-aws-node/CHANGELOG.md +++ b/packages/integration-aws-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/integration-aws-node +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + ## 0.1.16 ### Patch Changes diff --git a/packages/integration-aws-node/package.json b/packages/integration-aws-node/package.json index 695f74012a..c254d9dc06 100644 --- a/packages/integration-aws-node/package.json +++ b/packages/integration-aws-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration-aws-node", - "version": "0.1.16", + "version": "0.1.17-next.0", "description": "Helpers for fetching AWS account credentials", "backstage": { "role": "node-library" diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index 06f8807c4c..8e36db432b 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,33 @@ # @backstage/integration-react +## 1.2.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 1.2.9-next.0 + +### Patch Changes + +- 998fd15: Separated gitlab `write_repository` and `api` scope to pass checks in `RefreshingAuthSessionManager` +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.8 + +## 1.2.8 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + ## 1.2.7 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index 6d0e76aa86..5c8e490b0f 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", + "version": "1.2.9-next.1", "description": "Frontend package for managing integrations towards external systems", "backstage": { "role": "web-library" diff --git a/packages/integration-react/src/api/ScmAuth.ts b/packages/integration-react/src/api/ScmAuth.ts index 011422d4cf..823937ac28 100644 --- a/packages/integration-react/src/api/ScmAuth.ts +++ b/packages/integration-react/src/api/ScmAuth.ts @@ -173,7 +173,7 @@ export class ScmAuth implements ScmAuthApi { const host = options?.host ?? 'gitlab.com'; return new ScmAuth('gitlab', gitlabAuthApi, host, { default: ['read_user', 'read_api', 'read_repository'], - repoWrite: ['write_repository api'], + repoWrite: ['write_repository', 'api'], }); } diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 56c1f6722f..68be508017 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/integration +## 1.17.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + +## 1.17.1-next.0 + +### Patch Changes + +- e0189b8: UrlReader: Fix handling of access tokens for GitLab readURL requests +- d1e4a6d: Fixed bug where the GitLab user token and GitLab integration token were being merged together +- Updated dependencies + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 1.17.0 ### Minor Changes diff --git a/packages/integration/package.json b/packages/integration/package.json index 43d8f87e89..d7067096a1 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "1.17.0", + "version": "1.17.1-next.1", "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 d700c13429..f07249bc13 100644 --- a/packages/integration/report.api.md +++ b/packages/integration/report.api.md @@ -588,6 +588,7 @@ export function getGitilesAuthenticationUrl( export function getGitLabFileFetchUrl( url: string, config: GitLabIntegrationConfig, + token?: string, ): Promise; // @public diff --git a/packages/integration/src/gitlab/core.test.ts b/packages/integration/src/gitlab/core.test.ts index 81adc29701..d514dba755 100644 --- a/packages/integration/src/gitlab/core.test.ts +++ b/packages/integration/src/gitlab/core.test.ts @@ -188,7 +188,7 @@ describe('gitlab core', () => { }); describe('getGitLabRequestOptions', () => { - it('should return Authorization header when oauthToken is provided', () => { + it('should return Authorization bearer header when a token is provided', () => { const token = '1234567890'; const result = getGitLabRequestOptions( configSelfHosteWithRelativePath, @@ -202,29 +202,12 @@ describe('gitlab core', () => { }); }); - it('should return private-token header when gl-token is provided', () => { - const token = 'glpat-1234566'; - const result = getGitLabRequestOptions( - configSelfHosteWithRelativePath, - token, - ); + it('should return Authorization bearer header using the config token when no token is provided', () => { + const result = getGitLabRequestOptions(configSelfHosteWithRelativePath); expect(result).toEqual({ headers: { - 'PRIVATE-TOKEN': token, - }, - }); - }); - - it('should return private-token header when oauthToken is undefined', () => { - const oauthToken = undefined; - const result = getGitLabRequestOptions( - configSelfHosteWithRelativePath, - oauthToken, - ); - expect(result).toEqual({ - headers: { - 'PRIVATE-TOKEN': configSelfHosteWithRelativePath.token, + Authorization: `Bearer ${configSelfHosteWithRelativePath.token}`, }, }); }); diff --git a/packages/integration/src/gitlab/core.ts b/packages/integration/src/gitlab/core.ts index 4a17b2a15e..65360840b5 100644 --- a/packages/integration/src/gitlab/core.ts +++ b/packages/integration/src/gitlab/core.ts @@ -40,8 +40,9 @@ import { export async function getGitLabFileFetchUrl( url: string, config: GitLabIntegrationConfig, + token?: string, ): Promise { - const projectID = await getProjectId(url, config); + const projectID = await getProjectId(url, config, token); return buildProjectUrl(url, projectID, config).toString(); } @@ -56,20 +57,17 @@ export function getGitLabRequestOptions( config: GitLabIntegrationConfig, token?: string, ): { headers: Record } { - if (token) { - // If token comes from the user and starts with "gl", it's a private token (see https://docs.gitlab.com/ee/security/token_overview.html#token-prefixes) - return { - headers: token.startsWith('gl') - ? { 'PRIVATE-TOKEN': token } - : { Authorization: `Bearer ${token}` }, // Otherwise, it's a bearer token - }; + const headers: Record = {}; + + const accessToken = token || config.token; + if (accessToken) { + // OAuth, Personal, Project, and Group access tokens can all be passed via + // a bearer authorization header + // https://docs.gitlab.com/api/rest/authentication/#personalprojectgroup-access-tokens + headers.Authorization = `Bearer ${accessToken}`; } - // If token not provided, fetch the integration token - const { token: configToken = '' } = config; - return { - headers: { 'PRIVATE-TOKEN': configToken }, - }; + return { headers }; } // Converts @@ -113,6 +111,7 @@ export function buildProjectUrl( export async function getProjectId( target: string, config: GitLabIntegrationConfig, + token?: string, ): Promise { const url = new URL(target); @@ -143,12 +142,18 @@ export async function getProjectId( const response = await fetch( repoIDLookup.toString(), - getGitLabRequestOptions(config), + getGitLabRequestOptions(config, token), ); const data = await response.json(); if (!response.ok) { + if (response.status === 401) { + throw new Error( + 'GitLab Error: 401 - Unauthorized. The access token used is either expired, or does not have permission to read the project', + ); + } + throw new Error( `GitLab Error '${data.error}', ${data.error_description}`, ); diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 3ca8db071e..1bdff95cae 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,64 @@ # @backstage/repo-tools +## 0.15.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.2-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 0.15.0-next.0 + +### Minor Changes + +- 1e7082e: Clear output directory before running `package-docs` and skip all internal packages. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/cli-node@0.2.13 + - @backstage/config-loader@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/errors@1.2.7 + +## 0.14.0 + +### Minor Changes + +- bf9a173: Add support for caching the per-package output from the `package-docs` command. + +### Patch Changes + +- 4bff5d0: Fixed a bug where linting would fail with the generated clients when defining top-level `enum` schema values. +- 2d20024: Fix an issue where errors were not printed to console when running `backstage-repo-tools schema openapi generate` without the `--watch` flag. +- e643ee4: Add missing highlight language for the `package-docs` command. +- c83cd8b: Fixed some circular or otherwise unclear imports +- a372bf1: Updated dependency `@electric-sql/pglite` to `^0.3.0`. +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + +## 0.14.0-next.2 + +### Patch Changes + +- 4bff5d0: Fixed a bug where linting would fail with the generated clients when defining top-level `enum` schema values. +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/cli-node@0.2.13 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + ## 0.14.0-next.1 ### Patch Changes diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index 1d266dcb9b..10bc05ade2 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/repo-tools", - "version": "0.14.0-next.1", + "version": "0.15.0-next.1", "description": "CLI for Backstage repo tooling ", "backstage": { "role": "cli" @@ -49,7 +49,7 @@ "@backstage/cli-node": "workspace:^", "@backstage/config-loader": "workspace:^", "@backstage/errors": "workspace:^", - "@electric-sql/pglite": "^0.2.15", + "@electric-sql/pglite": "^0.3.0", "@manypkg/get-packages": "^1.1.3", "@microsoft/api-documenter": "^7.25.7", "@microsoft/api-extractor": "^7.47.2", diff --git a/packages/repo-tools/src/commands/package-docs/command.ts b/packages/repo-tools/src/commands/package-docs/command.ts index aa5edc7651..174a1d949d 100644 --- a/packages/repo-tools/src/commands/package-docs/command.ts +++ b/packages/repo-tools/src/commands/package-docs/command.ts @@ -17,11 +17,12 @@ import { exec } from 'child_process'; import { promisify } from 'util'; import { paths as cliPaths, resolvePackagePaths } from '../../lib/paths'; import { createTemporaryTsConfig } from './utils'; -import { readFile, writeFile } from 'fs/promises'; +import { readFile, rm, writeFile } from 'fs/promises'; import pLimit from 'p-limit'; import { mkdirp } from 'fs-extra'; import { PackageDocsCache } from './Cache'; import { Lockfile } from '@backstage/cli-node'; +import { glob } from 'glob'; const limit = pLimit(8); @@ -113,6 +114,24 @@ async function generateDocJson(pkg: string) { export default async function packageDocs(paths: string[] = [], opts: any) { console.warn('!!! This is an experimental command !!!'); + + const existingDocsJsonPaths = glob.sync( + cliPaths.resolveTargetRoot('dist-types/**/docs.json'), + ); + if (existingDocsJsonPaths.length > 0) { + console.warn( + `!!! Deleting all ${existingDocsJsonPaths.length} existing docs.json files !!!`, + ); + + for (const path of existingDocsJsonPaths) { + await rm(path, { force: true }); + } + } + console.warn('!!! Deleting existing docs output !!!'); + await rm(cliPaths.resolveTargetRoot('type-docs'), { + recursive: true, + force: true, + }); const selectedPackageDirs = await resolvePackagePaths({ paths, include: opts.include, @@ -128,7 +147,13 @@ export default async function packageDocs(paths: string[] = [], opts: any) { await Promise.all( selectedPackageDirs.map(pkg => limit(async () => { - if (EXCLUDE.includes(pkg)) { + const pkgJson = JSON.parse( + await readFile( + cliPaths.resolveTargetRoot(pkg, 'package.json'), + 'utf-8', + ), + ); + if (EXCLUDE.includes(pkg) || pkgJson.name.startsWith('@internal/')) { return; } if (await cache.has(pkg)) { diff --git a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts index 1a8dd36cab..c51b63cf54 100644 --- a/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts +++ b/packages/repo-tools/src/commands/package/schema/openapi/generate/server.ts @@ -60,7 +60,7 @@ async function generateSpecFile() { // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** import {createValidatedOpenApiRouterFromGeneratedEndpointMap} from '@backstage/backend-openapi-utils'; -import {EndpointMap} from './'; +import {EndpointMap} from './apis'; export const spec = ${JSON.stringify(yaml, null, 2)} as const; export const createOpenApiRouter = async ( options?: Parameters['1'], diff --git a/packages/scaffolder-internal/CHANGELOG.md b/packages/scaffolder-internal/CHANGELOG.md index d397fdc00e..409484fd42 100644 --- a/packages/scaffolder-internal/CHANGELOG.md +++ b/packages/scaffolder-internal/CHANGELOG.md @@ -1,5 +1,36 @@ # @internal/scaffolder +## 0.0.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.18.0-next.2 + +## 0.0.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.17.1-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.0.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.17.0 + - @backstage/frontend-plugin-api@0.10.3 + +## 0.0.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/plugin-scaffolder-react@1.16.1-next.2 + ## 0.0.10-next.1 ### Patch Changes diff --git a/packages/scaffolder-internal/package.json b/packages/scaffolder-internal/package.json index d9e9bb54cf..3769a100cc 100644 --- a/packages/scaffolder-internal/package.json +++ b/packages/scaffolder-internal/package.json @@ -1,6 +1,6 @@ { "name": "@internal/scaffolder", - "version": "0.0.10-next.1", + "version": "0.0.11-next.1", "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 53cabb42bb..9965024490 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,114 @@ # techdocs-cli-embedded-app +## 0.2.110-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.33.1-next.2 + - @backstage/theme@0.6.7-next.1 + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/app-defaults@1.6.4-next.2 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/test-utils@1.7.10-next.2 + - @backstage/plugin-catalog@1.31.1-next.2 + - @backstage/plugin-techdocs@1.13.2-next.1 + +## 0.2.110-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/cli@0.33.1-next.1 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/test-utils@1.7.10-next.1 + - @backstage/plugin-techdocs@1.13.2-next.1 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/plugin-catalog@1.31.1-next.1 + - @backstage/app-defaults@1.6.4-next.1 + +## 0.2.110-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs@1.13.2-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/theme@0.6.7-next.0 + - @backstage/cli@0.33.1-next.0 + - @backstage/plugin-catalog@1.31.1-next.0 + - @backstage/app-defaults@1.6.4-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/test-utils@1.7.10-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/core-plugin-api@1.10.8 + +## 0.2.109 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/cli@0.33.0 + - @backstage/plugin-techdocs@1.13.0 + - @backstage/plugin-catalog@1.31.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/app-defaults@1.6.3 + - @backstage/integration-react@1.2.8 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/test-utils@1.7.9 + - @backstage/theme@0.6.6 + +## 0.2.109-next.3 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.33.0-next.2 + - @backstage/app-defaults@1.6.3-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.0 + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/integration-react@1.2.7 + - @backstage/test-utils@1.7.8 + - @backstage/theme@0.6.6 + - @backstage/plugin-catalog@1.31.0-next.2 + - @backstage/plugin-techdocs@1.13.0-next.2 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + +## 0.2.109-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/cli@0.33.0-next.1 + - @backstage/plugin-catalog@1.31.0-next.2 + - @backstage/plugin-techdocs@1.13.0-next.2 + - @backstage/app-defaults@1.6.3-next.0 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/test-utils@1.7.8 + - @backstage/theme@0.6.6 + ## 0.2.109-next.1 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 71dc5b6bc2..260016851b 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.109-next.1", + "version": "0.2.110-next.2", "backstage": { "role": "frontend" }, diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 26179b1e80..a95cee9f08 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,48 @@ # @techdocs/cli +## 1.9.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/plugin-techdocs-node@1.13.5-next.1 + +## 1.9.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-techdocs-node@1.13.5-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + +## 1.9.4 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.4 + +## 1.9.4-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/catalog-model@1.7.4 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/plugin-techdocs-node@1.13.4-next.1 + ## 1.9.4-next.1 ### Patch Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 96ff8377ad..1582e05cb8 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,6 +1,6 @@ { "name": "@techdocs/cli", - "version": "1.9.4-next.1", + "version": "1.9.5-next.1", "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 ebbf6a99ba..a3c7ee6c7b 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/test-utils +## 1.7.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 1.7.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + +## 1.7.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-react@0.4.35 + +## 1.7.9 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-react@0.4.35 + ## 1.7.8 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index c35b80ae46..3807ef8c18 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", + "version": "1.7.10-next.2", "description": "Utilities to test Backstage plugins and apps.", "backstage": { "role": "web-library" diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index e4cbb4870b..141cb7588f 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/theme +## 0.6.7-next.1 + +### Patch Changes + +- 373486e: Add a different background colour to focused `MenuItem`s to fix a bug in MUI 4 (which is fixed in MUI 5) + +## 0.6.7-next.0 + +### Patch Changes + +- b68d269: Update uncomfortably bright color in recently visited and top visited home page cards + ## 0.6.6 ### Patch Changes diff --git a/packages/theme/package.json b/packages/theme/package.json index c483ed5034..3027a0c822 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/theme", - "version": "0.6.6", + "version": "0.6.7-next.1", "description": "material-ui theme for use with Backstage.", "backstage": { "role": "web-library" diff --git a/packages/theme/src/base/pageTheme.ts b/packages/theme/src/base/pageTheme.ts index 12a73b6089..3e1e8641fc 100644 --- a/packages/theme/src/base/pageTheme.ts +++ b/packages/theme/src/base/pageTheme.ts @@ -50,7 +50,7 @@ export const colorVariants: Record = { rubyRed: ['#98002B', '#8D1134'], toastyOrange: ['#BE2200', '#A41D00'], purpleSky: ['#8912CA', '#3E00EA'], - eveningSea: ['#00FFF2', '#035355'], + eveningSea: ['#00CFC5', '#035355'], teal: ['#005B4B'], pinkSea: ['#C8077A', '#C2297D'], greens: ['#4BB8A5', '#187656'], diff --git a/packages/theme/src/v5/defaultComponentThemes.ts b/packages/theme/src/v5/defaultComponentThemes.ts index 1d07c318a0..d627ecdcb7 100644 --- a/packages/theme/src/v5/defaultComponentThemes.ts +++ b/packages/theme/src/v5/defaultComponentThemes.ts @@ -269,4 +269,14 @@ export const defaultComponentThemes: ThemeOptions['components'] = { MuiPaper: { styleOverrides: { root: { backgroundImage: 'unset' } }, }, + MuiMenuItem: { + styleOverrides: { + root: ({ theme }) => ({ + // Fixes a bug with MUI 4 that's fixed in MUI 5 + '&:focus': { + backgroundColor: theme.palette.action.focus, + }, + }), + }, + }, }; diff --git a/packages/yarn-plugin/CHANGELOG.md b/packages/yarn-plugin/CHANGELOG.md index 9b8ab91819..3266a361ff 100644 --- a/packages/yarn-plugin/CHANGELOG.md +++ b/packages/yarn-plugin/CHANGELOG.md @@ -1,5 +1,20 @@ # yarn-plugin-backstage +## 0.0.7-next.0 + +### Patch Changes + +- d6084b8: Fixed a bug that would prevent the yarn plugin from installing new dependencies with the `backstage:^` protocol. + +## 0.0.6 + +### Patch Changes + +- a0f9e4e: added functionality so that adding or updating a backstage dependency to a package would maintain the "backstage:^" placeholder for the version. +- Updated dependencies + - @backstage/cli-common@0.1.15 + - @backstage/release-manifests@0.0.13 + ## 0.0.5 ### Patch Changes diff --git a/packages/yarn-plugin/README.md b/packages/yarn-plugin/README.md index 073669b9a5..5c112506e3 100644 --- a/packages/yarn-plugin/README.md +++ b/packages/yarn-plugin/README.md @@ -82,3 +82,12 @@ specific Backstage repository. As such, when publishing packages, all `backstage:^` versions should be removed from the package.json and replaced with the appropriate npm version ranges. This is handled by the `beforeWorkspacePacking` hook. + +### `afterWorkspaceDependencyAddition` hook + +_Replaces npm version ranges with `backstage:^` ranges for `@backstage/*` dependencies added after +the plugin has converted existing dependencies to `backstage:^` range_ + +### `afterWorkspaceDependencyReplacement` hook + +_warns user with console message when running `yarn add` for a `@backstage/*` scoped dependency that is already a dependency in the target package. Doing so will remove the `backstage:^` scope and replace it with the actual npm version range, which may not be desired._ diff --git a/packages/yarn-plugin/package.json b/packages/yarn-plugin/package.json index 978f62fc85..68320cf3c1 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", + "version": "0.0.7-next.0", "description": "Yarn plugin for working with Backstage monorepos", "backstage": { "role": "node-library" @@ -32,8 +32,9 @@ "dependencies": { "@backstage/cli-common": "workspace:^", "@backstage/release-manifests": "workspace:^", - "@yarnpkg/core": "^4.4.0", + "@yarnpkg/core": "^4.4.1", "@yarnpkg/fslib": "^3.1.2", + "@yarnpkg/plugin-essentials": "^4.4.0", "@yarnpkg/plugin-npm": "patch:@yarnpkg/plugin-npm@npm%3A3.1.0#~/.yarn/patches/@yarnpkg-plugin-npm-npm-3.1.0-6533d0f5a1.patch", "@yarnpkg/plugin-pack": "^4.0.1", "semver": "^7.6.0" diff --git a/packages/yarn-plugin/report.api.md b/packages/yarn-plugin/report.api.md index 620bbbb567..b9425282b4 100644 --- a/packages/yarn-plugin/report.api.md +++ b/packages/yarn-plugin/report.api.md @@ -4,10 +4,11 @@ ```ts import { Hooks } from '@yarnpkg/core'; -import { Hooks as Hooks_2 } from '@yarnpkg/plugin-pack'; +import { Hooks as Hooks_2 } from '@yarnpkg/plugin-essentials'; +import { Hooks as Hooks_3 } from '@yarnpkg/plugin-pack'; import { Plugin as Plugin_2 } from '@yarnpkg/core'; // @public (undocumented) -const plugin: Plugin_2; +const plugin: Plugin_2; export default plugin; ``` diff --git a/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.test.ts b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.test.ts new file mode 100644 index 0000000000..20cf482cb4 --- /dev/null +++ b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.test.ts @@ -0,0 +1,154 @@ +/* + * 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 { + Descriptor, + DescriptorHash, + httpUtils, + IdentHash, + Workspace, +} from '@yarnpkg/core'; +import { npath, ppath } from '@yarnpkg/fslib'; +import { suggestUtils } from '@yarnpkg/plugin-essentials'; +import { afterWorkspaceDependencyAddition } from './afterWorkspaceDependencyAddition'; +import { createMockDirectory } from '@backstage/backend-test-utils'; + +describe('afterWorkspaceDependencyAddition', () => { + const mockDir = createMockDirectory(); + const workspace = { + project: { + configuration: {}, + }, + } as Workspace; + const target = {} as suggestUtils.Target; + const strategies: Array = []; + + const consoleInfoSpy = jest + .spyOn(console, 'info') + .mockImplementation(() => {}); + + beforeEach(() => { + jest.resetAllMocks(); + + jest.spyOn(httpUtils, 'get').mockResolvedValue({ + releaseVersion: '1.23.45', + packages: [ + { + name: '@backstage/test-package', + version: '6.7.8', + }, + ], + }); + + jest + .spyOn(ppath, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + jest + .spyOn(process, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + mockDir.setContent({ + 'backstage.json': JSON.stringify({ + version: '1.23.45', + }), + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + }); + }); + + it('should replace the range for a backstage scoped dependency', async () => { + const input: Descriptor = { + scope: 'backstage', + name: 'test-package', + range: '^1.0.0', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + + await afterWorkspaceDependencyAddition( + workspace, + target, + input, + strategies, + ); + + expect(input.range).toBe('backstage:^'); + expect(consoleInfoSpy).toHaveBeenCalledWith( + `Setting ${input.scope}/${input.name} to backstage:^`, + ); + }); + + it('should not replace the range for a backstage scoped dependency where it cant find a version from remote', async () => { + const input: Descriptor = { + scope: 'backstage', + name: 'missing-package', + range: '^1.0.0', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + + await afterWorkspaceDependencyAddition( + workspace, + target, + input, + strategies, + ); + + expect(input.range).toBe('^1.0.0'); + }); + + it('should not replace the range for a non-backstage scoped dependency', async () => { + const input: Descriptor = { + scope: 'backstage-community', + name: 'test-package', + range: '^1.0.0', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + + await afterWorkspaceDependencyAddition( + workspace, + target, + input, + strategies, + ); + + expect(input.range).toBe('^1.0.0'); + }); + + it('should not replace the range for a dependency with backstage:^ version', async () => { + const input: Descriptor = { + scope: 'backstage', + name: 'another-test-package', + range: 'backstage:^', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + + await afterWorkspaceDependencyAddition( + workspace, + target, + input, + strategies, + ); + + expect(input.range).toBe('backstage:^'); + expect(consoleInfoSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.ts b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.ts new file mode 100644 index 0000000000..7fec01b3c7 --- /dev/null +++ b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyAddition.ts @@ -0,0 +1,48 @@ +/* + * 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 { Descriptor, structUtils, Workspace } from '@yarnpkg/core'; +import { suggestUtils } from '@yarnpkg/plugin-essentials'; +import { getPackageVersion } from '../util'; +import { PROTOCOL } from '../constants'; + +export const afterWorkspaceDependencyAddition = async ( + workspace: Workspace, + _target: suggestUtils.Target, + descriptor: Descriptor, + _strategies: Array, +) => { + const descriptorRange = structUtils.parseRange(descriptor.range); + + if ( + descriptor.scope === 'backstage' && + descriptorRange.protocol !== PROTOCOL + ) { + const originalRange = descriptor.range; + try { + descriptor.range = `${PROTOCOL}^`; + await getPackageVersion(descriptor, workspace.project.configuration); // Verify that the actual version can be resolved + console.info( + `Setting ${descriptor.scope}/${descriptor.name} to ${PROTOCOL}^`, + ); + } catch (_error: any) { + // if there's no found version then this is likely a deprecated package + // or otherwise the plugin won't be able to resolve the real version + // and we should leave the desired range as is + descriptor.range = originalRange; + } + } +}; diff --git a/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.test.ts b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.test.ts new file mode 100644 index 0000000000..137d91d680 --- /dev/null +++ b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.test.ts @@ -0,0 +1,127 @@ +/* + * 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 { + Descriptor, + DescriptorHash, + httpUtils, + IdentHash, + Workspace, +} from '@yarnpkg/core'; +import { npath, ppath } from '@yarnpkg/fslib'; +import { suggestUtils } from '@yarnpkg/plugin-essentials'; +import { afterWorkspaceDependencyReplacement } from './afterWorkspaceDependencyReplacement'; +import { createMockDirectory } from '@backstage/backend-test-utils'; + +describe('afterWorkspaceDependencyReplacement.test', () => { + const mockDir = createMockDirectory(); + const workspace = { + project: { + configuration: {}, + }, + } as Workspace; + const target = {} as suggestUtils.Target; + + const consoleWarnSpy = jest + .spyOn(console, 'warn') + .mockImplementation(() => {}); + + beforeEach(() => { + jest.resetAllMocks(); + + jest.spyOn(httpUtils, 'get').mockResolvedValue({ + releaseVersion: '1.23.45', + packages: [ + { + name: '@backstage/test-package', + version: '6.7.8', + }, + ], + }); + + jest + .spyOn(ppath, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + jest + .spyOn(process, 'cwd') + .mockReturnValue(npath.toPortablePath(mockDir.path)); + + mockDir.setContent({ + 'backstage.json': JSON.stringify({ + version: '1.23.45', + }), + 'package.json': JSON.stringify({ + workspaces: { + packages: ['packages/*'], + }, + }), + }); + }); + + it('should warn that the range is being changed for a backstage scoped dependency', async () => { + const fromDescriptor: Descriptor = { + scope: 'backstage', + name: 'test-package', + range: 'backstage:^', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + const toDescriptor: Descriptor = { + scope: 'backstage', + name: 'test-package', + range: '^1.0.0', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + + await afterWorkspaceDependencyReplacement( + workspace, + target, + fromDescriptor, + toDescriptor, + ); + + expect(consoleWarnSpy).toHaveBeenCalledWith( + 'test-package should be set to "backstage:^" instead of "^1.0.0". Make sure this change is intentional and not a mistake.', + ); + }); + + it('should ignore that the range is being changed for a non-backstage scoped dependency', async () => { + const fromDescriptor: Descriptor = { + scope: 'backstage-community', + name: 'test-package', + range: 'backstage:^', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + const toDescriptor: Descriptor = { + scope: 'backstage-community', + name: 'test-package', + range: '^1.0.0', + descriptorHash: {} as DescriptorHash, + identHash: {} as IdentHash, + }; + await afterWorkspaceDependencyReplacement( + workspace, + target, + fromDescriptor, + toDescriptor, + ); + + expect(consoleWarnSpy).not.toHaveBeenCalled(); + expect(toDescriptor.range).toBe('^1.0.0'); + }); +}); diff --git a/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.ts b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.ts new file mode 100644 index 0000000000..382a19c48b --- /dev/null +++ b/packages/yarn-plugin/src/handlers/afterWorkspaceDependencyReplacement.ts @@ -0,0 +1,37 @@ +/* + * 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 { Descriptor, structUtils, Workspace } from '@yarnpkg/core'; +import { suggestUtils } from '@yarnpkg/plugin-essentials'; +import { PROTOCOL } from '../constants'; + +export const afterWorkspaceDependencyReplacement = async ( + _workspace: Workspace, + _target: suggestUtils.Target, + _fromDescriptor: Descriptor, + toDescriptor: Descriptor, +) => { + const toDescriptorRange = structUtils.parseRange(toDescriptor.range); + + if ( + toDescriptor.scope === 'backstage' && + toDescriptorRange.protocol !== PROTOCOL + ) { + console.warn( + `${toDescriptor.name} should be set to "${PROTOCOL}^" instead of "${toDescriptor.range}". Make sure this change is intentional and not a mistake.`, + ); + } +}; diff --git a/packages/yarn-plugin/src/handlers/index.ts b/packages/yarn-plugin/src/handlers/index.ts index baa144b36f..fe8f05c935 100644 --- a/packages/yarn-plugin/src/handlers/index.ts +++ b/packages/yarn-plugin/src/handlers/index.ts @@ -16,3 +16,5 @@ export { beforeWorkspacePacking } from './beforeWorkspacePacking'; export { reduceDependency } from './reduceDependency'; +export { afterWorkspaceDependencyAddition } from './afterWorkspaceDependencyAddition'; +export { afterWorkspaceDependencyReplacement } from './afterWorkspaceDependencyReplacement'; diff --git a/packages/yarn-plugin/src/index.ts b/packages/yarn-plugin/src/index.ts index ad18deb0ff..b08d98eae7 100644 --- a/packages/yarn-plugin/src/index.ts +++ b/packages/yarn-plugin/src/index.ts @@ -23,7 +23,13 @@ import { Plugin, Hooks, semverUtils, YarnVersion } from '@yarnpkg/core'; import { Hooks as PackHooks } from '@yarnpkg/plugin-pack'; -import { beforeWorkspacePacking, reduceDependency } from './handlers'; +import { Hooks as EssentialHooks } from '@yarnpkg/plugin-essentials'; +import { + afterWorkspaceDependencyAddition, + afterWorkspaceDependencyReplacement, + beforeWorkspacePacking, + reduceDependency, +} from './handlers'; import { BackstageNpmResolver } from './resolvers'; // All dependencies of the yarn plugin are bundled during the build. Chalk @@ -44,8 +50,10 @@ if (!semverUtils.satisfiesWithPrereleases(YarnVersion, '^4.1.1')) { /** * @public */ -const plugin: Plugin = { +const plugin: Plugin = { hooks: { + afterWorkspaceDependencyAddition, + afterWorkspaceDependencyReplacement, reduceDependency, beforeWorkspacePacking, }, diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 960b46abf2..6abb39d3ec 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-api-docs +## 0.12.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-catalog@1.31.1-next.1 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.12.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.31.1-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + +## 0.12.8 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/plugin-catalog@1.31.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + +## 0.12.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog@1.31.0-next.2 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.34 + ## 0.12.8-next.1 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index ade2c0530b..b0b07eb252 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.8-next.1", + "version": "0.12.9-next.1", "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 42beb0377f..94143e903c 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/plugin-app-backend +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.2-next.0 + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-app-node@0.1.35-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.5.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-app-node@0.1.34 + ## 0.5.3-next.1 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 6a2a2ea09b..549c46119b 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.3-next.1", + "version": "0.5.4-next.0", "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 4052664edc..519aa81d90 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-app-node +## 0.1.35-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 0.1.34 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config-loader@1.10.1 + ## 0.1.34-next.1 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 83d0009c1b..542787b511 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.34-next.1", + "version": "0.1.35-next.0", "description": "Node.js library for the app plugin", "backstage": { "role": "node-library", diff --git a/plugins/app-visualizer/CHANGELOG.md b/plugins/app-visualizer/CHANGELOG.md index e5ca9aab39..596303664f 100644 --- a/plugins/app-visualizer/CHANGELOG.md +++ b/plugins/app-visualizer/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-app-visualizer +## 0.1.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.1.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.1.20 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + +## 0.1.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-plugin-api@1.10.7 + ## 0.1.20-next.0 ### Patch Changes diff --git a/plugins/app-visualizer/package.json b/plugins/app-visualizer/package.json index 7f3d005c0e..5457e0d03a 100644 --- a/plugins/app-visualizer/package.json +++ b/plugins/app-visualizer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app-visualizer", - "version": "0.1.20-next.0", + "version": "0.1.21-next.1", "description": "Visualizes the Backstage app structure", "backstage": { "role": "frontend-plugin", diff --git a/plugins/app/CHANGELOG.md b/plugins/app/CHANGELOG.md index 7ef336e214..e9b90698d2 100644 --- a/plugins/app/CHANGELOG.md +++ b/plugins/app/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-app +## 0.1.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.2.9-next.0 + - @backstage/theme@0.6.7-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.35 + +## 0.1.10 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/integration-react@1.2.8 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.35 + +## 0.1.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-react@0.4.34 + ## 0.1.10-next.0 ### Patch Changes diff --git a/plugins/app/package.json b/plugins/app/package.json index 717a4cc2c4..c130e0b046 100644 --- a/plugins/app/package.json +++ b/plugins/app/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-app", - "version": "0.1.10-next.0", + "version": "0.1.11-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "app", diff --git a/plugins/app/src/extensions/AppRoutes.tsx b/plugins/app/src/extensions/AppRoutes.tsx index 304b0b0289..1f15ced3ea 100644 --- a/plugins/app/src/extensions/AppRoutes.tsx +++ b/plugins/app/src/extensions/AppRoutes.tsx @@ -42,7 +42,9 @@ export const AppRoutes = createExtension({ const element = useRoutes([ ...inputs.routes.map(route => ({ - path: `${route.get(coreExtensionData.routePath)}/*`, + path: `${route + .get(coreExtensionData.routePath) + .replace(/\/$/, '')}/*`, element: route.get(coreExtensionData.reactElement), })), { diff --git a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md index 3ff30201af..bdfcf86f9c 100644 --- a/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-atlassian-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-atlassian-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-atlassian-provider/package.json b/plugins/auth-backend-module-atlassian-provider/package.json index c146a3a551..eb5586165c 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.4-next.1", + "version": "0.4.5-next.0", "description": "The atlassian-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md index 22cec4e289..76837455a4 100644 --- a/plugins/auth-backend-module-auth0-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-auth0-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-auth0-provider +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.2.4-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 3e18951293..4f3b0d8be2 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.4-next.1", + "version": "0.2.5-next.0", "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 a681992dc8..05723b2c82 100644 --- a/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-aws-alb-provider/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-auth-backend-module-aws-alb-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-backend@0.25.2-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- c83cd8b: Fixed some circular or otherwise unclear imports +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/plugin-auth-backend@0.25.1 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-aws-alb-provider/package.json b/plugins/auth-backend-module-aws-alb-provider/package.json index 4382931db3..aaa0cc0d3e 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.4-next.1", + "version": "0.4.5-next.0", "description": "The aws-alb provider module for the Backstage auth backend.", "backstage": { "role": "backend-plugin-module", 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 ebb3e8e24a..e809ae0fe3 100644 --- a/plugins/auth-backend-module-aws-alb-provider/report.api.md +++ b/plugins/auth-backend-module-aws-alb-provider/report.api.md @@ -6,7 +6,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { JWTHeaderParameters } from 'jose'; import { KeyObject } from 'crypto'; -import type { PassportProfile } from '@backstage/plugin-auth-node/'; +import type { PassportProfile } from '@backstage/plugin-auth-node'; import { ProxyAuthenticator } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; diff --git a/plugins/auth-backend-module-aws-alb-provider/src/types.ts b/plugins/auth-backend-module-aws-alb-provider/src/types.ts index b4c792a5f9..fd3b3dd80b 100644 --- a/plugins/auth-backend-module-aws-alb-provider/src/types.ts +++ b/plugins/auth-backend-module-aws-alb-provider/src/types.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import type { PassportProfile } from '@backstage/plugin-auth-node/'; +import type { PassportProfile } from '@backstage/plugin-auth-node'; + /** * JWT header extraction result, containing the raw value and the parsed JWT * payload. @@ -26,6 +27,7 @@ export type AwsAlbResult = { expiresInSeconds?: number; accessToken: string; }; + /** * @public */ @@ -39,6 +41,7 @@ export type AwsAlbClaims = { exp: number; iss: string; }; + /** * @internal */ diff --git a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md index f0359b1e55..7ffaf29bd9 100644 --- a/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-azure-easyauth-provider/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-backend-module-azure-easyauth-provider +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + ## 0.2.9-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 39ebf38dd6..a5abd8427f 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.9-next.1", + "version": "0.2.10-next.0", "description": "The azure-easyauth-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md index a9c90e8a05..88b2f3d8bc 100644 --- a/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-bitbucket-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-provider/package.json b/plugins/auth-backend-module-bitbucket-provider/package.json index edd1d811da..924ae21e11 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.4-next.1", + "version": "0.3.5-next.0", "description": "The bitbucket-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md index 7f2c2eac5c..6501b16920 100644 --- a/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-bitbucket-server-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-bitbucket-server-provider +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.2.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-bitbucket-server-provider/package.json b/plugins/auth-backend-module-bitbucket-server-provider/package.json index 7c2060a6df..b431c49ed6 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.4-next.1", + "version": "0.2.5-next.0", "description": "The bitbucket-server-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md index 1e206cdbaa..dc3f8de37c 100644 --- a/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-cloudflare-access-provider/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-backend-module-cloudflare-access-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-cloudflare-access-provider/package.json b/plugins/auth-backend-module-cloudflare-access-provider/package.json index fc9334f958..243314d56a 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.4-next.1", + "version": "0.4.5-next.0", "description": "The cloudflare-access-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 63b30dba1f..5f4cf36ac2 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index bbdeca08a3..3144cdf828 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.4-next.1", + "version": "0.4.5-next.0", "description": "A GCP IAP auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index ec3305ddca..7845a2e4d7 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 92352dd8aa..3d9bfc3105 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.4-next.1", + "version": "0.3.5-next.0", "description": "The github-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index 94c6a8a63c..b89ccdd273 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 8db0c5f881..6762bf8e4c 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.4-next.1", + "version": "0.3.5-next.0", "description": "The gitlab-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 451746eb0a..5606cecc9c 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index d0c77c3bde..c11cc59a0e 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.4-next.1", + "version": "0.3.5-next.0", "description": "A Google auth provider module for the Backstage auth backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-guest-provider/CHANGELOG.md b/plugins/auth-backend-module-guest-provider/CHANGELOG.md index c9117fb1e2..f180079480 100644 --- a/plugins/auth-backend-module-guest-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-guest-provider/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-backend-module-guest-provider +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + ## 0.2.9-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 92368c3364..e27ac8f472 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.9-next.1", + "version": "0.2.10-next.0", "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 3ddacc4be9..656f3a84e7 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index d3ecdcbc4d..3441fa4294 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.4-next.1", + "version": "0.3.5-next.0", "description": "The microsoft-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 67fcf5686d..11f13a6692 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index 33c221ce42..9027e1de6b 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.4-next.1", + "version": "0.4.5-next.0", "description": "The oauth2-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md index 6da4b2529d..501af74c8b 100644 --- a/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-proxy-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-oauth2-proxy-provider +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + ## 0.2.9-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 2df01732ac..ecd30d10ac 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.9-next.1", + "version": "0.2.10-next.0", "description": "The oauth2-proxy-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md index 37f529cbbc..7e31f20e7a 100644 --- a/plugins/auth-backend-module-oidc-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oidc-provider/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-auth-backend-module-oidc-provider +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-backend@0.25.2-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.4.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-backend@0.25.1 + ## 0.4.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 621726e6c4..8ee330dbdb 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.4-next.1", + "version": "0.4.5-next.0", "description": "The oidc-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-okta-provider/CHANGELOG.md b/plugins/auth-backend-module-okta-provider/CHANGELOG.md index 2c4022fc73..ecabdff63f 100644 --- a/plugins/auth-backend-module-okta-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-okta-provider/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-auth-backend-module-okta-provider +## 0.2.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.4 + +### Patch Changes + +- b25977f: Updated dependency `@davidzemon/passport-okta-oauth` to `^0.0.6`. +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.2.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-okta-provider/package.json b/plugins/auth-backend-module-okta-provider/package.json index 2780942fe8..a4b2263652 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.4-next.1", + "version": "0.2.5-next.0", "description": "The okta-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", @@ -36,7 +36,7 @@ "dependencies": { "@backstage/backend-plugin-api": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", - "@davidzemon/passport-okta-oauth": "^0.0.5", + "@davidzemon/passport-okta-oauth": "^0.0.6", "express": "^4.18.2", "passport": "^0.7.0", "zod": "^3.22.4" diff --git a/plugins/auth-backend-module-okta-provider/src/types.d.ts b/plugins/auth-backend-module-okta-provider/src/types.d.ts index 58cfb74173..7ca3645e3e 100644 --- a/plugins/auth-backend-module-okta-provider/src/types.d.ts +++ b/plugins/auth-backend-module-okta-provider/src/types.d.ts @@ -23,3 +23,87 @@ declare module 'passport-okta2' { authenticate(this: StrategyCreated, req: Request, options?: any): any; } } + +// NOTE(freben): This entire block is here because version 0.0.6 of this library +// accidentally didn't include types. It did, however, include a scope fix that +// is interesting for Backstage to get in. +// See https://github.com/backstage/backstage/pull/29529 +// See https://github.com/DavidZemon/passport-okta-oauth/pull/1 +declare module '@davidzemon/passport-okta-oauth' { + import OAuth2Strategy = require('passport-oauth2'); + + type OktaStrategyOptions = UniqueOktaStrategyOptions & + Omit< + import('passport-oauth2')._StrategyOptionsBase, + 'authorizationURL' | 'tokenURL' + >; + type UniqueOktaStrategyOptions = { + /** + * audience is the Okta Domain, e.g. `https://example.okta.com`, + * `https://example.oktapreview.com` + */ + audience: string; + /** + * authServerID is the authorization server ID. If it is defined, the token + * URL might be something like `https://example.okta.com/oauth2/authServerID/v1/token` + */ + authServerID: string | undefined; + /** + * idp is the Identity Provider (id). This is an optional field. it's a 20 character + * alphanumeric string, e.g. `qOp8aaJmCEhvep5Il6ZJ` (generated example) + */ + idp: string | undefined; + /** + * With this option enabled, `req` will be passed as the first argument to the + * verify callback. + */ + passReqToCallback: boolean | undefined; + /** + * Set this to 'code' + */ + response_type: 'code'; + }; + + class Strategy extends OAuth2Strategy { + /** + * @param {OktaStrategyOptions | undefined} options + * @param {import("passport-oauth2").VerifyFunction | import("passport-oauth2").VerifyFunctionWithRequest} verify + */ + constructor( + options: OktaStrategyOptions | undefined, + verify: + | import('passport-oauth2').VerifyFunction + | import('passport-oauth2').VerifyFunctionWithRequest, + ); + _userInfoUrl: string; + _idp: string; + _state: any; + /** + * Retrieve user profile from Okta. + * Further references at http://developer.okta.com/docs/api/resources/oidc.html#get-user-information + * + * This function constructs a normalized profile, with the following properties: + * + * - `provider` always set to `okta` + * - `id` + * - `username` + * - `displayName` + * + * @param {String} accessToken + * @param {Function} done + * @api protected + */ + userProfile(accessToken: string, done: Function): void; + /** + * Return extra Okta-specific parameters to be included in the authorization + * request. + * + * @param {Object} option + * @return {Object} + * @api protected + */ + authorizationParams(option: any): any; + } + + export { Strategy }; +} diff --git a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md index dce3cd0c6c..a03b5437cc 100644 --- a/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-onelogin-provider/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-auth-backend-module-onelogin-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + ## 0.3.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-onelogin-provider/package.json b/plugins/auth-backend-module-onelogin-provider/package.json index 58f5489bbc..7a808bf5f1 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.4-next.1", + "version": "0.3.5-next.0", "description": "The onelogin-provider backend module for the auth plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 0c31e3d28e..82bea1dbdf 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + ## 0.3.4-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 4cde024617..84c0135e85 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.4-next.1", + "version": "0.3.5-next.0", "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 e8e5ac6a05..0efd74c254 100644 --- a/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-vmware-cloud-provider/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-auth-backend-module-vmware-cloud-provider +## 0.5.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.5.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + ## 0.5.4-next.1 ### Patch Changes diff --git a/plugins/auth-backend-module-vmware-cloud-provider/package.json b/plugins/auth-backend-module-vmware-cloud-provider/package.json index 2aa4cc56bc..dc06071b41 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.4-next.1", + "version": "0.5.5-next.0", "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 0c97d879ae..0fd4e9f3f2 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-auth-backend +## 0.25.2-next.1 + +### Patch Changes + +- e88cb70: Small internal refactor to move out the `userInfo` database from the `tokenIssuer`. Also removes `exp` from being stored in `UserInfo` and it's now replaced with `created_at` and `updated_at` in the database instead. +- 207778c: Internal refactor of OIDC endpoints and `UserInfoDatabase` + +## 0.25.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.25.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.25.1-next.1 ### Patch Changes diff --git a/plugins/auth-backend/migrations/20250707164600_user_created_at.js b/plugins/auth-backend/migrations/20250707164600_user_created_at.js new file mode 100644 index 0000000000..eb78cb21b0 --- /dev/null +++ b/plugins/auth-backend/migrations/20250707164600_user_created_at.js @@ -0,0 +1,37 @@ +/* + * 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. + */ + +// @ts-check + +/** + * @param {import('knex').Knex} knex + */ +exports.up = async function up(knex) { + await knex.schema.alterTable('user_info', table => { + table.renameColumn('exp', 'updated_at'); + table.timestamp('created_at').notNullable().defaultTo(knex.fn.now()); + }); +}; + +/** + * @param {import('knex').Knex} knex + */ +exports.down = async function down(knex) { + await knex.schema.alterTable('user_info', table => { + table.dropColumn('created_at'); + table.renameColumn('updated_at', 'exp'); + }); +}; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 9ba97ee8ac..3b226485a3 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.1-next.1", + "version": "0.25.2-next.1", "description": "A Backstage backend plugin that handles authentication", "backstage": { "role": "backend-plugin", diff --git a/plugins/auth-backend/report.sql.md b/plugins/auth-backend/report.sql.md index 7eef7a33cb..b135414af6 100644 --- a/plugins/auth-backend/report.sql.md +++ b/plugins/auth-backend/report.sql.md @@ -33,11 +33,12 @@ ## Table `user_info` -| Column | Type | Nullable | Max Length | Default | -| ----------------- | -------------------------- | -------- | ---------- | ------- | -| `exp` | `timestamp with time zone` | false | - | - | -| `user_entity_ref` | `character varying` | false | 255 | - | -| `user_info` | `text` | false | - | - | +| Column | Type | Nullable | Max Length | Default | +| ----------------- | -------------------------- | -------- | ---------- | ------------------- | +| `created_at` | `timestamp with time zone` | false | - | `CURRENT_TIMESTAMP` | +| `updated_at` | `timestamp with time zone` | false | - | - | +| `user_entity_ref` | `character varying` | false | 255 | - | +| `user_info` | `text` | false | - | - | ### Indices diff --git a/plugins/auth-backend/src/authPlugin.test.ts b/plugins/auth-backend/src/authPlugin.test.ts index cd2b558c80..65d44614fe 100644 --- a/plugins/auth-backend/src/authPlugin.test.ts +++ b/plugins/auth-backend/src/authPlugin.test.ts @@ -86,6 +86,7 @@ describe('authPlugin', () => { }); const refreshRes = await request(server).post('/api/auth/guest/refresh'); + expect(refreshRes.status).toBe(200); expect(refreshRes.body).toMatchObject({ backstageIdentity: { @@ -110,7 +111,6 @@ describe('authPlugin', () => { claims: { sub: expectedIdentity.userEntityRef, ent: expectedIdentity.ownershipEntityRefs, - exp: expect.any(Number), }, }); }); @@ -160,7 +160,6 @@ describe('authPlugin', () => { claims: { sub: expectedIdentity.userEntityRef, ent: expectedIdentity.ownershipEntityRefs, - exp: expect.any(Number), }, }); }); diff --git a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts b/plugins/auth-backend/src/database/UserInfoDatabase.test.ts similarity index 83% rename from plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts rename to plugins/auth-backend/src/database/UserInfoDatabase.test.ts index 4d696aa8f2..edaeec450b 100644 --- a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.test.ts +++ b/plugins/auth-backend/src/database/UserInfoDatabase.test.ts @@ -17,7 +17,9 @@ import { resolvePackagePath } from '@backstage/backend-plugin-api'; import { TestDatabaseId, TestDatabases } from '@backstage/backend-test-utils'; import { Knex } from 'knex'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; +import { UserInfoDatabase } from './UserInfoDatabase'; +import { AuthDatabase } from './AuthDatabase'; +import { DateTime } from 'luxon'; const migrationsDir = resolvePackagePath( '@backstage/plugin-auth-backend', @@ -26,7 +28,7 @@ const migrationsDir = resolvePackagePath( jest.setTimeout(60_000); -describe('UserInfoDatabaseHandler', () => { +describe('UserInfoDatabase', () => { const databases = TestDatabases.create(); async function createDatabaseHandler(databaseId: TestDatabaseId) { @@ -38,7 +40,11 @@ describe('UserInfoDatabaseHandler', () => { return { knex, - dbHandler: new UserInfoDatabaseHandler(knex), + dbHandler: await UserInfoDatabase.create({ + database: AuthDatabase.create({ + getClient: async () => knex, + }), + }), }; } @@ -46,7 +52,7 @@ describe('UserInfoDatabaseHandler', () => { 'should support database %p', databaseId => { let knex: Knex; - let dbHandler: UserInfoDatabaseHandler; + let dbHandler: UserInfoDatabase; beforeEach(async () => { ({ knex, dbHandler } = await createDatabaseHandler(databaseId)); @@ -57,7 +63,6 @@ describe('UserInfoDatabaseHandler', () => { claims: { sub: 'user:default/foo', ent: ['group:default/foo-group', 'group:default/bar'], - exp: 1234567890, }, }; @@ -66,10 +71,12 @@ describe('UserInfoDatabaseHandler', () => { const savedUserInfo = await knex('user_info') .where('user_entity_ref', 'user:default/foo') .first(); + expect(savedUserInfo).toEqual({ user_entity_ref: 'user:default/foo', user_info: JSON.stringify(userInfo), - exp: expect.anything(), + updated_at: expect.anything(), + created_at: expect.anything(), }); userInfo.claims.ent = ['group:default/group1', 'group:default/group2']; @@ -78,10 +85,12 @@ describe('UserInfoDatabaseHandler', () => { const updatedUserInfo = await knex('user_info') .where('user_entity_ref', 'user:default/foo') .first(); + expect(updatedUserInfo).toEqual({ user_entity_ref: 'user:default/foo', user_info: JSON.stringify(userInfo), - exp: expect.anything(), + updated_at: expect.anything(), + created_at: expect.anything(), }); }); @@ -90,14 +99,13 @@ describe('UserInfoDatabaseHandler', () => { claims: { sub: 'user:default/backstage-user', ent: ['group:default/group1', 'group:default/group2'], - exp: 1234567890, }, }; await knex('user_info').insert({ user_entity_ref: 'user:default/backstage-user', user_info: JSON.stringify(userInfo), - exp: knex.fn.now(), + updated_at: DateTime.now().toSQL({ includeOffset: false }), }); const savedUserInfo = await dbHandler.getUserInfo( diff --git a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.ts b/plugins/auth-backend/src/database/UserInfoDatabase.ts similarity index 75% rename from plugins/auth-backend/src/identity/UserInfoDatabaseHandler.ts rename to plugins/auth-backend/src/database/UserInfoDatabase.ts index c0b31de8e4..9377ab89f7 100644 --- a/plugins/auth-backend/src/identity/UserInfoDatabaseHandler.ts +++ b/plugins/auth-backend/src/database/UserInfoDatabase.ts @@ -17,31 +17,30 @@ import { DateTime } from 'luxon'; import { Knex } from 'knex'; -import { BackstageTokenPayload } from './TokenFactory'; +import { AuthDatabase } from './AuthDatabase'; +import { JsonObject } from '@backstage/types'; const TABLE = 'user_info'; type Row = { user_entity_ref: string; user_info: string; - exp: string; + updated_at: string; }; type UserInfo = { - claims: Omit; + claims: JsonObject; }; -export class UserInfoDatabaseHandler { - constructor(private readonly client: Knex) {} +export class UserInfoDatabase { + private constructor(private readonly client: Knex) {} async addUserInfo(userInfo: UserInfo): Promise { await this.client(TABLE) .insert({ user_entity_ref: userInfo.claims.sub as string, user_info: JSON.stringify(userInfo), - exp: DateTime.fromSeconds(userInfo.claims.exp as number, { - zone: 'utc', - }).toSQL({ includeOffset: false }), + updated_at: DateTime.utc().toSQL({ includeOffset: false }), }) .onConflict('user_entity_ref') .merge(); @@ -59,4 +58,9 @@ export class UserInfoDatabaseHandler { const userInfo = JSON.parse(info.user_info); return userInfo; } + + static async create(options: { database: AuthDatabase }) { + const client = await options.database.get(); + return new UserInfoDatabase(client); + } } diff --git a/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts b/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts index ef6f7bd148..ad08427e85 100644 --- a/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts +++ b/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts @@ -18,8 +18,6 @@ import { createLocalJWKSet, jwtVerify } from 'jose'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { StaticKeyStore } from './StaticKeyStore'; import { mockServices } from '@backstage/backend-test-utils'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; -import { omit } from 'lodash'; const logger = mockServices.logger.mock(); const entityRef = stringifyEntityRef({ @@ -29,10 +27,6 @@ const entityRef = stringifyEntityRef({ }); describe('StaticTokenIssuer', () => { - const mockUserInfoDatabaseHandler = { - addUserInfo: jest.fn().mockResolvedValue(undefined), - } as unknown as UserInfoDatabaseHandler; - const staticKeyStore = { listKeys: () => { return Promise.resolve({ @@ -85,7 +79,6 @@ describe('StaticTokenIssuer', () => { logger, issuer: 'my-issuer', sessionExpirationSeconds: keyDurationSeconds, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }, staticKeyStore as unknown as StaticKeyStore, ); @@ -119,9 +112,6 @@ describe('StaticTokenIssuer', () => { expect(verifyResult.payload.exp).toBe( verifyResult.payload.iat! + keyDurationSeconds, ); - expect(mockUserInfoDatabaseHandler.addUserInfo).toHaveBeenCalledWith({ - claims: omit(verifyResult.payload, ['aud', 'iat', 'iss', 'uip']), - }); }); it('should issue valid tokens with omitted claims', async () => { @@ -131,7 +121,6 @@ describe('StaticTokenIssuer', () => { logger, issuer: 'my-issuer', sessionExpirationSeconds: keyDurationSeconds, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, omitClaimsFromToken: ['ent'], }, staticKeyStore as unknown as StaticKeyStore, @@ -165,11 +154,5 @@ describe('StaticTokenIssuer', () => { expect(verifyResult.payload.exp).toBe( verifyResult.payload.iat! + keyDurationSeconds, ); - expect(mockUserInfoDatabaseHandler.addUserInfo).toHaveBeenCalledWith({ - claims: { - ...omit(verifyResult.payload, ['aud', 'iat', 'iss', 'uip']), - ent: [entityRef], - }, - }); }); }); diff --git a/plugins/auth-backend/src/identity/StaticTokenIssuer.ts b/plugins/auth-backend/src/identity/StaticTokenIssuer.ts index 6875e47457..965eb5164c 100644 --- a/plugins/auth-backend/src/identity/StaticTokenIssuer.ts +++ b/plugins/auth-backend/src/identity/StaticTokenIssuer.ts @@ -22,7 +22,6 @@ import { BackstageSignInResult, TokenParams, } from '@backstage/plugin-auth-node'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; import { issueUserToken } from './issueUserToken'; export type Config = { @@ -42,7 +41,6 @@ export type Options = { * A list of claims to omit from issued tokens and only store in the user info database */ omitClaimsFromToken?: string[]; - userInfoDatabaseHandler: UserInfoDatabaseHandler; }; /** @@ -55,7 +53,6 @@ export class StaticTokenIssuer implements TokenIssuer { private readonly keyStore: StaticKeyStore; private readonly sessionExpirationSeconds: number; private readonly omitClaimsFromToken?: string[]; - private readonly userInfoDatabaseHandler: UserInfoDatabaseHandler; public constructor(options: Options, keyStore: StaticKeyStore) { this.issuer = options.issuer; @@ -63,10 +60,11 @@ export class StaticTokenIssuer implements TokenIssuer { this.sessionExpirationSeconds = options.sessionExpirationSeconds; this.keyStore = keyStore; this.omitClaimsFromToken = options.omitClaimsFromToken; - this.userInfoDatabaseHandler = options.userInfoDatabaseHandler; } - public async issueToken(params: TokenParams): Promise { + public async issueToken( + params: TokenParams & { claims: { ent: string[] } }, + ): Promise { const key = await this.getSigningKey(); return issueUserToken({ @@ -76,7 +74,6 @@ export class StaticTokenIssuer implements TokenIssuer { logger: this.logger, omitClaimsFromToken: this.omitClaimsFromToken, params, - userInfoDatabaseHandler: this.userInfoDatabaseHandler, }); } diff --git a/plugins/auth-backend/src/identity/TokenFactory.test.ts b/plugins/auth-backend/src/identity/TokenFactory.test.ts index 764b26eda4..e6cd888be6 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.test.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.test.ts @@ -21,10 +21,8 @@ import { decodeProtectedHeader, jwtVerify, } from 'jose'; -import { omit } from 'lodash'; import { MemoryKeyStore } from './MemoryKeyStore'; import { TokenFactory } from './TokenFactory'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; import { tokenTypes } from '@backstage/plugin-auth-node'; import { mockServices } from '@backstage/backend-test-utils'; @@ -45,10 +43,6 @@ const entityRef = stringifyEntityRef({ }); describe('TokenFactory', () => { - const mockUserInfoDatabaseHandler = { - addUserInfo: jest.fn().mockResolvedValue(undefined), - } as unknown as UserInfoDatabaseHandler; - it('should issue valid tokens signed by a listed key', async () => { const keyDurationSeconds = 5; const factory = new TokenFactory({ @@ -56,7 +50,6 @@ describe('TokenFactory', () => { keyStore: new MemoryKeyStore(), keyDurationSeconds, logger, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); await expect(factory.listPublicKeys()).resolves.toEqual({ keys: [] }); @@ -93,10 +86,6 @@ describe('TokenFactory', () => { verifyResult.payload.iat! + keyDurationSeconds, ); - expect(mockUserInfoDatabaseHandler.addUserInfo).toHaveBeenCalledWith({ - claims: omit(verifyResult.payload, ['aud', 'iat', 'iss', 'uip']), - }); - // Emulate the reconstruction of a limited user token const limitedUserToken = [ base64url.encode( @@ -139,14 +128,13 @@ describe('TokenFactory', () => { keyStore: new MemoryKeyStore(), keyDurationSeconds: 5, logger, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); const { token: token1 } = await factory.issueToken({ - claims: { sub: entityRef }, + claims: { sub: entityRef, ent: [entityRef] }, }); const { token: token2 } = await factory.issueToken({ - claims: { sub: entityRef }, + claims: { sub: entityRef, ent: [entityRef] }, }); expect(jwtKid(token1)).toBe(jwtKid(token2)); @@ -165,7 +153,7 @@ describe('TokenFactory', () => { }); const { token: token3 } = await factory.issueToken({ - claims: { sub: entityRef }, + claims: { sub: entityRef, ent: [entityRef] }, }); expect(jwtKid(token3)).not.toBe(jwtKid(token2)); @@ -185,12 +173,11 @@ describe('TokenFactory', () => { keyStore: new MemoryKeyStore(), keyDurationSeconds, logger, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); await expect(() => { return factory.issueToken({ - claims: { sub: 'UserId' }, + claims: { sub: 'UserId', ent: [entityRef] }, }); }).rejects.toThrow(); }); @@ -203,12 +190,11 @@ describe('TokenFactory', () => { keyDurationSeconds, logger, algorithm: '', - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); await expect(() => { return factory.issueToken({ - claims: { sub: 'UserId' }, + claims: { sub: 'UserId', ent: [entityRef] }, }); }).rejects.toThrow(); }); @@ -219,7 +205,6 @@ describe('TokenFactory', () => { keyStore: new MemoryKeyStore(), keyDurationSeconds: 5, logger, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); await expect(() => { @@ -238,7 +223,6 @@ describe('TokenFactory', () => { keyStore: new MemoryKeyStore(), keyDurationSeconds, logger, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, }); const { token } = await factory.issueToken({ diff --git a/plugins/auth-backend/src/identity/TokenFactory.ts b/plugins/auth-backend/src/identity/TokenFactory.ts index 5a8a789d17..d94825e322 100644 --- a/plugins/auth-backend/src/identity/TokenFactory.ts +++ b/plugins/auth-backend/src/identity/TokenFactory.ts @@ -25,7 +25,6 @@ import { } from '@backstage/plugin-auth-node'; import { AnyJWK, KeyStore, TokenIssuer } from './types'; import { JsonValue } from '@backstage/types'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; import { issueUserToken } from './issueUserToken'; /** @@ -92,7 +91,6 @@ type Options = { * A list of claims to omit from issued tokens and only store in the user info database */ omitClaimsFromToken?: string[]; - userInfoDatabaseHandler: UserInfoDatabaseHandler; }; /** @@ -116,7 +114,6 @@ export class TokenFactory implements TokenIssuer { private readonly keyDurationSeconds: number; private readonly algorithm: string; private readonly omitClaimsFromToken?: string[]; - private readonly userInfoDatabaseHandler: UserInfoDatabaseHandler; private keyExpiry?: Date; private privateKeyPromise?: Promise; @@ -128,10 +125,11 @@ export class TokenFactory implements TokenIssuer { this.keyDurationSeconds = options.keyDurationSeconds; this.algorithm = options.algorithm ?? 'ES256'; this.omitClaimsFromToken = options.omitClaimsFromToken; - this.userInfoDatabaseHandler = options.userInfoDatabaseHandler; } - async issueToken(params: TokenParams): Promise { + async issueToken( + params: TokenParams & { claims: { ent: string[] } }, + ): Promise { const key = await this.getKey(); return issueUserToken({ @@ -141,7 +139,6 @@ export class TokenFactory implements TokenIssuer { logger: this.logger, omitClaimsFromToken: this.omitClaimsFromToken, params, - userInfoDatabaseHandler: this.userInfoDatabaseHandler, }); } diff --git a/plugins/auth-backend/src/identity/issueUserToken.ts b/plugins/auth-backend/src/identity/issueUserToken.ts index d411439e6e..8deeb96cc3 100644 --- a/plugins/auth-backend/src/identity/issueUserToken.ts +++ b/plugins/auth-backend/src/identity/issueUserToken.ts @@ -22,7 +22,6 @@ import { tokenTypes, } from '@backstage/plugin-auth-node'; import { omit } from 'lodash'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; import { LoggerService } from '@backstage/backend-plugin-api'; import { GeneralSign, importJWK, JWK, KeyLike, SignJWT } from 'jose'; import { BackstageTokenPayload } from './TokenFactory'; @@ -37,17 +36,15 @@ export async function issueUserToken({ logger, omitClaimsFromToken, params, - userInfoDatabaseHandler, }: { issuer: string; key: JWK; keyDurationSeconds: number; logger: LoggerService; omitClaimsFromToken?: string[]; - params: TokenParams; - userInfoDatabaseHandler: UserInfoDatabaseHandler; + params: TokenParams & { claims: { ent: string[] } }; }): Promise { - const { sub, ent = [sub], ...additionalClaims } = params.claims; + const { sub, ent, ...additionalClaims } = params.claims; const aud = tokenTypes.user.audClaim; const iat = Math.floor(Date.now() / MS_IN_S); const exp = iat + keyDurationSeconds; @@ -109,12 +106,6 @@ export async function issueUserToken({ ); } - // Store the user info in the database upon successful token - // issuance so that it can be retrieved later by limited user tokens - await userInfoDatabaseHandler.addUserInfo({ - claims: omit(claims, ['aud', 'iat', 'iss', 'uip']), - }); - return { token, identity: { diff --git a/plugins/auth-backend/src/identity/router.test.ts b/plugins/auth-backend/src/identity/router.test.ts deleted file mode 100644 index 9ede44f195..0000000000 --- a/plugins/auth-backend/src/identity/router.test.ts +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2020 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, - createBackendPlugin, -} from '@backstage/backend-plugin-api'; -import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; -import Router from 'express-promise-router'; -import request from 'supertest'; -import { bindOidcRouter } from './router'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; - -describe('bindOidcRouter', () => { - it('should return user info for full tokens', async () => { - const auth = mockServices.auth.mock(); - const mockUserInfoDatabaseHandler = { - getUserInfo: jest.fn().mockResolvedValue({ - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }), - } as unknown as UserInfoDatabaseHandler; - - const { server } = await startTestBackend({ - features: [ - createBackendPlugin({ - pluginId: 'auth', - register(reg) { - reg.registerInit({ - deps: { httpRouter: coreServices.httpRouter }, - async init({ httpRouter }) { - const router = Router(); - bindOidcRouter(router, { - baseUrl: 'http://localhost:7000', - auth, - tokenIssuer: {} as any, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, - }); - httpRouter.use(router); - httpRouter.addAuthPolicy({ - path: '/', - allow: 'unauthenticated', - }); - }, - }); - }, - }), - ], - }); - - auth.authenticate.mockResolvedValueOnce({} as any); - auth.isPrincipal.mockReturnValueOnce(true); - - await request(server) - .get('/api/auth/v1/userinfo') - .set( - 'Authorization', - `Bearer h.${btoa( - JSON.stringify({ sub: 'k/ns:n', ent: ['k/ns:a', 'k/ns:b'] }), - )}.s`, - ) - .expect(200, { - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }); - - expect(mockUserInfoDatabaseHandler.getUserInfo).toHaveBeenCalledWith( - 'k/ns:n', - ); - }); - - it('should return user info for limited tokens', async () => { - const auth = mockServices.auth.mock(); - const mockUserInfoDatabaseHandler = { - getUserInfo: jest.fn().mockResolvedValue({ - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }), - } as unknown as UserInfoDatabaseHandler; - - const { server } = await startTestBackend({ - features: [ - createBackendPlugin({ - pluginId: 'auth', - register(reg) { - reg.registerInit({ - deps: { httpRouter: coreServices.httpRouter }, - async init({ httpRouter }) { - const router = Router(); - bindOidcRouter(router, { - baseUrl: 'http://localhost:7000', - auth, - tokenIssuer: {} as any, - userInfoDatabaseHandler: mockUserInfoDatabaseHandler, - }); - httpRouter.use(router); - httpRouter.addAuthPolicy({ - path: '/', - allow: 'unauthenticated', - }); - }, - }); - }, - }), - ], - }); - - auth.authenticate.mockResolvedValueOnce({} as any); - auth.isPrincipal.mockReturnValueOnce(true); - - await request(server) - .get('/api/auth/v1/userinfo') - .set( - 'Authorization', - `Bearer h.${btoa(JSON.stringify({ sub: 'k/ns:n' }))}.s`, - ) - .expect(200, { - claims: { - sub: 'k/ns:n', - ent: ['k/ns:a', 'k/ns:b'], - }, - }); - - expect(mockUserInfoDatabaseHandler.getUserInfo).toHaveBeenCalledWith( - 'k/ns:n', - ); - }); -}); diff --git a/plugins/auth-backend/src/identity/router.ts b/plugins/auth-backend/src/identity/router.ts deleted file mode 100644 index 3952ec64b5..0000000000 --- a/plugins/auth-backend/src/identity/router.ts +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright 2020 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 express from 'express'; -import Router from 'express-promise-router'; -import { TokenIssuer } from './types'; -import { AuthService } from '@backstage/backend-plugin-api'; -import { decodeJwt } from 'jose'; -import { AuthenticationError, InputError } from '@backstage/errors'; -import { UserInfoDatabaseHandler } from './UserInfoDatabaseHandler'; - -export function bindOidcRouter( - targetRouter: express.Router, - options: { - baseUrl: string; - auth: AuthService; - tokenIssuer: TokenIssuer; - userInfoDatabaseHandler: UserInfoDatabaseHandler; - }, -) { - const { baseUrl, auth, tokenIssuer, userInfoDatabaseHandler } = options; - - const router = Router(); - targetRouter.use(router); - - const config = { - issuer: baseUrl, - token_endpoint: `${baseUrl}/v1/token`, - userinfo_endpoint: `${baseUrl}/v1/userinfo`, - jwks_uri: `${baseUrl}/.well-known/jwks.json`, - response_types_supported: ['id_token'], - subject_types_supported: ['public'], - id_token_signing_alg_values_supported: [ - 'RS256', - 'RS384', - 'RS512', - 'ES256', - 'ES384', - 'ES512', - 'PS256', - 'PS384', - 'PS512', - 'EdDSA', - ], - scopes_supported: ['openid'], - token_endpoint_auth_methods_supported: [], - claims_supported: ['sub', 'ent'], - grant_types_supported: [], - }; - - router.get('/.well-known/openid-configuration', (_req, res) => { - res.json(config); - }); - - router.get('/.well-known/jwks.json', async (_req, res) => { - const { keys } = await tokenIssuer.listPublicKeys(); - res.json({ keys }); - }); - - router.get('/v1/token', (_req, res) => { - res.status(501).send('Not Implemented'); - }); - - // This endpoint doesn't use the regular HttpAuthService, since the contract - // is specifically for the header to be communicated in the Authorization - // header, regardless of token type - router.get('/v1/userinfo', async (req, res) => { - const matches = req.headers.authorization?.match(/^Bearer[ ]+(\S+)$/i); - const token = matches?.[1]; - if (!token) { - throw new AuthenticationError('No token provided'); - } - - const credentials = await auth.authenticate(token, { - allowLimitedAccess: true, - }); - if (!auth.isPrincipal(credentials, 'user')) { - throw new InputError( - 'Userinfo endpoint must be called with a token that represents a user principal', - ); - } - - const { sub: userEntityRef } = decodeJwt(token); - - if (typeof userEntityRef !== 'string') { - throw new Error('Invalid user token, user entity ref must be a string'); - } - - const userInfo = await userInfoDatabaseHandler.getUserInfo(userEntityRef); - if (!userInfo) { - res.status(404).send('User info not found'); - return; - } - - res.json(userInfo); - }); -} diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts index 4426cb7815..d0323e7651 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.test.ts @@ -19,6 +19,7 @@ import { mockServices } from '@backstage/backend-test-utils'; import { TokenIssuer } from '../../identity/types'; import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; import { NotFoundError } from '@backstage/errors'; +import { UserInfoDatabase } from '../../database/UserInfoDatabase'; describe('CatalogAuthResolverContext', () => { beforeEach(() => { @@ -28,6 +29,16 @@ describe('CatalogAuthResolverContext', () => { const catalog = catalogServiceMock(); jest.spyOn(catalog, 'getEntities'); + const mockUserInfo = { + addUserInfo: jest.fn().mockResolvedValue(undefined), + getUserInfo: jest.fn().mockResolvedValue({ + claims: { + sub: 'user:default/user', + ent: ['user:default/user'], + }, + }), + } as unknown as jest.Mocked; + it('adds kind to filter when missing', async () => { const auth = mockServices.auth(); const context = CatalogAuthResolverContext.create({ @@ -35,6 +46,7 @@ describe('CatalogAuthResolverContext', () => { catalog, tokenIssuer: {} as TokenIssuer, auth, + userInfo: mockUserInfo, }); await expect( diff --git a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts index 4f9b33d021..269c7131dc 100644 --- a/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts +++ b/plugins/auth-backend/src/lib/resolvers/CatalogAuthResolverContext.ts @@ -32,6 +32,7 @@ import { TokenParams, } from '@backstage/plugin-auth-node'; import { CatalogIdentityClient } from '../catalog/CatalogIdentityClient'; +import { UserInfoDatabase } from '../../database/UserInfoDatabase'; function getDefaultOwnershipEntityRefs(entity: Entity) { const membershipRefs = @@ -51,6 +52,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { tokenIssuer: TokenIssuer; auth: AuthService; ownershipResolver?: AuthOwnershipResolver; + userInfo: UserInfoDatabase; }): CatalogAuthResolverContext { const catalogIdentityClient = new CatalogIdentityClient({ catalog: options.catalog, @@ -63,6 +65,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { catalogIdentityClient, options.catalog, options.auth, + options.userInfo, options.ownershipResolver, ); } @@ -73,11 +76,29 @@ export class CatalogAuthResolverContext implements AuthResolverContext { public readonly catalogIdentityClient: CatalogIdentityClient, private readonly catalog: CatalogService, private readonly auth: AuthService, + private readonly userInfo: UserInfoDatabase, private readonly ownershipResolver?: AuthOwnershipResolver, ) {} async issueToken(params: TokenParams) { - return await this.tokenIssuer.issueToken(params); + const { sub, ent = [sub], ...additionalClaims } = params.claims; + const claims = { + sub, + ent, + ...additionalClaims, + }; + + const issuedToken = await this.tokenIssuer.issueToken({ + claims, + }); + + // Store the user info in the database upon successful token + // issuance so that it can be retrieved later by limited user tokens + await this.userInfo.addUserInfo({ + claims, + }); + + return issuedToken; } async findCatalogUser(query: AuthResolverCatalogUserQuery) { @@ -160,7 +181,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { entity, ); - return await this.tokenIssuer.issueToken({ + return await this.issueToken({ claims: { sub: stringifyEntityRef(entity), ent: ownershipEntityRefs, @@ -180,7 +201,7 @@ export class CatalogAuthResolverContext implements AuthResolverContext { }), ); - return await this.tokenIssuer.issueToken({ + return await this.issueToken({ claims: { sub: userEntityRef, ent: [userEntityRef], diff --git a/plugins/auth-backend/src/migrations.test.ts b/plugins/auth-backend/src/migrations.test.ts index b4a6572a86..f9575c70fd 100644 --- a/plugins/auth-backend/src/migrations.test.ts +++ b/plugins/auth-backend/src/migrations.test.ts @@ -107,4 +107,83 @@ describe('migrations', () => { await knex.destroy(); }, ); + + it.each(databases.eachSupportedId())( + '20250707164600_user_created_at.js, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await migrateUntilBefore(knex, '20250707164600_user_created_at.js'); + + if (knex.client.config.client.includes('sqlite')) { + // Sqlite doesn't support adding a column with non-constant default when table has data + // so we just test that the migration runs without errors + await migrateUpOnce(knex); + + return; + } + + const user_info = JSON.stringify({ + claims: { + ent: ['group:default/group1', 'group:default/group2'], + }, + }); + + await knex + .insert({ + user_entity_ref: 'user:default/backstage-user', + user_info, + exp: knex.fn.now(), + }) + .into('user_info'); + + const { exp } = await knex('user_info').first(); + + await migrateUpOnce(knex); + + const { created_at, updated_at } = await knex('user_info').first(); + + expect(updated_at).toEqual(exp); + expect(created_at).toBeDefined(); + + await knex + .insert({ + user_entity_ref: 'user:default/backstage-user', + user_info, + updated_at: knex.fn.now(), + }) + .into('user_info') + .onConflict(['user_entity_ref']) + .merge(); + + await knex + .insert({ + user_entity_ref: 'user:default/backstage-user-2', + user_info, + updated_at: knex.fn.now(), + }) + .into('user_info'); + + await expect( + knex('user_info').select('created_at', 'updated_at'), + ).resolves.toEqual([ + { + created_at: expect.any(Date), + updated_at: expect.any(Date), + }, + { + created_at: expect.any(Date), + updated_at: expect.any(Date), + }, + ]); + + await migrateDownOnce(knex); + + await expect(knex('user_info').select('exp')).resolves.toEqual([ + { exp: expect.any(Date) }, + { exp: expect.any(Date) }, + ]); + + await knex.destroy(); + }, + ); }); diff --git a/plugins/auth-backend/src/providers/router.ts b/plugins/auth-backend/src/providers/router.ts index d04347e7b3..8505c1f2bd 100644 --- a/plugins/auth-backend/src/providers/router.ts +++ b/plugins/auth-backend/src/providers/router.ts @@ -27,6 +27,7 @@ import Router from 'express-promise-router'; import { Minimatch } from 'minimatch'; import { CatalogAuthResolverContext } from '../lib/resolvers/CatalogAuthResolverContext'; import { TokenIssuer } from '../identity/types'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; export type ProviderFactories = { [s: string]: AuthProviderFactory }; @@ -40,6 +41,7 @@ export function bindProviderRouters( logger: LoggerService; auth: AuthService; tokenIssuer: TokenIssuer; + userInfo: UserInfoDatabase; ownershipResolver?: AuthOwnershipResolver; catalog: CatalogService; }, @@ -54,6 +56,7 @@ export function bindProviderRouters( tokenIssuer, catalog, ownershipResolver, + userInfo, } = options; const providersConfig = config.getOptionalConfig('auth.providers'); @@ -82,6 +85,7 @@ export function bindProviderRouters( tokenIssuer, auth, ownershipResolver, + userInfo, }), }); diff --git a/plugins/auth-backend/src/service/OidcRouter.test.ts b/plugins/auth-backend/src/service/OidcRouter.test.ts new file mode 100644 index 0000000000..dbc6c88f93 --- /dev/null +++ b/plugins/auth-backend/src/service/OidcRouter.test.ts @@ -0,0 +1,151 @@ +/* + * Copyright 2020 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, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import Router from 'express-promise-router'; +import request from 'supertest'; +import { OidcRouter } from './OidcRouter'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; + +describe('OidcRouter', () => { + describe('/v1/userinfo', () => { + it('should return user info for full tokens', async () => { + const auth = mockServices.auth.mock(); + const mockUserInfo = { + getUserInfo: jest.fn().mockResolvedValue({ + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + }, + }), + } as unknown as UserInfoDatabase; + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + const router = Router(); + + router.use( + OidcRouter.create({ + auth, + tokenIssuer: {} as any, + baseUrl: 'http://localhost:7000', + userInfo: mockUserInfo, + }).getRouter(), + ); + httpRouter.use(router); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + auth.authenticate.mockResolvedValueOnce({} as any); + auth.isPrincipal.mockReturnValueOnce(true); + + await request(server) + .get('/api/auth/v1/userinfo') + .set( + 'Authorization', + `Bearer h.${btoa( + JSON.stringify({ sub: 'k/ns:n', ent: ['k/ns:a', 'k/ns:b'] }), + )}.s`, + ) + .expect(200, { + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + }, + }); + + expect(mockUserInfo.getUserInfo).toHaveBeenCalledWith('k/ns:n'); + }); + + it('should return user info for limited tokens', async () => { + const auth = mockServices.auth.mock(); + const mockUserInfo = { + getUserInfo: jest.fn().mockResolvedValue({ + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + }, + }), + } as unknown as UserInfoDatabase; + + const { server } = await startTestBackend({ + features: [ + createBackendPlugin({ + pluginId: 'auth', + register(reg) { + reg.registerInit({ + deps: { httpRouter: coreServices.httpRouter }, + async init({ httpRouter }) { + const router = Router(); + + router.use( + OidcRouter.create({ + auth, + tokenIssuer: {} as any, + baseUrl: 'http://localhost:7000', + userInfo: mockUserInfo, + }).getRouter(), + ); + httpRouter.use(router); + httpRouter.addAuthPolicy({ + path: '/', + allow: 'unauthenticated', + }); + }, + }); + }, + }), + ], + }); + + auth.authenticate.mockResolvedValueOnce({} as any); + auth.isPrincipal.mockReturnValueOnce(true); + + await request(server) + .get('/api/auth/v1/userinfo') + .set( + 'Authorization', + `Bearer h.${btoa(JSON.stringify({ sub: 'k/ns:n' }))}.s`, + ) + .expect(200, { + claims: { + sub: 'k/ns:n', + ent: ['k/ns:a', 'k/ns:b'], + }, + }); + + expect(mockUserInfo.getUserInfo).toHaveBeenCalledWith('k/ns:n'); + }); + }); +}); diff --git a/plugins/auth-backend/src/service/OidcRouter.ts b/plugins/auth-backend/src/service/OidcRouter.ts new file mode 100644 index 0000000000..6ada071c44 --- /dev/null +++ b/plugins/auth-backend/src/service/OidcRouter.ts @@ -0,0 +1,73 @@ +/* + * 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 Router from 'express-promise-router'; +import { OidcService } from './OidcService'; +import { AuthenticationError } from '@backstage/errors'; +import { AuthService } from '@backstage/backend-plugin-api'; +import { TokenIssuer } from '../identity/types'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; + +export class OidcRouter { + private constructor(private readonly oidc: OidcService) {} + + static create(options: { + auth: AuthService; + tokenIssuer: TokenIssuer; + baseUrl: string; + userInfo: UserInfoDatabase; + }) { + return new OidcRouter(OidcService.create(options)); + } + + public getRouter() { + const router = Router(); + + router.get('/.well-known/openid-configuration', (_req, res) => { + res.json(this.oidc.getConfiguration()); + }); + + router.get('/.well-known/jwks.json', async (_req, res) => { + const { keys } = await this.oidc.listPublicKeys(); + res.json({ keys }); + }); + + router.get('/v1/token', (_req, res) => { + res.status(501).send('Not Implemented'); + }); + + // This endpoint doesn't use the regular HttpAuthoidc, since the contract + // is specifically for the header to be communicated in the Authorization + // header, regardless of token type + router.get('/v1/userinfo', async (req, res) => { + const matches = req.headers.authorization?.match(/^Bearer[ ]+(\S+)$/i); + const token = matches?.[1]; + if (!token) { + throw new AuthenticationError('No token provided'); + } + + const userInfo = await this.oidc.getUserInfo({ token }); + + if (!userInfo) { + res.status(404).send('User info not found'); + return; + } + + res.json(userInfo); + }); + + return router; + } +} diff --git a/plugins/auth-backend/src/service/OidcService.ts b/plugins/auth-backend/src/service/OidcService.ts new file mode 100644 index 0000000000..5024b2cc8c --- /dev/null +++ b/plugins/auth-backend/src/service/OidcService.ts @@ -0,0 +1,92 @@ +/* + * 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 { AuthService } from '@backstage/backend-plugin-api'; +import { TokenIssuer } from '../identity/types'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; +import { InputError } from '@backstage/errors'; +import { decodeJwt } from 'jose'; + +export class OidcService { + private constructor( + private readonly auth: AuthService, + private readonly tokenIssuer: TokenIssuer, + private readonly baseUrl: string, + private readonly userInfo: UserInfoDatabase, + ) {} + + static create(options: { + auth: AuthService; + tokenIssuer: TokenIssuer; + baseUrl: string; + userInfo: UserInfoDatabase; + }) { + return new OidcService( + options.auth, + options.tokenIssuer, + options.baseUrl, + options.userInfo, + ); + } + + public getConfiguration() { + return { + issuer: this.baseUrl, + token_endpoint: `${this.baseUrl}/v1/token`, + userinfo_endpoint: `${this.baseUrl}/v1/userinfo`, + jwks_uri: `${this.baseUrl}/.well-known/jwks.json`, + response_types_supported: ['id_token'], + subject_types_supported: ['public'], + id_token_signing_alg_values_supported: [ + 'RS256', + 'RS384', + 'RS512', + 'ES256', + 'ES384', + 'ES512', + 'PS256', + 'PS384', + 'PS512', + 'EdDSA', + ], + scopes_supported: ['openid'], + token_endpoint_auth_methods_supported: [], + claims_supported: ['sub', 'ent'], + grant_types_supported: [], + }; + } + + public async listPublicKeys() { + return await this.tokenIssuer.listPublicKeys(); + } + + public async getUserInfo({ token }: { token: string }) { + const credentials = await this.auth.authenticate(token, { + allowLimitedAccess: true, + }); + if (!this.auth.isPrincipal(credentials, 'user')) { + throw new InputError( + 'Userinfo endpoint must be called with a token that represents a user principal', + ); + } + + const { sub: userEntityRef } = decodeJwt(token); + + if (typeof userEntityRef !== 'string') { + throw new Error('Invalid user token, user entity ref must be a string'); + } + return await this.userInfo.getUserInfo(userEntityRef); + } +} diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index fd9373d5a3..5012c90106 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -27,10 +27,9 @@ import { import { AuthOwnershipResolver } from '@backstage/plugin-auth-node'; import { CatalogService } from '@backstage/plugin-catalog-node'; import { NotFoundError } from '@backstage/errors'; -import { bindOidcRouter } from '../identity/router'; import { KeyStores } from '../identity/KeyStores'; import { TokenFactory } from '../identity/TokenFactory'; -import { UserInfoDatabaseHandler } from '../identity/UserInfoDatabaseHandler'; +import { UserInfoDatabase } from '../database/UserInfoDatabase'; import session from 'express-session'; import connectSessionKnex from 'connect-session-knex'; import passport from 'passport'; @@ -40,6 +39,7 @@ import { TokenIssuer } from '../identity/types'; import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; import { StaticKeyStore } from '../identity/StaticKeyStore'; import { bindProviderRouters, ProviderFactories } from '../providers/router'; +import { OidcRouter } from './OidcRouter'; interface RouterOptions { logger: LoggerService; @@ -60,7 +60,7 @@ export async function createRouter( logger, config, discovery, - database, + database: db, tokenFactoryAlgorithm, providerFactories = {}, } = options; @@ -70,16 +70,16 @@ export async function createRouter( const appUrl = config.getString('app.baseUrl'); const authUrl = await discovery.getExternalBaseUrl('auth'); const backstageTokenExpiration = readBackstageTokenExpiration(config); - const authDb = AuthDatabase.create(database); + const database = AuthDatabase.create(db); const keyStore = await KeyStores.fromConfig(config, { logger, - database: authDb, + database, }); - const userInfoDatabaseHandler = new UserInfoDatabaseHandler( - await authDb.get(), - ); + const userInfo = await UserInfoDatabase.create({ + database, + }); const omitClaimsFromToken = config.getOptionalBoolean( 'auth.omitIdentityTokenOwnershipClaim', @@ -94,7 +94,6 @@ export async function createRouter( logger: logger.child({ component: 'token-factory' }), issuer: authUrl, sessionExpirationSeconds: backstageTokenExpiration, - userInfoDatabaseHandler, omitClaimsFromToken, }, keyStore as StaticKeyStore, @@ -108,7 +107,6 @@ export async function createRouter( algorithm: tokenFactoryAlgorithm ?? config.getOptionalString('auth.identityTokenAlgorithm'), - userInfoDatabaseHandler, omitClaimsFromToken, }); } @@ -126,7 +124,7 @@ export async function createRouter( cookie: { secure: enforceCookieSSL ? 'auto' : false }, store: new KnexSessionStore({ createtable: false, - knex: await authDb.get(), + knex: await database.get(), }), }), ); @@ -146,15 +144,18 @@ export async function createRouter( tokenIssuer, ...options, auth: options.auth, + userInfo, }); - bindOidcRouter(router, { + const oidcRouter = OidcRouter.create({ auth: options.auth, tokenIssuer, baseUrl: authUrl, - userInfoDatabaseHandler, + userInfo, }); + router.use(oidcRouter.getRouter()); + // Gives a more helpful error message than a plain 404 router.use('/:provider/', req => { const { provider } = req.params; diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index 5745219625..93b91751ae 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-auth-node +## 0.6.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 0.6.4 + +### Patch Changes + +- 0169b23: Internal tweak to avoid circular dependencies +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.6.4-next.1 ### Patch Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index fe24d798d6..e016c36adf 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.4-next.1", + "version": "0.6.5-next.0", "backstage": { "role": "node-library", "pluginId": "auth", diff --git a/plugins/auth-node/report.api.md b/plugins/auth-node/report.api.md index 090f981636..69e953cf13 100644 --- a/plugins/auth-node/report.api.md +++ b/plugins/auth-node/report.api.md @@ -3,8 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackstageIdentityResponse as BackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; -import { BackstageSignInResult as BackstageSignInResult_2 } from '@backstage/plugin-auth-node'; import { Config } from '@backstage/config'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -575,8 +573,8 @@ export type PassportProfile = Profile & { // @public export function prepareBackstageIdentityResponse( - result: BackstageSignInResult_2, -): BackstageIdentityResponse_2; + result: BackstageSignInResult, +): BackstageIdentityResponse; // @public export type ProfileInfo = { diff --git a/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts b/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts index a1de4e365c..aa827cef3f 100644 --- a/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts +++ b/plugins/auth-node/src/identity/prepareBackstageIdentityResponse.ts @@ -15,10 +15,7 @@ */ import { InputError } from '@backstage/errors'; -import { - BackstageIdentityResponse, - BackstageSignInResult, -} from '@backstage/plugin-auth-node'; +import { BackstageIdentityResponse, BackstageSignInResult } from '../types'; function parseJwtPayload(token: string) { const [_header, payload, _signature] = token.split('.'); diff --git a/plugins/auth-react/CHANGELOG.md b/plugins/auth-react/CHANGELOG.md index e0380a9fa9..567c44ff77 100644 --- a/plugins/auth-react/CHANGELOG.md +++ b/plugins/auth-react/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-auth-react +## 0.1.17-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + +## 0.1.16 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + +## 0.1.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + ## 0.1.15 ### Patch Changes diff --git a/plugins/auth-react/package.json b/plugins/auth-react/package.json index f668c95c06..4a4727ea6b 100644 --- a/plugins/auth-react/package.json +++ b/plugins/auth-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-react", - "version": "0.1.15", + "version": "0.1.17-next.1", "description": "Web library for the auth plugin", "backstage": { "role": "web-library", diff --git a/plugins/bitbucket-cloud-common/CHANGELOG.md b/plugins/bitbucket-cloud-common/CHANGELOG.md index 6924ab6310..64a2932b96 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.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/bitbucket-cloud-common/package.json b/plugins/bitbucket-cloud-common/package.json index 97150d6e8d..9c8386a611 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", + "version": "0.3.1-next.0", "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 95417d8073..0cd6f78c46 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,68 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.4.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/integration@1.17.1-next.1 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-kubernetes-common@0.9.5 + +## 0.4.12 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-kubernetes-common@0.9.5 + +## 0.4.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-kubernetes-common@0.9.5 + ## 0.4.12-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index e1259daa7c..38ea9c61c9 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.12-next.1", + "version": "0.4.13-next.1", "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 c59dfea5b3..b870c2099b 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,38 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.3.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.3.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.3.6 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.3.6-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index cfc5f2a30d..5b8e18ff37 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.6-next.1", + "version": "0.3.7-next.1", "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 f2282c7796..d136b0879d 100644 --- a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-backstage-openapi +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/backend-openapi-utils@0.5.5-next.0 + +## 0.5.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.5.3-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 b3669aa611..3f1b0444d4 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.3-next.1", + "version": "0.5.4-next.0", "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 bbab017765..46f889a114 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,72 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.5.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-bitbucket-cloud-common@0.3.1-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.5.0 + +### Minor Changes + +- 8a150bf: **BREAKING**: `BitbucketCloudEntityProvider` now accepts a `CatalogService` instead of a `CatalogApi`. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-events-node@0.4.12 + +## 0.5.0-next.2 + +### Minor Changes + +- 8a150bf: **BREAKING**: `BitbucketCloudEntityProvider` now accepts a `CatalogService` instead of a `CatalogApi`. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.4.9-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index b0f4c08403..3c29b6962d 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.9-next.1", + "version": "0.5.1-next.1", "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 7506f728cf..16c9291be7 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.5.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.5.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.5.0 + +### Minor Changes + +- eb8b3aa: Add validateLocationsExist option to avoid generating locations for catalog-info.yaml files that do not exist in the source repository. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-events-node@0.4.12 + ## 0.4.2-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/config.d.ts b/plugins/catalog-backend-module-bitbucket-server/config.d.ts index 0f6889efa7..d57a15a736 100644 --- a/plugins/catalog-backend-module-bitbucket-server/config.d.ts +++ b/plugins/catalog-backend-module-bitbucket-server/config.d.ts @@ -51,6 +51,11 @@ export interface Config { */ skipArchivedRepos?: boolean; }; + /** + * (Optional) Whether to validate locations that exist before emitting them. + * Default: `false`. + */ + validateLocationsExist?: boolean; /** * (Optional) TaskScheduleDefinition for the refresh. */ @@ -83,6 +88,11 @@ export interface Config { */ skipArchivedRepos?: boolean; }; + /** + * (Optional) Whether to validate locations that exist before emitting them. + * Default: `false`. + */ + validateLocationsExist?: boolean; /** * (Optional) TaskScheduleDefinition for the refresh. */ diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index de7fb993e9..abe2b8f5f7 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.2-next.1", + "version": "0.5.1-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts index fa0c455b04..7bf6480f33 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/lib/BitbucketServerClient.ts @@ -70,8 +70,11 @@ export class BitbucketServerClient { repo: string; path: string; }): Promise { + const normalizedPath = options.path.startsWith('/') + ? options.path.substring(1) + : options.path; return fetch( - `${this.config.apiBaseUrl}/projects/${options.projectKey}/repos/${options.repo}/raw/${options.path}`, + `${this.config.apiBaseUrl}/projects/${options.projectKey}/repos/${options.repo}/raw/${normalizedPath}`, getBitbucketServerRequestOptions(this.config), ); } diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts index ba4e3e0411..790bef7939 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.test.ts @@ -668,6 +668,97 @@ describe('BitbucketServerEntityProvider', () => { }); }); + it('do not add locations when validateLocationsExist and catalog-info does not exist', async () => { + server.use( + rest.get( + `https://${host}/rest/api/1.0/projects/project-test/repos/repo-test/raw/catalog-info.yaml`, + (_, res, ctx) => { + return res(ctx.status(404)); + }, + ), + rest.get( + `https://${host}/rest/api/1.0/projects/other-project/repos/other-repo/raw/catalog-info.yaml`, + (_, res, ctx) => { + return res(ctx.status(200)); + }, + ), + ); + const config = new ConfigReader({ + integrations: { + bitbucketServer: [ + { + host: host, + }, + ], + }, + catalog: { + providers: { + bitbucketServer: { + mainProvider: { + host: host, + validateLocationsExist: true, + }, + }, + }, + }, + }); + const schedule = new PersistingTaskRunner(); + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + const provider = BitbucketServerEntityProvider.fromConfig(config, { + logger, + schedule, + })[0]; + expect(provider.getProviderName()).toEqual( + 'bitbucketServer-provider:mainProvider', + ); + + setupStubs( + [ + { key: 'project-test', repos: [{ name: 'repo-test' }] }, + { key: 'other-project', repos: [{ name: 'other-repo' }] }, + ], + `https://${host}`, + 'master', + ); + await provider.connect(entityProviderConnection); + + const taskDef = schedule.getTasks()[0]; + expect(taskDef.id).toEqual('bitbucketServer-provider:mainProvider:refresh'); + await (taskDef.fn as () => Promise)(); + + const expectedEntities = [ + { + entity: { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Location', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'backstage.io/managed-by-origin-location': `url:https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + 'bitbucket.org/default-branch': 'master', + }, + name: 'generated-d8d4944c30c2906dfee172ddda9537f9893b2c0f', + }, + spec: { + presence: 'optional', + target: `https://${host}/projects/other-project/repos/other-repo/browse/catalog-info.yaml`, + type: 'url', + }, + }, + locationKey: 'bitbucketServer-provider:mainProvider', + }, + ]; + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(1); + expect(entityProviderConnection.applyMutation).toHaveBeenCalledWith({ + type: 'full', + entities: expectedEntities, + }); + }); + it('Multiple location entities to deferred entities', async () => { const schedule = new PersistingTaskRunner(); const config = new ConfigReader({ diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts index b462f8756a..4b02632892 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProvider.ts @@ -253,6 +253,37 @@ export class BitbucketServerEntityProvider implements EntityProvider { if (this.config?.filters?.skipArchivedRepos && repository.archived) { continue; } + if (this.config.validateLocationsExist) { + try { + const response = await client.getFile({ + projectKey: project.key, + repo: repository.slug, + path: this.config.catalogPath, + }); + if (!response.ok) { + if (response.status === 404) { + this.logger.debug( + `Skipping repository ${repository.slug} in project ${project.key} because the catalog file does not exist.`, + ); + } else { + this.logger.warn( + `Unexpected response code ${ + response.status + } while fetching the catalog file from repository ${ + repository.slug + } in project ${ + project.key + }. Response details: ${JSON.stringify(response)}`, + ); + } + continue; + } + } catch (error) { + this.logger.error( + `An error occurred while fetching the catalog file from repository ${repository.slug} in project ${project.key}: ${error}`, + ); + } + } for await (const entity of this.parser({ client, logger: this.logger, diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts index 755fa4b58a..cd39f0659b 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.test.ts @@ -49,6 +49,8 @@ describe('readProviderConfigs', () => { repoSlug: undefined, skipArchivedRepos: undefined, }, + schedule: undefined, + validateLocationsExist: false, }); }); @@ -89,6 +91,8 @@ describe('readProviderConfigs', () => { repoSlug: undefined, skipArchivedRepos: undefined, }, + schedule: undefined, + validateLocationsExist: false, }); expect(providerConfigs[1]).toEqual({ id: 'secondaryProvider', @@ -99,6 +103,8 @@ describe('readProviderConfigs', () => { repoSlug: undefined, skipArchivedRepos: undefined, }, + schedule: undefined, + validateLocationsExist: false, }); expect(providerConfigs[2]).toEqual({ id: 'thirdProvider', @@ -109,6 +115,7 @@ describe('readProviderConfigs', () => { repoSlug: undefined, skipArchivedRepos: undefined, }, + validateLocationsExist: false, schedule: { frequency: { minutes: 30 }, timeout: { @@ -147,6 +154,36 @@ describe('readProviderConfigs', () => { repoSlug: /.*/, skipArchivedRepos: true, }, + schedule: undefined, + validateLocationsExist: false, + }); + }); + + it('single simple provider config with validateLocationsExist', () => { + const config = new ConfigReader({ + catalog: { + providers: { + bitbucketServer: { + host: 'bitbucket.mycompany.com', + validateLocationsExist: true, + }, + }, + }, + }); + const providerConfigs = readProviderConfigs(config); + + expect(providerConfigs).toHaveLength(1); + expect(providerConfigs[0]).toEqual({ + id: 'default', + catalogPath: '/catalog-info.yaml', + host: 'bitbucket.mycompany.com', + filters: { + projectKey: undefined, + repoSlug: undefined, + skipArchivedRepos: undefined, + }, + schedule: undefined, + validateLocationsExist: true, }); }); }); diff --git a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts index 3b921b6bab..1a224536a7 100644 --- a/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts +++ b/plugins/catalog-backend-module-bitbucket-server/src/providers/BitbucketServerEntityProviderConfig.ts @@ -32,6 +32,7 @@ export type BitbucketServerEntityProviderConfig = { repoSlug?: RegExp; skipArchivedRepos?: boolean; }; + validateLocationsExist: boolean; schedule?: SchedulerServiceTaskScheduleDefinition; }; @@ -66,7 +67,8 @@ function readProviderConfig( const skipArchivedReposFlag = config.getOptionalBoolean( 'filters.skipArchivedRepos', ); - + const validateLocationsExistFlag = + config?.getOptionalBoolean('validateLocationsExist') ?? false; const schedule = config.has('schedule') ? readSchedulerServiceTaskScheduleDefinitionFromConfig( config.getConfig('schedule'), @@ -82,6 +84,7 @@ function readProviderConfig( repoSlug: repoSlugPattern ? new RegExp(repoSlugPattern) : undefined, skipArchivedRepos: skipArchivedReposFlag, }, + validateLocationsExist: validateLocationsExistFlag, schedule, }; } diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index 99df738894..0420c1bc22 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + +## 0.3.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-kubernetes-common@0.9.5 + ## 0.3.9-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index dbcd8c8249..bf14b1f51e 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.9-next.1", + "version": "0.3.10-next.0", "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 104a13390d..cd47b604b6 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 5568702238..fab0894e17 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.3-next.1", + "version": "0.3.4-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "catalog", diff --git a/plugins/catalog-backend-module-gitea/CHANGELOG.md b/plugins/catalog-backend-module-gitea/CHANGELOG.md index 849f8205b4..21e817725e 100644 --- a/plugins/catalog-backend-module-gitea/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-catalog-backend-module-gitea +## 0.1.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.1.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.1.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitea/package.json b/plugins/catalog-backend-module-gitea/package.json index 714252d43a..5304108d01 100644 --- a/plugins/catalog-backend-module-gitea/package.json +++ b/plugins/catalog-backend-module-gitea/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-gitea", - "version": "0.1.1-next.1", + "version": "0.1.2-next.1", "license": "Apache-2.0", "description": "The gitea backend module for the catalog plugin.", "main": "src/index.ts", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index 8e0cb68570..64d67ed4de 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-backend-module-github@0.10.1-next.1 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-github@0.10.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/config@1.3.2 + +## 0.3.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-catalog-backend-module-github@0.10.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/plugin-events-node@0.4.12 + +## 0.3.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-backend-module-github@0.9.1-next.2 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.3.11-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index 438ec45dcf..cf7d1afb74 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.11-next.1", + "version": "0.3.12-next.1", "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 11ff006b82..1369941ecc 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,69 @@ # @backstage/plugin-catalog-backend-module-github +## 0.10.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-backend@3.0.0-next.1 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.10.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.0.0-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.10.0 + +### Minor Changes + +- 7c0dfb0: GitHub organization now matches in a case-insensitive manner when processing events. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-backend@2.1.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-events-node@0.4.12 + +## 0.9.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@2.0.1-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.9.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index 95fae59471..2ab1be32cd 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.1-next.1", + "version": "0.10.1-next.1", "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.test.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts index b7df19c296..4032e1d368 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.test.ts @@ -940,6 +940,27 @@ describe('GithubEntityProvider', () => { expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(0); expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes(0); }); + + it('should process when orgs case-insensitive match', async () => { + const config = createSingleProviderConfig({ + providerConfig: { + organization: 'test-org', + }, + }); + const provider = createProviders(config)[0]; + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + await provider.connect(entityProviderConnection); + + const event = createPushEvent({ org: 'Test-Org' }); + + await provider.onEvent(event); + + expect(entityProviderConnection.refresh).toHaveBeenCalledTimes(1); + }); }); describe('on repository event', () => { @@ -1189,6 +1210,36 @@ describe('GithubEntityProvider', () => { 0, ); }); + + it('applies update when orgs case-insensitive match', async () => { + const config = createSingleProviderConfig({ + providerConfig: { + organization: 'test-org', + filters: { + topic: { + include: ['backstage-include'], + }, + }, + }, + }); + + const provider = createProviders(config)[0]; + + const entityProviderConnection: EntityProviderConnection = { + applyMutation: jest.fn(), + refresh: jest.fn(), + }; + await provider.connect(entityProviderConnection); + + const event = createRepoEvent('edited'); + event.eventPayload.organization!.login = 'Test-Org'; + + await provider.onEvent(event); + + expect(entityProviderConnection.applyMutation).toHaveBeenCalledTimes( + 1, + ); + }); }); describe('on repository privatized event', () => { diff --git a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts index 131237c581..5f3c652cb9 100644 --- a/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts +++ b/plugins/catalog-backend-module-github/src/providers/GithubEntityProvider.ts @@ -323,7 +323,13 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { } private async onPush(event: PushEvent) { - if (this.config.organization !== event.organization?.login) { + const configOrganization = this.config.organization; + const eventOrganization = event.organization?.login; + + if ( + configOrganization.toLocaleLowerCase('en-US') !== + eventOrganization?.toLocaleLowerCase('en-US') + ) { this.logger.debug( `skipping push event from organization ${event.organization?.login}`, ); @@ -406,7 +412,13 @@ export class GithubEntityProvider implements EntityProvider, EventSubscriber { } private async onRepoChange(event: RepositoryEvent) { - if (this.config.organization !== event.organization?.login) { + const configOrganization = this.config.organization; + const eventOrganization = event.organization?.login; + + if ( + configOrganization.toLocaleLowerCase('en-US') !== + eventOrganization?.toLocaleLowerCase('en-US') + ) { this.logger.debug( `skipping repository event from organization ${event.organization?.login}`, ); diff --git a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md index 87609736c4..f341943600 100644 --- a/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab-org/CHANGELOG.md @@ -1,5 +1,45 @@ # @backstage/plugin-catalog-backend-module-gitlab-org +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-backend-module-gitlab@0.7.1-next.1 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + +## 0.2.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend-module-gitlab@0.7.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab-org/package.json b/plugins/catalog-backend-module-gitlab-org/package.json index 6240674608..9a6c53e5fb 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.10-next.1", + "version": "0.2.11-next.1", "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 790d906cd9..86675c470b 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,70 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.7.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.7.0 + +### Minor Changes + +- 42bb3b8: **BREAKING CHANGE**: User and Group discovery will default to ingesting all users in sub groups that belong to the specified root group in config. Disable by setting `restrictUsersToGroup: true` in app-config under your module settings. + +### Patch Changes + +- 57a0bad: Implement retry for GitLab API calls to handle rate limiting +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-events-node@0.4.12 + +## 0.7.0-next.2 + +### Minor Changes + +- 42bb3b8: **BREAKING CHANGE**: User and Group discovery will default to ingesting all users in sub groups that belong to the specified root group in config. Disable by setting `restrictUsersToGroup: true` in app-config under your module settings. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.6.7-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 6dea628d79..eaff144da7 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.7-next.1", + "version": "0.7.1-next.1", "description": "A Backstage catalog backend module that helps integrate towards GitLab", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts index d7ce338d6d..d75d0e224e 100644 --- a/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts +++ b/plugins/catalog-backend-module-gitlab/src/GitLabDiscoveryProcessor.test.ts @@ -72,7 +72,7 @@ function setupFakeServer( assertion(req); } - if (req.headers.get('private-token') !== 'test-token') { + if (req.headers.get('authorization') !== 'Bearer test-token') { return res(ctx.status(401), ctx.json({})); } const page = req.url.searchParams.get('page'); @@ -104,8 +104,11 @@ function setupFakeServer( rest.head( `${API_URL}/projects/:project_path/repository/files/:file_path`, (req, res, ctx) => { - if (req.headers.get('private-token') !== 'test-token') { - return res(ctx.status(401), ctx.json({})); + if (req.headers.get('authorization') !== 'Bearer test-token') { + return res( + ctx.status(401), + ctx.json({ message: '401 Unauthorized' }), + ); } const ref = req.url.searchParams.get('ref'); diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 1bb1971898..4810d959c9 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,69 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.7.2-next.1 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- e2dd095: Fixed bug in `IncrementalIngestionEngine` by adding `burstLength` check when a burst completes +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-backend@3.0.0-next.1 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.0.0-next.0 + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + +## 0.7.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-catalog-backend@2.1.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-permission-common@0.9.0 + +## 0.7.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/plugin-catalog-backend@2.0.1-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + - @backstage/plugin-permission-common@0.9.0 + ## 0.7.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index b4cb22eb36..994a760ead 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.1-next.1", + "version": "0.7.2-next.1", "description": "An entity provider for streaming large asset sources into the catalog", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-incremental-ingestion/report.api.md b/plugins/catalog-backend-module-incremental-ingestion/report.api.md index b1fac447b2..fa69a0987c 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/report.api.md +++ b/plugins/catalog-backend-module-incremental-ingestion/report.api.md @@ -8,8 +8,6 @@ import type { DeferredEntity } from '@backstage/plugin-catalog-node'; import { EventParams } from '@backstage/plugin-events-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { HumanDuration } from '@backstage/types'; -import { IncrementalEntityProvider as IncrementalEntityProvider_2 } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; -import { IncrementalEntityProviderOptions as IncrementalEntityProviderOptions_2 } from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; // @public const catalogModuleIncrementalIngestionEntityProvider: BackendFeature; @@ -68,8 +66,8 @@ export interface IncrementalEntityProviderOptions { // @public export interface IncrementalIngestionProviderExtensionPoint { addProvider(config: { - options: IncrementalEntityProviderOptions_2; - provider: IncrementalEntityProvider_2; + options: IncrementalEntityProviderOptions; + provider: IncrementalEntityProvider; }): void; } diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.test.ts b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.test.ts new file mode 100644 index 0000000000..4bfe0666da --- /dev/null +++ b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.test.ts @@ -0,0 +1,211 @@ +/* + * 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 { IncrementalIngestionEngine } from './IncrementalIngestionEngine'; +import { IterationEngineOptions } from '../types'; +import { performance } from 'perf_hooks'; + +jest.setTimeout(60_000); + +describe('IncrementalIngestionEngine - Burst Length', () => { + const createMockProvider = () => ({ + getProviderName: jest.fn().mockReturnValue('test-provider'), + next: jest.fn(), + around: jest.fn(), + }); + + const createMockManager = () => + ({ + getLastMark: jest.fn().mockResolvedValue(null), + createMark: jest.fn().mockResolvedValue(undefined), + createMarkEntities: jest.fn().mockResolvedValue(undefined), + computeRemoved: jest.fn().mockResolvedValue({ total: 0, removed: [] }), + } as any); + + const createMockConnection = () => + ({ + applyMutation: jest.fn().mockResolvedValue(undefined), + refresh: jest.fn().mockResolvedValue(undefined), + } as any); + + const createMockLogger = () => + ({ + info: jest.fn(), + debug: jest.fn(), + error: jest.fn(), + warn: jest.fn(), + child: jest.fn().mockReturnThis(), + } as any); + + it('should respect burst length and stop burst when time limit exceeded', async () => { + const mockProvider = createMockProvider(); + const mockManager = createMockManager(); + const mockConnection = createMockConnection(); + const mockLogger = createMockLogger(); + + const options: IterationEngineOptions = { + provider: mockProvider, + manager: mockManager, + connection: mockConnection, + burstLength: { milliseconds: 100 }, // Short burst length for testing + restLength: { minutes: 1 }, + logger: mockLogger, + ready: Promise.resolve(), + }; + + const engine = new IncrementalIngestionEngine(options); + + let callCount = 0; + mockProvider.around.mockImplementation(async fn => { + await fn({}); + }); + + // Mock provider.next to return multiple batches that never complete + // Each call takes some time to simulate real processing + mockProvider.next.mockImplementation(async () => { + callCount++; + // Add a small delay to ensure we exceed burst length + await new Promise(resolve => setTimeout(resolve, 30)); + return { + done: false, + entities: [ + { + entity: { + kind: 'Component', + metadata: { name: `test-component-${callCount}` }, + }, + }, + ], + cursor: `cursor-${callCount}`, + }; + }); + + const signal = new AbortController().signal; + const start = performance.now(); + + const result = await engine.ingestOneBurst('test-ingestion', signal); + + const duration = performance.now() - start; + + // Verify that the burst was stopped due to time limit, not completion + expect(result).toBe(false); + expect(duration).toBeGreaterThanOrEqual(100); + expect(duration).toBeLessThan(200); + expect(mockProvider.next).toHaveBeenCalledTimes(callCount); + + expect(callCount).toBeGreaterThan(1); + }); + + it('should complete burst normally when provider returns done before burst length', async () => { + const mockProvider = createMockProvider(); + const mockManager = createMockManager(); + const mockConnection = createMockConnection(); + const mockLogger = createMockLogger(); + + const options: IterationEngineOptions = { + provider: mockProvider, + manager: mockManager, + connection: mockConnection, + burstLength: { seconds: 10 }, + restLength: { minutes: 1 }, + logger: mockLogger, + ready: Promise.resolve(), + }; + + const engine = new IncrementalIngestionEngine(options); + + mockProvider.around.mockImplementation(async fn => { + await fn({}); + }); + + // Mock provider.next to return done after first call + mockProvider.next.mockResolvedValueOnce({ + done: true, + entities: [ + { + entity: { + kind: 'Component', + metadata: { name: 'test-component-1' }, + }, + }, + ], + cursor: 'final-cursor', + }); + + const signal = new AbortController().signal; + const start = performance.now(); + const result = await engine.ingestOneBurst('test-ingestion', signal); + const duration = performance.now() - start; + + expect(result).toBe(true); + expect(mockProvider.next).toHaveBeenCalledTimes(1); + expect(duration).toBeLessThan(100); // Should complete quickly since provider returns done immediately + }); + + it('should stop burst when time limit is reached', async () => { + const mockProvider = createMockProvider(); + const mockManager = createMockManager(); + const mockConnection = createMockConnection(); + const mockLogger = createMockLogger(); + + const options: IterationEngineOptions = { + provider: mockProvider, + manager: mockManager, + connection: mockConnection, + burstLength: { milliseconds: 80 }, + restLength: { minutes: 1 }, + logger: mockLogger, + ready: Promise.resolve(), + }; + + const engine = new IncrementalIngestionEngine(options); + + let callCount = 0; + mockProvider.around.mockImplementation(async fn => { + await fn({}); + }); + + mockProvider.next.mockImplementation(async () => { + callCount++; + await new Promise(resolve => setTimeout(resolve, 30)); + return { + done: false, + entities: [ + { + entity: { + kind: 'Component', + metadata: { name: `test-component-${callCount}` }, + }, + }, + ], + cursor: `cursor-${callCount}`, + }; + }); + + const signal = new AbortController().signal; + const start = performance.now(); + + const result = await engine.ingestOneBurst('test-ingestion', signal); + + const duration = performance.now() - start; + + expect(result).toBe(false); + expect(mockProvider.next).toHaveBeenCalledTimes(3); + expect(callCount).toBe(3); + expect(duration).toBeGreaterThanOrEqual(90); + expect(duration).toBeLessThan(120); + }); +}); diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts index e005d29292..95510e78e4 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/engine/IncrementalIngestionEngine.ts @@ -27,6 +27,7 @@ import { HumanDuration } from '@backstage/types'; export class IncrementalIngestionEngine implements IterationEngine { private readonly restLength: Duration; + private readonly burstLength: Duration; private readonly backoff: HumanDuration[]; private readonly lastStarted: Gauge; private readonly lastCompleted: Gauge; @@ -38,6 +39,7 @@ export class IncrementalIngestionEngine implements IterationEngine { this.manager = options.manager; this.restLength = Duration.fromObject(options.restLength); + this.burstLength = Duration.fromObject(options.burstLength); this.backoff = options.backoff ?? [ { minutes: 1 }, { minutes: 5 }, @@ -247,6 +249,14 @@ export class IncrementalIngestionEngine implements IterationEngine { }); if (signal.aborted || next.done) { break; + } else if ( + performance.now() - start > + this.burstLength.as('milliseconds') + ) { + this.options.logger.info( + `incremental-engine: Ingestion '${id}' burst ending after ${this.burstLength.toHuman()}.`, + ); + break; } else { next = await this.options.provider.next(context, next.cursor); count++; diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts index 9e931c11fd..8c18f09798 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/module/catalogModuleIncrementalIngestionEntityProvider.ts @@ -20,12 +20,12 @@ import { createExtensionPoint, } from '@backstage/backend-plugin-api'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { WrapperProviders } from './WrapperProviders'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; import { IncrementalEntityProvider, IncrementalEntityProviderOptions, -} from '@backstage/plugin-catalog-backend-module-incremental-ingestion'; -import { WrapperProviders } from './WrapperProviders'; -import { eventsServiceRef } from '@backstage/plugin-events-node'; +} from '../types'; /** * @public diff --git a/plugins/catalog-backend-module-incremental-ingestion/src/types.ts b/plugins/catalog-backend-module-incremental-ingestion/src/types.ts index 75aecf8fa5..04a73bd364 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/src/types.ts +++ b/plugins/catalog-backend-module-incremental-ingestion/src/types.ts @@ -188,6 +188,7 @@ export interface IterationEngineOptions { manager: IncrementalIngestionDatabaseManager; provider: IncrementalEntityProvider; restLength: HumanDuration; + burstLength: HumanDuration; ready: Promise; backoff?: IncrementalEntityProviderOptions['backoff']; rejectRemovalsAbovePercentage?: number; diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 53039b1260..578d41df95 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.11.7-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.11.6 + +### Patch Changes + +- 087d51d: Export LDAP vendor types and instances for testing custom transformers +- f07b0ad: Added the ability to configure disabling one side of the relations tree with LDAP. + + Groups have a `member` attribute and users have a `memberOf` attribute, however these can drift out of sync in some LDAP installations, leaving weird states in the Catalog as we collate these results together and deduplicate them. + + You can chose to optionally disable one side of these relationships, or even both by setting the respective mapping to `null` in your `app-config.yaml` for your groups and/or users: + + ```yaml + catalog: + providers: + ldapOrg: + default: + target: ldaps://ds.example.net + bind: + dn: uid=ldap-reader-user,ou=people,ou=example,dc=example,dc=net + secret: ${LDAP_SECRET} + users: + - dn: ou=people,ou=example,dc=example,dc=net + options: + filter: (uid=*) + map: + # this ensures that outgoing memberships from users is ignored + memberOf: null + groups: + - dn: ou=access,ou=groups,ou=example,dc=example,dc=net + options: + filter: (&(objectClass=some-group-class)(!(groupType=email))) + map: + description: l + set: + metadata.customField: 'hello' + map: + # this ensures that outgoing memberships from groups is ignored + members: null + ``` + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.11.6-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/config.d.ts b/plugins/catalog-backend-module-ldap/config.d.ts index 8bd8623891..f9b49eabee 100644 --- a/plugins/catalog-backend-module-ldap/config.d.ts +++ b/plugins/catalog-backend-module-ldap/config.d.ts @@ -141,7 +141,7 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.memberOf field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; }; } | Array<{ @@ -221,7 +221,7 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.memberOf field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; }; }>; @@ -311,12 +311,12 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.parent field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; /** * The name of the attribute that shall be used for the values of * the spec.children field of the entity. Defaults to "member". */ - members?: string; + members?: string | null; }; } | Array<{ @@ -401,12 +401,12 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.parent field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; /** * The name of the attribute that shall be used for the values of * the spec.children field of the entity. Defaults to "member". */ - members?: string; + members?: string | null; }; }>; /** @@ -556,7 +556,7 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.memberOf field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; }; } | Array<{ @@ -588,6 +588,7 @@ export interface Config { pagePause?: boolean; }; }; + /** * JSON paths (on a.b.c form) and hard coded values to set on those * paths. @@ -636,7 +637,7 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.memberOf field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; }; }>; @@ -726,12 +727,12 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.parent field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; /** * The name of the attribute that shall be used for the values of * the spec.children field of the entity. Defaults to "member". */ - members?: string; + members?: string | null; }; } | Array<{ @@ -816,12 +817,12 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.parent field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; /** * The name of the attribute that shall be used for the values of * the spec.children field of the entity. Defaults to "member". */ - members?: string; + members?: string | null; }; }>; @@ -926,7 +927,6 @@ export interface Config { * paths. * * This can be useful for example if you want to hard code a - * namespace or similar on the generated entities. */ set?: { [key: string]: JsonValue }; /** @@ -969,7 +969,7 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.memberOf field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; }; }; @@ -1006,6 +1006,7 @@ export interface Config { }; }; /** + * @default false * JSON paths (on a.b.c form) and hard coded values to set on those * paths. * @@ -1058,12 +1059,12 @@ export interface Config { * The name of the attribute that shall be used for the values of * the spec.parent field of the entity. Defaults to "memberOf". */ - memberOf?: string; + memberOf?: string | null; /** * The name of the attribute that shall be used for the values of * the spec.children field of the entity. Defaults to "member". */ - members?: string; + members?: string | null; }; }; /** diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index a5d22ba3f5..5b31f831d1 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.6-next.1", + "version": "0.11.7-next.0", "description": "A Backstage catalog backend module that helps integrate towards LDAP", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-ldap/report.api.md b/plugins/catalog-backend-module-ldap/report.api.md index 16546cb773..1bcf59affb 100644 --- a/plugins/catalog-backend-module-ldap/report.api.md +++ b/plugins/catalog-backend-module-ldap/report.api.md @@ -12,7 +12,6 @@ import { EntityProvider } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { GroupEntity } from '@backstage/catalog-model'; -import { GroupTransformer as GroupTransformer_2 } from '@backstage/plugin-catalog-backend-module-ldap'; import { JsonValue } from '@backstage/types'; import { LocationSpec } from '@backstage/plugin-catalog-common'; import { LoggerService } from '@backstage/backend-plugin-api'; @@ -22,7 +21,6 @@ import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugi import { SearchEntry } from 'ldapjs'; import { SearchOptions } from 'ldapjs'; import { UserEntity } from '@backstage/catalog-model'; -import { UserTransformer as UserTransformer_2 } from '@backstage/plugin-catalog-backend-module-ldap'; // @public export type BindConfig = { @@ -63,8 +61,8 @@ export type GroupConfig = { displayName: string; email?: string; picture?: string; - memberOf: string; - members: string; + memberOf: string | null; + members: string | null; }; }; @@ -152,10 +150,10 @@ export type LdapOrgEntityProviderOptions = // @public export interface LdapOrgEntityProviderTransformsExtensionPoint { setGroupTransformer( - transformer: GroupTransformer_2 | Record, + transformer: GroupTransformer | Record, ): void; setUserTransformer( - transformer: UserTransformer_2 | Record, + transformer: UserTransformer | Record, ): void; } @@ -259,7 +257,7 @@ export type UserConfig = { displayName: string; email: string; picture?: string; - memberOf: string; + memberOf: string | null; }; }; @@ -275,4 +273,14 @@ export type VendorConfig = { dnAttributeName?: string; uuidAttributeName?: string; }; + +// @public +export const vendors: { + readonly activeDirectory: LdapVendor; + readonly aeDir: LdapVendor; + readonly freeIpa: LdapVendor; + readonly googleLdap: LdapVendor; + readonly lldap: LdapVendor; + readonly default: LdapVendor; +}; ``` diff --git a/plugins/catalog-backend-module-ldap/src/ldap/config.ts b/plugins/catalog-backend-module-ldap/src/ldap/config.ts index 6fdf682b42..8c7fd0dda5 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/config.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/config.ts @@ -90,6 +90,7 @@ export type UserConfig = { // Only the scope, filter, attributes, and paged fields are supported. The // default is scope "one" and attributes "*" and "+". options: SearchOptions; + // JSON paths (on a.b.c form) and hard coded values to set on those paths set?: { [path: string]: JsonValue }; // Mappings from well known entity fields, to LDAP attribute names @@ -114,7 +115,7 @@ export type UserConfig = { picture?: string; // The name of the attribute that shall be used for the values of the // spec.memberOf field of the entity. Defaults to "memberOf". - memberOf: string; + memberOf: string | null; }; }; @@ -129,6 +130,7 @@ export type GroupConfig = { // The search options to use. // Only the scope, filter, attributes, and paged fields are supported. options: SearchOptions; + // JSON paths (on a.b.c form) and hard coded values to set on those paths set?: { [path: string]: JsonValue }; // Mappings from well known entity fields, to LDAP attribute names @@ -156,10 +158,10 @@ export type GroupConfig = { picture?: string; // The name of the attribute that shall be used for the values of the // spec.parent field of the entity. Defaults to "memberOf". - memberOf: string; + memberOf: string | null; // The name of the attribute that shall be used for the values of the // spec.children field of the entity. Defaults to "member". - members: string; + members: string | null; }; }; diff --git a/plugins/catalog-backend-module-ldap/src/ldap/index.ts b/plugins/catalog-backend-module-ldap/src/ldap/index.ts index 6d8fbd06d6..800ebec316 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/index.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/index.ts @@ -13,6 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { + ActiveDirectoryVendor, + AEDirVendor, + DefaultLdapVendor, + FreeIpaVendor, + GoogleLdapVendor, + LdapVendor, + LLDAPVendor, +} from './vendors'; export { LdapClient } from './client'; export { mapStringAttr } from './util'; @@ -37,3 +46,16 @@ export { readLdapOrg, } from './read'; export type { GroupTransformer, UserTransformer } from './types'; +/** + * An LDAP Vendor types. + * + * @public + */ +export const vendors = { + activeDirectory: ActiveDirectoryVendor as LdapVendor, + aeDir: AEDirVendor as LdapVendor, + freeIpa: FreeIpaVendor as LdapVendor, + googleLdap: GoogleLdapVendor as LdapVendor, + lldap: LLDAPVendor as LdapVendor, + default: DefaultLdapVendor as LdapVendor, +} as const; diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts index 4e4f4f7cfd..374cc2afd0 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.test.ts @@ -210,6 +210,53 @@ describe('readLdapUsers', () => { ); }); + it('should allow skipping memberOf', async () => { + client.getVendor.mockResolvedValue(DefaultLdapVendor); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn(searchEntry({ memberOf: ['x', 'y', 'z'] })); + }); + + client.getVendor.mockResolvedValue(DefaultLdapVendor); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + uid: ['uid-value'], + description: ['description-value'], + cn: ['cn-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + customDN: ['dn-value'], + customUUID: ['uuid-value'], + }), + ); + }); + const config: UserConfig[] = [ + { + dn: 'ddd', + options: {}, + + map: { + rdn: 'uid', + name: 'uid', + description: 'description', + displayName: 'cn', + email: 'mail', + picture: 'avatarUrl', + memberOf: null, + }, + }, + ]; + + const vendorConfig: VendorConfig = { + dnAttributeName: 'customDN', + uuidAttributeName: 'customUUID', + }; + + const { userMemberOf } = await readLdapUsers(client, config, vendorConfig); + expect(userMemberOf.size).toBe(0); + }); + it('transfers all attributes from Microsoft Active Directory', async () => { client.getVendor.mockResolvedValue(ActiveDirectoryVendor); client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { @@ -729,6 +776,51 @@ describe('readLdapGroups', () => { ); }); + it('should allow skipping members', async () => { + client.getVendor.mockResolvedValue(DefaultLdapVendor); + client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { + await fn( + searchEntry({ + cn: ['cn-value'], + description: ['description-value'], + tt: ['type-value'], + mail: ['mail-value'], + avatarUrl: ['avatarUrl-value'], + memberOf: ['x', 'y', 'z'], + member: ['e', 'f', 'g'], + customDN: ['dn-value'], + customUUID: ['uuid-value'], + }), + ); + }); + const config: GroupConfig[] = [ + { + dn: 'ddd', + options: {}, + map: { + rdn: 'cn', + name: 'cn', + description: 'description', + displayName: 'cn', + email: 'mail', + picture: 'avatarUrl', + type: 'tt', + memberOf: 'memberOf', + members: null, + }, + }, + ]; + + const vendorConfig: VendorConfig = { + dnAttributeName: 'customDN', + uuidAttributeName: 'customUUID', + }; + + const { groupMember } = await readLdapGroups(client, config, vendorConfig); + + expect(groupMember.size).toBe(0); + }); + it('can process a list of GroupConfigs', async () => { client.getVendor.mockResolvedValue(DefaultLdapVendor); client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => { diff --git a/plugins/catalog-backend-module-ldap/src/ldap/read.ts b/plugins/catalog-backend-module-ldap/src/ldap/read.ts index 9b9041913d..a3f3ce1e75 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/read.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/read.ts @@ -146,6 +146,7 @@ export async function readLdapUsers( mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => { ensureItems(userMemberOf, myDn, vs); }); + entities.push(entity); }); } @@ -277,6 +278,7 @@ export async function readLdapGroups( mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => { ensureItems(groupMemberOf, myDn, vs); }); + mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => { ensureItems(groupMember, myDn, vs); }); @@ -349,7 +351,7 @@ export async function readLdapOrg( function mapReferencesAttr( entry: SearchEntry, vendor: LdapVendor, - attributeName: string | undefined, + attributeName: string | undefined | null, setter: (sourceDn: string, targets: string[]) => void, ) { if (attributeName) { diff --git a/plugins/catalog-backend-module-ldap/src/module.ts b/plugins/catalog-backend-module-ldap/src/module.ts index ad76b133f1..dd47bf48da 100644 --- a/plugins/catalog-backend-module-ldap/src/module.ts +++ b/plugins/catalog-backend-module-ldap/src/module.ts @@ -20,11 +20,8 @@ import { createExtensionPoint, } from '@backstage/backend-plugin-api'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; -import { - GroupTransformer, - UserTransformer, -} from '@backstage/plugin-catalog-backend-module-ldap'; import { LdapOrgEntityProvider } from './processors'; +import { GroupTransformer, UserTransformer } from './ldap/types'; /** * Interface for {@link LdapOrgEntityProviderTransformsExtensionPoint}. diff --git a/plugins/catalog-backend-module-logs/CHANGELOG.md b/plugins/catalog-backend-module-logs/CHANGELOG.md index 5795eb8023..bda780e972 100644 --- a/plugins/catalog-backend-module-logs/CHANGELOG.md +++ b/plugins/catalog-backend-module-logs/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-catalog-backend-module-logs +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-backend@3.0.0-next.1 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@3.0.0-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + +## 0.1.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@2.1.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + +## 0.1.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@2.0.1-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + ## 0.1.11-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-logs/package.json b/plugins/catalog-backend-module-logs/package.json index c21f08cfa5..ba93fb1d11 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.11-next.1", + "version": "0.1.12-next.1", "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 9e495277c3..e581e8713c 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.7.2-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.7.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.7.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 67285b2897..0b4f7cabff 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.1-next.1", + "version": "0.7.2-next.0", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-msgraph/report.api.md b/plugins/catalog-backend-module-msgraph/report.api.md index 02d3949d04..c6687a638a 100644 --- a/plugins/catalog-backend-module-msgraph/report.api.md +++ b/plugins/catalog-backend-module-msgraph/report.api.md @@ -11,18 +11,14 @@ import { EntityProvider } from '@backstage/plugin-catalog-node'; import { EntityProviderConnection } from '@backstage/plugin-catalog-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { GroupEntity } from '@backstage/catalog-model'; -import { GroupTransformer as GroupTransformer_2 } from '@backstage/plugin-catalog-backend-module-msgraph'; import { LocationSpec } from '@backstage/plugin-catalog-common'; import { LoggerService } from '@backstage/backend-plugin-api'; import * as MicrosoftGraph from '@microsoft/microsoft-graph-types'; -import { OrganizationTransformer as OrganizationTransformer_2 } from '@backstage/plugin-catalog-backend-module-msgraph'; -import { ProviderConfigTransformer as ProviderConfigTransformer_2 } from '@backstage/plugin-catalog-backend-module-msgraph'; import { SchedulerService } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskRunner } from '@backstage/backend-plugin-api'; import { SchedulerServiceTaskScheduleDefinition } from '@backstage/backend-plugin-api'; import { TokenCredential } from '@azure/identity'; import { UserEntity } from '@backstage/catalog-model'; -import { UserTransformer as UserTransformer_2 } from '@backstage/plugin-catalog-backend-module-msgraph'; // @public const catalogModuleMicrosoftGraphOrgEntityProvider: BackendFeature; @@ -184,20 +180,20 @@ export const microsoftGraphOrgEntityProviderTransformExtensionPoint: ExtensionPo // @public export interface MicrosoftGraphOrgEntityProviderTransformsExtensionPoint { setGroupTransformer( - transformer: GroupTransformer_2 | Record, + transformer: GroupTransformer | Record, ): void; setOrganizationTransformer( transformer: - | OrganizationTransformer_2 - | Record, + | OrganizationTransformer + | Record, ): void; setProviderConfigTransformer( transformer: - | ProviderConfigTransformer_2 - | Record, + | ProviderConfigTransformer + | Record, ): void; setUserTransformer( - transformer: UserTransformer_2 | Record, + transformer: UserTransformer | Record, ): void; } diff --git a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts index 262c56cf3f..a11748f021 100644 --- a/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts +++ b/plugins/catalog-backend-module-msgraph/src/module/catalogModuleMicrosoftGraphOrgEntityProvider.ts @@ -25,7 +25,7 @@ import { OrganizationTransformer, ProviderConfigTransformer, UserTransformer, -} from '@backstage/plugin-catalog-backend-module-msgraph'; +} from '../microsoftGraph/types'; import { MicrosoftGraphOrgEntityProvider } from '../processors'; /** diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 0db664e8c8..8458b41442 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,40 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.2.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.2.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/catalog-model@1.7.4 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.2.11-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 742cf6e796..a882841d21 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.11-next.1", + "version": "0.2.12-next.1", "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 f1c0618cc8..5d62625f1a 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.2.12-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.2.11-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index 0df55cbbe0..b92c92a3e4 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.11-next.1", + "version": "0.2.12-next.0", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts index 891b19691a..9527ddd774 100644 --- a/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts +++ b/plugins/catalog-backend-module-puppetdb/src/puppet/read.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { PuppetDbEntityProviderConfig } from '../providers'; +import { PuppetDbEntityProviderConfig } from '../providers/PuppetDbEntityProviderConfig'; import { PuppetNode, ResourceTransformer } from './types'; import { ResourceEntity } from '@backstage/catalog-model'; import { defaultResourceTransformer } from './transformers'; diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index 1eacdb3f61..93a8635916 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,34 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.2.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-scaffolder-common@1.5.11 + ## 0.2.9-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 2c78f3f07c..6496e6f32b 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.9-next.1", + "version": "0.2.10-next.1", "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 87e43914b0..26afe56315 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.6.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.9-next.0 + +## 0.6.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-unprocessed-entities-common@0.0.8 + - @backstage/plugin-permission-common@0.9.0 + ## 0.6.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index f8a7d529b1..cee3253fb3 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.1-next.1", + "version": "0.6.2-next.0", "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 e09921bb01..f3d28782eb 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,99 @@ # @backstage/plugin-catalog-backend +## 3.0.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/backend-openapi-utils@0.5.5-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 3.0.0-next.0 + +### Major Changes + +- 2339363: **BREAKING:** The experimental `catalog.useUrlReadersSearch` configuration flag (introduced in v1.36) has been removed. + + The `UrlReaderProcessor` now always uses the `search` method of `UrlReaders`. Built-in `UrlReaderService` implementations have been updated accordingly. + If you use custom `UrlReaderService` implementations, you need to adapt their `search` method to correctly handle both specific URLs and potential + search patterns (see changes on built-in readers [in the original PR](https://github.com/backstage/backstage/pull/28379/files#diff-68b0452f173ee54bdd40f7b5e047a9cb8bb59200425622c212c217b76dac1d1b)). + + Previous behavior was to call the `search` method only if the parsed Git URL's filename contained a wildcard and use `readUrl` otherwise. Each `UrlReaderService` must implement this logic in the `search` method instead. + + This allows each `UrlReaderService` implementation to check whether it's a search URL (that contains a wildcard pattern) or not using logic that is specific to each provider. + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + +## 2.1.0 + +### Minor Changes + +- 2e7adf0: Implement the action `get-catalog-entity` with the `ActionsRegistry` + +### Patch Changes + +- 2cac8b0: You can now specify an optional value when applying the `HAS_LABEL` permission rule, similar to the `HAS_ANNOTATION` permission rule. +- c83cd8b: Fixed some circular or otherwise unclear imports +- 4654a78: Update `refresh_state_references.id` to be a big int +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + +## 2.0.1-next.2 + +### Patch Changes + +- 2cac8b0: You can now specify an optional value when applying the `HAS_LABEL` permission rule, similar to the `HAS_ANNOTATION` permission rule. +- Updated dependencies + - @backstage/backend-openapi-utils@0.5.4-next.1 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1-next.1 + ## 2.0.1-next.1 ### Patch Changes diff --git a/plugins/catalog-backend/config.d.ts b/plugins/catalog-backend/config.d.ts index 7e68c4bea9..3ba6047ada 100644 --- a/plugins/catalog-backend/config.d.ts +++ b/plugins/catalog-backend/config.d.ts @@ -218,19 +218,5 @@ export interface Config { * housing catalog-info files. */ processingInterval?: HumanDuration | false; - - /** - * Defines if the UrlReaderProcessor should always call the search method of the - * different UrlReaders. - * - * If set to false, the UrlReaderProcessor will use the legacy behavior that tries to - * parse a Git URL and calls search if there's wildcard patterns and readUrl otherwise. - * - * If set to true, the UrlReaderProcessor always call the search method and lets each UrlReader - * determine if it's a search pattern or not. - * - * This flag is temporary and will be enabled by default in future releases. - */ - useUrlReadersSearch?: boolean; }; } diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 8547858f65..4e691700e0 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.1-next.1", + "version": "3.0.0-next.1", "description": "The Backstage backend plugin that provides the Backstage catalog", "backstage": { "role": "backend-plugin", @@ -99,7 +99,6 @@ "@backstage/cli": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", "@backstage/repo-tools": "workspace:^", - "@backstage/test-utils": "workspace:^", "@types/core-js": "^2.5.4", "@types/express": "^4.17.6", "@types/git-url-parse": "^9.0.0", diff --git a/plugins/catalog-backend/report.api.md b/plugins/catalog-backend/report.api.md index 9bb94b2dd5..6e5ca956bc 100644 --- a/plugins/catalog-backend/report.api.md +++ b/plugins/catalog-backend/report.api.md @@ -135,11 +135,7 @@ export function transformLegacyPolicyToProcessor( // @public (undocumented) export class UrlReaderProcessor implements CatalogProcessor { - constructor(options: { - reader: UrlReaderService; - logger: LoggerService; - config?: Config; - }); + constructor(options: { reader: UrlReaderService; logger: LoggerService }); // (undocumented) getProcessorName(): string; // (undocumented) diff --git a/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.test.ts b/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.test.ts new file mode 100644 index 0000000000..ed8aec264d --- /dev/null +++ b/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.test.ts @@ -0,0 +1,78 @@ +/* + * 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 { createGetCatalogEntityAction } from './createGetCatalogEntityAction'; +import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; +import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha'; + +describe('createGetCatalogEntityAction', () => { + it('should throw an error if the entity is not found', async () => { + const mockActionsRegistry = actionsRegistryServiceMock(); + const mockCatalog = catalogServiceMock(); + + createGetCatalogEntityAction({ + catalog: mockCatalog, + actionsRegistry: mockActionsRegistry, + }); + + await expect( + mockActionsRegistry.invoke({ + id: 'test:get-catalog-entity', + input: { name: 'test' }, + }), + ).rejects.toThrow(`No entity found with name "test"`); + }); + + it('should throw an error if theres multiple entities found', async () => { + const mockActionsRegistry = actionsRegistryServiceMock(); + const mockCatalog = catalogServiceMock({ + entities: [ + { + kind: 'Component', + apiVersion: 'backstage.io/v1alpha1', + metadata: { + name: 'test', + namespace: 'default', + }, + spec: { + type: 'component', + }, + }, + { + kind: 'API', + apiVersion: 'backstage.io/v1alpha1', + metadata: { + name: 'test', + namespace: 'default', + }, + }, + ], + }); + + createGetCatalogEntityAction({ + catalog: mockCatalog, + actionsRegistry: mockActionsRegistry, + }); + + await expect( + mockActionsRegistry.invoke({ + id: 'test:get-catalog-entity', + input: { name: 'test' }, + }), + ).rejects.toThrow( + `Multiple entities found with name "test", please provide more specific filters. Entities found: "component:default/test", "api:default/test"`, + ); + }); +}); diff --git a/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.ts b/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.ts new file mode 100644 index 0000000000..da33064c08 --- /dev/null +++ b/plugins/catalog-backend/src/actions/createGetCatalogEntityAction.ts @@ -0,0 +1,95 @@ +/* + * 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 { ActionsRegistryService } from '@backstage/backend-plugin-api/alpha'; +import { stringifyEntityRef } from '@backstage/catalog-model'; +import { InputError } from '@backstage/errors'; +import { CatalogService } from '@backstage/plugin-catalog-node'; + +export const createGetCatalogEntityAction = ({ + catalog, + actionsRegistry, +}: { + catalog: CatalogService; + actionsRegistry: ActionsRegistryService; +}) => { + actionsRegistry.register({ + name: 'get-catalog-entity', + title: 'Get Catalog Entity', + description: ` +This allows you to get a single entity from the software catalog. +Each entity in the software catalog has a unique name, kind, and namespace. The default namespace is "default". +Each entity is identified by a unique entity reference, which is a string of the form "kind:namespace/name". + `, + schema: { + input: z => + z.object({ + kind: z + .string() + .describe( + 'The kind of the entity to query. If the kind is unknown it can be omitted.', + ) + .optional(), + namespace: z + .string() + .describe( + 'The namespace of the entity to query. If the namespace is unknown it can be omitted.', + ) + .optional(), + name: z.string().describe('The name of the entity to query'), + }), + // TODO: is there a better way to do this? + output: z => z.object({}).passthrough(), + }, + action: async ({ input, credentials }) => { + const filter: Record = { 'metadata.name': input.name }; + + if (input.kind) { + filter.kind = input.kind; + } + + if (input.namespace) { + filter['metadata.namespace'] = input.namespace; + } + + const { items } = await catalog.queryEntities( + { filter }, + { + credentials, + }, + ); + + if (items.length === 0) { + throw new InputError(`No entity found with name "${input.name}"`); + } + + if (items.length > 1) { + throw new Error( + `Multiple entities found with name "${ + input.name + }", please provide more specific filters. Entities found: ${items + .map(item => `"${stringifyEntityRef(item)}"`) + .join(', ')}`, + ); + } + + const [entity] = items; + + return { + output: entity, + }; + }, + }); +}; diff --git a/plugins/catalog-backend/src/processors/UrlReaderProcessor.test.ts b/plugins/catalog-backend/src/processors/UrlReaderProcessor.test.ts index 2d7268eb33..0b17ecb19f 100644 --- a/plugins/catalog-backend/src/processors/UrlReaderProcessor.test.ts +++ b/plugins/catalog-backend/src/processors/UrlReaderProcessor.test.ts @@ -30,8 +30,6 @@ import { import { defaultEntityDataParser } from '../util/parse'; import { UrlReaderProcessor } from './UrlReaderProcessor'; import { UrlReaders } from '@backstage/backend-defaults/urlReader'; -import { UrlReaderService } from '@backstage/backend-plugin-api'; -import { mockApis } from '@backstage/test-utils'; describe('UrlReaderProcessor', () => { const mockApiOrigin = 'http://localhost'; @@ -191,11 +189,11 @@ describe('UrlReaderProcessor', () => { expect(generated.location).toBe(spec); expect(generated.error.name).toBe('NotFoundError'); expect(generated.error.message).toBe( - `Unable to read url, NotFoundError: could not read ${mockApiOrigin}/component-notfound.yaml, 404 Not Found`, + `Unable to read url, no matching files found for ${mockApiOrigin}/component-notfound.yaml`, ); }); - it('uses search when there are globs', async () => { + it("uses reader' search method", async () => { const logger = mockServices.logger.mock(); const reader = mockServices.urlReader.mock({ @@ -206,38 +204,6 @@ describe('UrlReaderProcessor', () => { const emit = jest.fn(); - await processor.readLocation( - { type: 'url', target: 'https://github.com/a/b/blob/x/**/b.yaml' }, - false, - emit, - defaultEntityDataParser, - mockCache, - ); - - expect(reader.search).toHaveBeenCalledTimes(1); - }); - - it('uses search when catalog.useUrlReadersSearch flag is set to true', async () => { - const logger = mockServices.logger.mock(); - - const reader: jest.Mocked = { - readUrl: jest.fn(), - readTree: jest.fn(), - search: jest.fn().mockImplementation(async () => []), - }; - - const config = mockApis.config({ - data: { - catalog: { - useUrlReadersSearch: true, - }, - }, - }); - - const processor = new UrlReaderProcessor({ reader, logger, config }); - - const emit = jest.fn(); - await processor.readLocation( { type: 'url', target: 'https://github.com/a/b/blob/x/b.yaml' }, false, diff --git a/plugins/catalog-backend/src/processors/UrlReaderProcessor.ts b/plugins/catalog-backend/src/processors/UrlReaderProcessor.ts index bc383512be..4440a81bae 100644 --- a/plugins/catalog-backend/src/processors/UrlReaderProcessor.ts +++ b/plugins/catalog-backend/src/processors/UrlReaderProcessor.ts @@ -18,7 +18,6 @@ import { Entity } from '@backstage/catalog-model'; import { assertError } from '@backstage/errors'; import limiterFactory, { Limit } from 'p-limit'; import { LocationSpec } from '@backstage/plugin-catalog-common'; -import parseGitUrl from 'git-url-parse'; import { CatalogProcessor, CatalogProcessorCache, @@ -29,7 +28,6 @@ import { processingResult, } from '@backstage/plugin-catalog-node'; import { LoggerService, UrlReaderService } from '@backstage/backend-plugin-api'; -import { Config } from '@backstage/config'; const CACHE_KEY = 'v1'; @@ -48,25 +46,14 @@ export class UrlReaderProcessor implements CatalogProcessor { // This limiter is used for only consuming a limited number of read streams // concurrently. #limiter: Limit; - #useUrlReadersSearch: boolean; constructor( private readonly options: { reader: UrlReaderService; logger: LoggerService; - config?: Config; }, ) { this.#limiter = limiterFactory(5); - - this.#useUrlReadersSearch = - this.options.config?.getOptionalBoolean('catalog.useUrlReadersSearch') || - false; - if (!this.#useUrlReadersSearch) { - this.options.logger.warn( - 'UrlReaderProcessor uses the legacy readUrl/search behavior which will be removed in a future release. Set catalog.useUrlReadersSearch to true to adopt the new behavior.', - ); - } } getProcessorName() { @@ -149,35 +136,13 @@ export class UrlReaderProcessor implements CatalogProcessor { location: string, etag?: string, ): Promise<{ response: { data: Buffer; url: string }[]; etag?: string }> { - // New behavior: always use the search method - if (this.#useUrlReadersSearch) { - const response = await this.options.reader.search(location, { etag }); + const response = await this.options.reader.search(location, { etag }); - const output = response.files.map(async file => ({ - url: file.url, - data: await this.#limiter(file.content), - })); + const output = response.files.map(async file => ({ + url: file.url, + data: await this.#limiter(file.content), + })); - return { response: await Promise.all(output), etag: response.etag }; - } - - // Old behavior: Does it contain globs? I.e. does it contain asterisks or question marks - // (no curly braces for now) - - const { filepath } = parseGitUrl(location); - if (filepath?.match(/[*?]/)) { - const response = await this.options.reader.search(location, { etag }); - const output = response.files.map(async file => ({ - url: file.url, - data: await this.#limiter(file.content), - })); - return { response: await Promise.all(output), etag: response.etag }; - } - - const data = await this.options.reader.readUrl(location, { etag }); - return { - response: [{ url: location, data: await data.buffer() }], - etag: data.etag, - }; + return { response: await Promise.all(output), etag: response.etag }; } } diff --git a/plugins/catalog-backend/src/schema/openapi/generated/router.ts b/plugins/catalog-backend/src/schema/openapi/generated/router.ts index 67eb1a699e..d32ef0a4de 100644 --- a/plugins/catalog-backend/src/schema/openapi/generated/router.ts +++ b/plugins/catalog-backend/src/schema/openapi/generated/router.ts @@ -18,7 +18,7 @@ // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils'; -import { EndpointMap } from './'; +import { EndpointMap } from './apis'; export const spec = { openapi: '3.0.3', diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index c9d75298c8..e50aa84d80 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -356,7 +356,7 @@ export class CatalogBuilder { return [ new FileReaderProcessor(), - new UrlReaderProcessor({ reader, logger, config }), + new UrlReaderProcessor({ reader, logger }), new AnnotateLocationEntityProcessor({ integrations }), ]; } diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index 9925f11c7b..a9bb2c9285 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -22,6 +22,7 @@ import { ForwardedError } from '@backstage/errors'; import { CatalogProcessor, CatalogProcessorParser, + catalogServiceRef, EntityProvider, LocationAnalyzer, PlaceholderResolver, @@ -43,6 +44,8 @@ import { eventsServiceRef } from '@backstage/plugin-events-node'; import { Permission } from '@backstage/plugin-permission-common'; import { merge } from 'lodash'; import { CatalogBuilder } from './CatalogBuilder'; +import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha'; +import { createGetCatalogEntityAction } from '../actions/createGetCatalogEntityAction'; class CatalogLocationsExtensionPointImpl implements CatalogLocationsExtensionPoint @@ -237,6 +240,8 @@ export const catalogPlugin = createBackendPlugin({ httpAuth: coreServices.httpAuth, auditor: coreServices.auditor, events: eventsServiceRef, + catalog: catalogServiceRef, + actionsRegistry: actionsRegistryServiceRef, }, async init({ logger, @@ -250,6 +255,8 @@ export const catalogPlugin = createBackendPlugin({ scheduler, auth, httpAuth, + catalog, + actionsRegistry, auditor, events, }) { @@ -313,6 +320,11 @@ export const catalogPlugin = createBackendPlugin({ } httpRouter.use(router); + + createGetCatalogEntityAction({ + catalog, + actionsRegistry, + }); }, }); }, diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index b0dddbdef0..193d5b5b52 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -56,7 +56,7 @@ describe('createRouter readonly disabled', () => { let app: express.Express | Server; let refreshService: RefreshService; let locationAnalyzer: jest.Mocked; - const permissionsService = mockServices.permissions.mock(); + const permissionsService = mockServices.permissions(); beforeEach(async () => { entitiesCatalog = { @@ -799,12 +799,6 @@ describe('createRouter readonly disabled', () => { metadata: { name: 'n' }, }; - permissionsService.authorize.mockResolvedValueOnce([ - { - result: AuthorizeResult.ALLOW, - }, - ]); - orchestrator.process.mockResolvedValueOnce({ ok: true, state: {}, @@ -845,12 +839,6 @@ describe('createRouter readonly disabled', () => { metadata: { name: 'invalid*name' }, }; - permissionsService.authorize.mockResolvedValueOnce([ - { - result: AuthorizeResult.ALLOW, - }, - ]); - orchestrator.process.mockResolvedValueOnce({ ok: false, errors: [new Error('Invalid entity name')], @@ -888,12 +876,6 @@ describe('createRouter readonly disabled', () => { metadata: { name: 'n' }, }; - permissionsService.authorize.mockResolvedValueOnce([ - { - result: AuthorizeResult.ALLOW, - }, - ]); - const response = await request(app) .post('/validate-entity') .send({ entity, location: null }); @@ -940,7 +922,7 @@ describe('createRouter readonly and raw json enabled', () => { let entitiesCatalog: jest.Mocked; let app: express.Express; let locationService: jest.Mocked; - const permissionsService = mockServices.permissions.mock(); + const permissionsService = mockServices.permissions(); beforeAll(async () => { entitiesCatalog = { @@ -1144,7 +1126,7 @@ describe('NextRouter permissioning', () => { let locationService: jest.Mocked; let app: express.Express; let refreshService: RefreshService; - const permissionsService = mockServices.permissions.mock(); + const permissionsService = mockServices.permissions(); const fakeRule = createPermissionRule({ name: 'FAKE_RULE', diff --git a/plugins/catalog-common/CHANGELOG.md b/plugins/catalog-common/CHANGELOG.md index e0b901855d..a007e87419 100644 --- a/plugins/catalog-common/CHANGELOG.md +++ b/plugins/catalog-common/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-common +## 1.1.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + ## 1.1.4 ### Patch Changes diff --git a/plugins/catalog-common/package.json b/plugins/catalog-common/package.json index 17e631a547..5d9015b32e 100644 --- a/plugins/catalog-common/package.json +++ b/plugins/catalog-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-common", - "version": "1.1.4", + "version": "1.1.5-next.0", "description": "Common functionalities for the catalog plugin", "backstage": { "role": "common-library", diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index c1be6d3076..fd4712e4af 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,62 @@ # @backstage/plugin-catalog-graph +## 0.4.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.4.21-next.0 + +### Patch Changes + +- fe1a2f4: Catalog graph plugin support i18n +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + +## 0.4.20 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/types@1.2.1 + +## 0.4.20-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/types@1.2.1 + ## 0.4.20-next.1 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index f7cdb8c4bc..cb3324ae97 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.20-next.1", + "version": "0.4.21-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-graph", diff --git a/plugins/catalog-graph/report-alpha.api.md b/plugins/catalog-graph/report-alpha.api.md index cf5835e35c..230edde3da 100644 --- a/plugins/catalog-graph/report-alpha.api.md +++ b/plugins/catalog-graph/report-alpha.api.md @@ -16,6 +16,35 @@ import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; +import { TranslationRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +export const catalogGraphTranslationRef: TranslationRef< + 'catalog-graph', + { + readonly 'catalogGraphCard.title': 'Relations'; + readonly 'catalogGraphCard.deepLinkTitle': 'View graph'; + readonly 'catalogGraphPage.title': 'Catalog Graph'; + readonly 'catalogGraphPage.filterToggleButtonTitle': 'Filters'; + readonly 'catalogGraphPage.supportButtonDescription': 'Start tracking your component in by adding it to the software catalog.'; + readonly 'catalogGraphPage.simplifiedSwitchLabel': 'Simplified'; + readonly 'catalogGraphPage.mergeRelationsSwitchLabel': 'Merge relations'; + readonly 'catalogGraphPage.zoomOutDescription': 'Use pinch & zoom to move around the diagram. Click to change active node, shift click to navigate to entity.'; + readonly 'catalogGraphPage.curveFilter.title': 'Curve'; + readonly 'catalogGraphPage.curveFilter.curveStepBefore': 'Step Before'; + readonly 'catalogGraphPage.curveFilter.curveMonotoneX': 'Monotone X'; + readonly 'catalogGraphPage.directionFilter.title': 'Direction'; + readonly 'catalogGraphPage.directionFilter.leftToRight': 'Left to right'; + readonly 'catalogGraphPage.directionFilter.rightToLeft': 'Right to left'; + readonly 'catalogGraphPage.directionFilter.topToBottom': 'Top to bottom'; + readonly 'catalogGraphPage.directionFilter.bottomToTop': 'Bottom to top'; + readonly 'catalogGraphPage.maxDepthFilter.title': 'Max depth'; + readonly 'catalogGraphPage.maxDepthFilter.inputPlaceholder': '∞ Infinite'; + readonly 'catalogGraphPage.maxDepthFilter.clearButtonAriaLabel': 'clear max depth'; + readonly 'catalogGraphPage.selectedKindsFilter.title': 'Kinds'; + readonly 'catalogGraphPage.selectedRelationsFilter.title': 'Relations'; + } +>; // @public (undocumented) const _default: FrontendPlugin< diff --git a/plugins/catalog-graph/src/alpha.tsx b/plugins/catalog-graph/src/alpha.tsx index aa1f6a05cf..6ee16be1c4 100644 --- a/plugins/catalog-graph/src/alpha.tsx +++ b/plugins/catalog-graph/src/alpha.tsx @@ -96,3 +96,5 @@ export default createFrontendPlugin({ }, extensions: [CatalogGraphPage, CatalogGraphEntityCard], }); + +export { catalogGraphTranslationRef } from './translation'; diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx index e09d57bd31..ed8069a8f4 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.test.tsx @@ -34,6 +34,7 @@ import userEvent from '@testing-library/user-event'; import { catalogGraphRouteRef } from '../../routes'; import { CatalogGraphCard } from './CatalogGraphCard'; import Button from '@material-ui/core/Button'; +import { translationApiRef } from '@backstage/core-plugin-api/alpha'; describe('', () => { let entity: Entity; @@ -52,7 +53,10 @@ describe('', () => { namespace: 'd', }, }; - apis = TestApiRegistry.from([catalogApiRef, catalog]); + apis = TestApiRegistry.from( + [catalogApiRef, catalog], + [translationApiRef, mockApis.translation()], + ); wrapper = ( @@ -213,7 +217,12 @@ describe('', () => { const analyticsApi = mockApis.analytics(); await renderInTestApp( - + {wrapper} , { diff --git a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx index a75993a1bc..733f209990 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphCard/CatalogGraphCard.tsx @@ -38,6 +38,8 @@ import { EntityRelationsGraph, EntityRelationsGraphProps, } from '../EntityRelationsGraph'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { catalogGraphTranslationRef } from '../../translation'; /** @public */ export type CatalogGraphCardClassKey = 'card' | 'graph'; @@ -66,6 +68,7 @@ export const CatalogGraphCard = ( action?: ReactNode; }, ) => { + const { t } = useTranslationRef(catalogGraphTranslationRef); const { variant = 'gridItem', relationPairs = ALL_RELATION_PAIRS, @@ -81,7 +84,7 @@ export const CatalogGraphCard = ( action, rootEntityNames, onNodeClick, - title = 'Relations', + title = t('catalogGraphCard.title'), zoom = 'enable-on-click', } = props; @@ -133,7 +136,7 @@ export const CatalogGraphCard = ( variant={variant} noPadding deepLink={{ - title: 'View graph', + title: t('catalogGraphCard.deepLinkTitle'), link: catalogGraphUrl, }} > diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx index cf3e628a98..69cb04bf3a 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CatalogGraphPage.tsx @@ -50,6 +50,8 @@ import { SelectedKindsFilter } from './SelectedKindsFilter'; import { SelectedRelationsFilter } from './SelectedRelationsFilter'; import { SwitchFilter } from './SwitchFilter'; import { useCatalogGraphPage } from './useCatalogGraphPage'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { catalogGraphTranslationRef } from '../../translation'; /** @public */ export type CatalogGraphPageClassKey = @@ -136,7 +138,7 @@ export const CatalogGraphPage = ( initialState, entityFilter, } = props; - + const { t } = useTranslationRef(catalogGraphTranslationRef); const navigate = useNavigate(); const classes = useStyles(); const catalogEntityRoute = useRouteRef(entityRouteRef); @@ -192,7 +194,7 @@ export const CatalogGraphPage = ( return (
humanizeEntityRef(e)).join(', ')} /> @@ -203,13 +205,12 @@ export const CatalogGraphPage = ( selected={showFilters} onChange={() => toggleShowFilters()} > - Filters + {t('catalogGraphPage.filterToggleButtonTitle')} } > - Start tracking your component in by adding it to the software - catalog. + {t('catalogGraphPage.supportButtonDescription')} @@ -230,12 +231,12 @@ export const CatalogGraphPage = ( )} @@ -247,9 +248,8 @@ export const CatalogGraphPage = ( display="block" className={classes.legend} > - Use pinch & zoom to move - around the diagram. Click to change active node, shift click to - navigate to entity. + {' '} + {t('catalogGraphPage.zoomOutDescription')} ', () => { - test('should display current curve label', () => { + test('should display current curve label', async () => { const onChange = jest.fn(); - render(); + await renderInTestApp( + , + ); expect(screen.getByText('Monotone X')).toBeInTheDocument(); }); test('should select an alternative curve factory', async () => { const onChange = jest.fn(); - render(); + await renderInTestApp( + , + ); expect(screen.getByText('Step Before')).toBeInTheDocument(); diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx index 411c144b05..0e6a941b1a 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/CurveFilter.tsx @@ -16,12 +16,10 @@ import { Select, SelectedItems } from '@backstage/core-components'; import Box from '@material-ui/core/Box'; import { useCallback } from 'react'; +import { catalogGraphTranslationRef } from '../../translation'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; type Curve = 'curveStepBefore' | 'curveMonotoneX'; -const CURVE_DISPLAY_NAMES: Record = { - curveMonotoneX: 'Monotone X', - curveStepBefore: 'Step Before', -}; export type Props = { value: Curve; @@ -31,6 +29,12 @@ export type Props = { const curves: Array = ['curveMonotoneX', 'curveStepBefore']; export const CurveFilter = ({ value, onChange }: Props) => { + const { t } = useTranslationRef(catalogGraphTranslationRef); + const CURVE_DISPLAY_NAMES: Record = { + curveMonotoneX: t('catalogGraphPage.curveFilter.curveMonotoneX'), + curveStepBefore: t('catalogGraphPage.curveFilter.curveStepBefore'), + }; + const handleChange = useCallback( (v: SelectedItems) => onChange(v as Curve), [onChange], @@ -39,7 +43,7 @@ export const CurveFilter = ({ value, onChange }: Props) => { return ( ({ label: DIRECTION_DISPLAY_NAMES[v], diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx index ef3df7eb53..2e4d5d44a3 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.test.tsx @@ -14,20 +14,21 @@ * limitations under the License. */ -import { render, screen } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import user from '@testing-library/user-event'; import { MaxDepthFilter } from './MaxDepthFilter'; +import { renderInTestApp } from '@backstage/test-utils'; describe('', () => { - test('should display current value', () => { - render( {}} />); + test('should display current value', async () => { + await renderInTestApp( {}} />); expect(screen.getByLabelText('maxp')).toBeInTheDocument(); expect(screen.getByLabelText('maxp')).toHaveValue(5); }); - test('should display infinite if non finite', () => { - render( + test('should display infinite if non finite', async () => { + await renderInTestApp( {}} />, ); @@ -37,7 +38,7 @@ describe('', () => { test('should clear max depth', async () => { const onChange = jest.fn(); - render(); + await renderInTestApp(); expect(onChange).not.toHaveBeenCalled(); await user.click(screen.getByLabelText('clear max depth')); @@ -46,7 +47,7 @@ describe('', () => { test('should set max depth to undefined if below one', async () => { const onChange = jest.fn(); - render(); + await renderInTestApp(); await user.clear(screen.getByLabelText('maxp')); await user.type(screen.getByLabelText('maxp'), '0'); @@ -56,7 +57,7 @@ describe('', () => { test('should select direction', async () => { let value = 5; - render( + await renderInTestApp( { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx index 33742ad72e..cdd27b9adb 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/MaxDepthFilter.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { useTranslationRef } from '@backstage/frontend-plugin-api'; import Box from '@material-ui/core/Box'; import FormControl from '@material-ui/core/FormControl'; import IconButton from '@material-ui/core/IconButton'; @@ -22,6 +23,7 @@ import Typography from '@material-ui/core/Typography'; import { makeStyles } from '@material-ui/core/styles'; import ClearIcon from '@material-ui/icons/Clear'; import { ChangeEvent, useCallback, useEffect, useRef, useState } from 'react'; +import { catalogGraphTranslationRef } from '../../translation'; export type Props = { value: number; @@ -45,6 +47,7 @@ export const MaxDepthFilter = ({ value, onChange }: Props) => { const classes = useStyles(); const onChangeRef = useRef(onChange); const [currentValue, setCurrentValue] = useState(value); + const { t } = useTranslationRef(catalogGraphTranslationRef); // Keep a fresh reference to the latest callback useEffect(() => { @@ -75,16 +78,20 @@ export const MaxDepthFilter = ({ value, onChange }: Props) => { return ( - Max Depth + + {t('catalogGraphPage.maxDepthFilter.title')} + diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx index 929b22755d..4349d7005a 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.test.tsx @@ -15,13 +15,18 @@ */ import { ApiProvider } from '@backstage/core-app-api'; -import { AlertApi, alertApiRef } from '@backstage/core-plugin-api'; +import { AlertApi, alertApiRef, errorApiRef } from '@backstage/core-plugin-api'; import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { renderWithEffects, TestApiRegistry } from '@backstage/test-utils'; +import { + mockApis, + renderWithEffects, + TestApiRegistry, +} from '@backstage/test-utils'; import { waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { SelectedKindsFilter } from './SelectedKindsFilter'; import { catalogApiMock } from '@backstage/plugin-catalog-react/testUtils'; +import { translationApiRef } from '@backstage/core-plugin-api/alpha'; const catalogApi = catalogApiMock.mock({ getEntityFacets: jest.fn().mockResolvedValue({ @@ -38,6 +43,8 @@ const catalogApi = catalogApiMock.mock({ const apis = TestApiRegistry.from( [catalogApiRef, catalogApi], [alertApiRef, {} as AlertApi], + [translationApiRef, mockApis.translation()], + [errorApiRef, { post: jest.fn() }], ); describe('', () => { diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.tsx index 4970c4527a..d9a5ef9833 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedKindsFilter.tsx @@ -27,6 +27,8 @@ import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import Autocomplete from '@material-ui/lab/Autocomplete'; import { useCallback, useEffect, useMemo } from 'react'; import useAsync from 'react-use/esm/useAsync'; +import { catalogGraphTranslationRef } from '../../translation'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; /** @public */ export type SelectedKindsFilterClassKey = 'formControl'; @@ -49,6 +51,7 @@ export const SelectedKindsFilter = ({ value, onChange }: Props) => { const classes = useStyles(); const alertApi = useApi(alertApiRef); const catalogApi = useApi(catalogApiRef); + const { t } = useTranslationRef(catalogGraphTranslationRef); const { error, value: kinds } = useAsync(async () => { return await catalogApi @@ -91,13 +94,15 @@ export const SelectedKindsFilter = ({ value, onChange }: Props) => { return ( - Kinds + + {t('catalogGraphPage.selectedKindsFilter.title')} + kinds[normalizedKinds.indexOf(k)] ?? k} diff --git a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx index 41df29d072..057842bcdc 100644 --- a/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx +++ b/plugins/catalog-graph/src/components/CatalogGraphPage/SelectedRelationsFilter.test.tsx @@ -19,14 +19,15 @@ import { RELATION_HAS_MEMBER, RELATION_OWNED_BY, } from '@backstage/catalog-model'; -import { render, waitFor, screen } from '@testing-library/react'; +import { waitFor, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ALL_RELATION_PAIRS } from '../EntityRelationsGraph'; import { SelectedRelationsFilter } from './SelectedRelationsFilter'; +import { renderInTestApp } from '@backstage/test-utils'; describe('', () => { - test('should render current value', () => { - render( + test('should render current value', async () => { + await renderInTestApp( ', () => { test('should select value', async () => { const onChange = jest.fn(); - render( + await renderInTestApp( ', () => { test('should return undefined if all values are selected', async () => { const onChange = jest.fn(); - render( + await renderInTestApp( p).filter( @@ -92,7 +93,7 @@ describe('', () => { test('should return all values when cleared', async () => { const onChange = jest.fn(); - render( + await renderInTestApp( { const classes = useStyles(); const relations = useMemo(() => relationPairs.flat(), [relationPairs]); + const { t } = useTranslationRef(catalogGraphTranslationRef); const handleChange = useCallback( (_: unknown, v: string[]) => { @@ -65,13 +68,15 @@ export const SelectedRelationsFilter = ({ return ( - Relations + + {t('catalogGraphPage.selectedRelationsFilter.title')} + ', () => { const catalogImportApi: jest.Mocked = { diff --git a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx index 5a6cad055c..2100d3da81 100644 --- a/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx +++ b/plugins/catalog-import/src/components/StepInitAnalyzeUrl/StepInitAnalyzeUrl.tsx @@ -130,7 +130,7 @@ export const StepInitAnalyzeUrl = (props: StepInitAnalyzeUrlProps) => { } } } catch (e: any) { - setError(e?.data?.error?.message ?? e.message); + setError(e?.body?.error?.message ?? e.message); setSubmitted(false); } }, diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index b42bb83c6f..872cdeb979 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-catalog-node +## 1.17.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + +## 1.17.1 + +### Patch Changes + +- 0169b23: Internal tweak to avoid circular dependencies +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + ## 1.17.1-next.1 ### Patch Changes diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index bfe1bb218f..d6e1d8e424 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.1-next.1", + "version": "1.17.2-next.0", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", "backstage": { "role": "node-library", diff --git a/plugins/catalog-node/src/api/provider.ts b/plugins/catalog-node/src/api/provider.ts index a576f32022..63f3f0bd09 100644 --- a/plugins/catalog-node/src/api/provider.ts +++ b/plugins/catalog-node/src/api/provider.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DeferredEntity } from '../processing'; +import { DeferredEntity } from '../processing/types'; /** * A 'full' mutation replaces all existing entities created by this entity provider with new ones. diff --git a/plugins/catalog-node/src/processing/types.ts b/plugins/catalog-node/src/processing/types.ts index 625effc4df..d4255c1c51 100644 --- a/plugins/catalog-node/src/processing/types.ts +++ b/plugins/catalog-node/src/processing/types.ts @@ -21,7 +21,7 @@ import { AnalyzeLocationResponse, } from '@backstage/plugin-catalog-common'; import { JsonValue } from '@backstage/types'; -import { CatalogProcessorEmit } from '../api'; +import { CatalogProcessorEmit } from '../api/processor'; import { BackstageCredentials } from '@backstage/backend-plugin-api'; /** diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 3caa6d7959..eed7655238 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,179 @@ # @backstage/plugin-catalog-react +## 1.19.1-next.1 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-test-utils@0.3.4-next.1 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.19.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.2.9-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/frontend-test-utils@0.3.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-react@0.4.35 + +## 1.19.0 + +### Minor Changes + +- 406acb6: Introduces a new `EntityIconLinkBlueprint` that customizes the `About` card icon links on the `Catalog` entity page. + + The blueprint currently accepts a `useProps` hook as `param` and this function returns the following props that will be passed to the icon link component: + + | Name | Description | Type | Default Value | + | ---------- | --------------------------------------------------- | ------------- | ------------- | + | `icon` | The icon to display. | `JSX.Element` | N/A | + | `label` | The label for the element. | `string` | N/A | + | `title` | The title for the element. | `string` | N/A | + | `disabled` | Whether the element is disabled. | `boolean` | `false` | + | `href` | The URL to navigate to when the element is clicked. | `string` | N/A | + | `onClick` | A function to call when the element is clicked. | `() => void` | N/A | + + Here is an usage example: + + ```tsx + import { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha'; + //... + + EntityIconLinkBlueprint.make({ + name: 'my-icon-link', + params: { + useProps() { + const { t } = useTranslationRef(myIconLinkTranslationRef); + return { + label: t('myIconLink.label'), + icon: , + href: '/my-plugin', + }; + }, + }, + }); + ``` + + Additionally, the `app-config.yaml` file allows you to override some of the default icon link parameters, including `label` and `title` values. Here's how to set them: + + ```yaml + app: + extensions: + - entity-icon-link:my-plugin/my-icon-link: + config: + label: 'My Custom Icon Link label' + ``` + + Finally, you can disable all links if you want to hide the About card header completely (useful, for example, when links are displayed on separate cards). The header is hidden when no icon links extensions are enabled. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/integration-react@1.2.8 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.3.3 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-react@0.4.35 + +## 1.19.0-next.2 + +### Minor Changes + +- 406acb6: Introduces a new `EntityIconLinkBlueprint` that customizes the `About` card icon links on the `Catalog` entity page. + + The blueprint currently accepts a `useProps` hook as `param` and this function returns the following props that will be passed to the icon link component: + + | Name | Description | Type | Default Value | + | ---------- | --------------------------------------------------- | ------------- | ------------- | + | `icon` | The icon to display. | `JSX.Element` | N/A | + | `label` | The label for the element. | `string` | N/A | + | `title` | The title for the element. | `string` | N/A | + | `disabled` | Whether the element is disabled. | `boolean` | `false` | + | `href` | The URL to navigate to when the element is clicked. | `string` | N/A | + | `onClick` | A function to call when the element is clicked. | `() => void` | N/A | + + Here is an usage example: + + ```tsx + import { EntityIconLinkBlueprint } from '@backstage/plugin-catalog-react/alpha'; + //... + + EntityIconLinkBlueprint.make({ + name: 'my-icon-link', + params: { + useProps() { + const { t } = useTranslationRef(myIconLinkTranslationRef); + return { + label: t('myIconLink.label'), + icon: , + href: '/my-plugin', + }; + }, + }, + }); + ``` + + Additionally, the `app-config.yaml` file allows you to override some of the default icon link parameters, including `label` and `title` values. Here's how to set them: + + ```yaml + app: + extensions: + - entity-icon-link:my-plugin/my-icon-link: + config: + label: 'My Custom Icon Link label' + ``` + + Finally, you can disable all links if you want to hide the About card header completely (useful, for example, when links are displayed on separate cards). The header is hidden when no icon links extensions are enabled. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/frontend-test-utils@0.3.3-next.1 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-react@0.4.34 + ## 1.18.1-next.1 ### Patch Changes diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 9f5b7d7b3c..83072f52be 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.1-next.1", + "version": "1.19.1-next.1", "description": "A frontend library that helps other Backstage plugins interact with the catalog", "backstage": { "role": "web-library", diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.ts b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.ts index dbfb11092a..4ff9d83282 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.ts +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentBlueprint.ts @@ -27,7 +27,7 @@ import { entityContentGroupDataRef, defaultEntityContentGroups, } from './extensionData'; -import { EntityPredicate } from '../predicates'; +import { EntityPredicate } from '../predicates/types'; import { resolveEntityFilterData } from './resolveEntityFilterData'; import { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema'; import { Entity } from '@backstage/catalog-model'; diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContentLayoutBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContentLayoutBlueprint.tsx index e529d60685..c13ea8ad56 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContentLayoutBlueprint.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContentLayoutBlueprint.tsx @@ -25,7 +25,7 @@ import { EntityCardType, } from './extensionData'; import { JSX } from 'react'; -import { EntityPredicate } from '../predicates'; +import { EntityPredicate } from '../predicates/types'; import { resolveEntityFilterData } from './resolveEntityFilterData'; import { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema'; import { Entity } from '@backstage/catalog-model'; diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx index 470aa13646..4b9b166196 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityContextMenuItemBlueprint.tsx @@ -24,10 +24,8 @@ 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 { EntityPredicate } from '../predicates/types'; +import { entityPredicateToFilterFunction } from '../predicates/entityPredicateToFilterFunction'; import type { Entity } from '@backstage/catalog-model'; import { entityFilterFunctionDataRef } from './extensionData'; import { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema'; diff --git a/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx b/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx index 11c9a2b805..2368945c65 100644 --- a/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx +++ b/plugins/catalog-react/src/alpha/blueprints/EntityIconLinkBlueprint.tsx @@ -20,7 +20,7 @@ import { createExtensionDataRef, } from '@backstage/frontend-plugin-api'; -import { EntityPredicate } from '../predicates'; +import { EntityPredicate } from '../predicates/types'; import { createEntityPredicateSchema } from '../predicates/createEntityPredicateSchema'; import { diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx index 2a5dc115d4..fe0c386aea 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityCardExtension.tsx @@ -18,9 +18,9 @@ import { compatWrapper } from '@backstage/core-compat-api'; import { BackstagePlugin, getComponentData } from '@backstage/core-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { ComponentType } from 'react'; -import { EntityCardBlueprint } from '../blueprints'; +import { EntityCardBlueprint } from '../blueprints/EntityCardBlueprint'; import kebabCase from 'lodash/kebabCase'; -import { EntityPredicate } from '../predicates'; +import { EntityPredicate } from '../predicates/types'; import { Entity } from '@backstage/catalog-model'; /** @alpha */ diff --git a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx index 740427e9b5..719eb9c6d5 100644 --- a/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx +++ b/plugins/catalog-react/src/alpha/converters/convertLegacyEntityContentExtension.tsx @@ -27,8 +27,8 @@ import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import kebabCase from 'lodash/kebabCase'; import startCase from 'lodash/startCase'; import { ComponentType } from 'react'; -import { EntityContentBlueprint } from '../blueprints'; -import { EntityPredicate } from '../predicates'; +import { EntityContentBlueprint } from '../blueprints/EntityContentBlueprint'; +import { EntityPredicate } from '../predicates/types'; import { Entity } from '@backstage/catalog-model'; /** @alpha */ diff --git a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts index 9e5f44b327..cfd2e451df 100644 --- a/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts +++ b/plugins/catalog-react/src/alpha/predicates/createEntityPredicateSchema.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { EntityPredicate, EntityPredicateValue } from '.'; +import { EntityPredicate, EntityPredicateValue } from './types'; import type { z as zImpl, ZodType } from 'zod'; /** @internal */ diff --git a/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.test.ts b/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.test.ts index c83b213970..152230de7c 100644 --- a/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.test.ts +++ b/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.test.ts @@ -149,6 +149,8 @@ describe('entityPredicateToFilterFunction', () => { ['s,w,g,a', Object.create({ kind: 'component' })], ['', { 'metadata.tags': { $contains: 'go' } }], ['', { 'metadata.tags.0': 'java' }], + ['s,w,g', { kind: { $in: ['component', 'group'] } }], + ['a', { $not: { kind: { $in: ['component', 'group'] } } }], ['w,g,a', { $not: { 'metadata.tags': { $contains: 'java' } } }], [ 's,g', diff --git a/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.ts b/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.ts index 2fa220e9e2..f223911992 100644 --- a/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.ts +++ b/plugins/catalog-react/src/alpha/predicates/entityPredicateToFilterFunction.ts @@ -15,11 +15,7 @@ */ import { JsonValue } from '@backstage/types'; -import { - EntityPredicate, - EntityPredicatePrimitive, - EntityPredicateValue, -} from './types'; +import { EntityPredicate, EntityPredicateValue } from './types'; import { valueAtPath } from './valueAtPath'; /** @@ -92,7 +88,7 @@ function evaluatePredicateValue( return value.some(v => evaluateEntityPredicate(filter.$contains, v)); } if ('$in' in filter) { - return filter.$in.includes(value as EntityPredicatePrimitive); + return filter.$in.some(search => valuesAreEqual(value, search)); } if ('$exists' in filter) { if (filter.$exists === true) { diff --git a/plugins/catalog-unprocessed-entities-common/CHANGELOG.md b/plugins/catalog-unprocessed-entities-common/CHANGELOG.md index 0bed34b2e0..ec35b64b00 100644 --- a/plugins/catalog-unprocessed-entities-common/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-catalog-unprocessed-entities-common +## 0.0.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + ## 0.0.8 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities-common/package.json b/plugins/catalog-unprocessed-entities-common/package.json index 5bbe80a78a..d8ed071d09 100644 --- a/plugins/catalog-unprocessed-entities-common/package.json +++ b/plugins/catalog-unprocessed-entities-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-unprocessed-entities-common", - "version": "0.0.8", + "version": "0.0.9-next.0", "description": "Common functionalities for the catalog-unprocessed-entities plugin", "backstage": { "role": "common-library", diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index cc82f793d5..6d04d720a9 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.2.19-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.2.18 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + +## 0.2.18-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + ## 0.2.18-next.1 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index 95ef6bc065..64bd9de980 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.18-next.1", + "version": "0.2.19-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "catalog-unprocessed-entities", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index d5f981432c..671a9d57ac 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,128 @@ # @backstage/plugin-catalog +## 1.31.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.2 + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + +## 1.31.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.31.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration-react@1.2.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-search-react@1.9.2-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.31.0 + +### Minor Changes + +- 406acb6: Add support to customize the about card icon links via `EntityIconLinkBlueprint` and provide a default catalog view catalog source, launch scaffolder template and read techdocs docs icon links extensions. + + **BREAKING ALPHA** + + The `Scaffolder` launch template and `TechDocs` read documentation icons have been extracted from the default `Catalog` about card links and are now provided respectively by the `Scaffolder` and `TechDocs` plugins in the new frontend system. It means that they will not be available unless you install the `TechDocs` and `Scaffolder` plugins. Also If you are using translation for these icon link titles other than the default, you should now translate them using the scaffolder translation reference or the TechDocs translation reference (the translation keys are still the same, `aboutCard.viewTechdocs` and `aboutCard.launchTemplate`). + +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-search-react@1.9.1 + - @backstage/integration-react@1.2.8 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + - @backstage/plugin-search-common@1.2.18 + +## 1.31.0-next.2 + +### Minor Changes + +- 406acb6: Add support to customize the about card icon links via `EntityIconLinkBlueprint` and provide a default catalog view catalog source, launch scaffolder template and read techdocs docs icon links extensions. + + **BREAKING ALPHA** + + The `Scaffolder` launch template and `TechDocs` read documentation icons have been extracted from the default `Catalog` about card links and are now provided respectively by the `Scaffolder` and `TechDocs` plugins in the new frontend system. It means that they will not be available unless you install the `TechDocs` and `Scaffolder` plugins. Also If you are using translation for these icon link titles other than the default, you should now translate them using the scaffolder translation reference or the TechDocs translation reference (the translation keys are still the same, `aboutCard.viewTechdocs` and `aboutCard.launchTemplate`). + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-search-react@1.9.1-next.1 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.34 + - @backstage/plugin-scaffolder-common@1.5.11 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + ## 1.31.0-next.1 ### Patch Changes diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 1e78d40306..1ea64c1e7a 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog", - "version": "1.31.0-next.1", + "version": "1.31.1-next.2", "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 4be914a69d..4e00a5185e 100644 --- a/plugins/catalog/report-alpha.api.md +++ b/plugins/catalog/report-alpha.api.md @@ -1012,9 +1012,26 @@ const _default: FrontendPlugin< }>; 'page:catalog': ExtensionDefinition<{ config: { + pagination: + | boolean + | { + mode: 'offset' | 'cursor'; + offset?: number | undefined; + limit?: number | undefined; + }; + } & { path: string | undefined; }; configInput: { + pagination?: + | boolean + | { + mode: 'offset' | 'cursor'; + offset?: number | undefined; + limit?: number | undefined; + } + | undefined; + } & { path?: string | undefined; }; output: diff --git a/plugins/catalog/src/alpha/entityCards.tsx b/plugins/catalog/src/alpha/entityCards.tsx index 3913f8b2b7..f99c5961a1 100644 --- a/plugins/catalog/src/alpha/entityCards.tsx +++ b/plugins/catalog/src/alpha/entityCards.tsx @@ -56,6 +56,11 @@ export const catalogAboutEntityCard = EntityCardBlueprint.makeWithOverrides({ } return originalFactory({ type: 'info', + filter: { + $not: { + kind: { $in: ['user', 'group'] }, + }, + }, async loader() { const { InternalAboutCard } = await import( '../components/AboutCard/AboutCard' @@ -72,7 +77,7 @@ export const catalogLinksEntityCard = EntityCardBlueprint.make({ name: 'links', params: { type: 'info', - filter: 'has:links', + filter: { 'metadata.links': { $exists: true } }, loader: async () => import('../components/EntityLinksCard').then(m => compatWrapper(), @@ -84,7 +89,7 @@ export const catalogLabelsEntityCard = EntityCardBlueprint.make({ name: 'labels', params: { type: 'info', - filter: 'has:labels', + filter: { 'metadata.labels': { $exists: true } }, loader: async () => import('../components/EntityLabelsCard').then(m => compatWrapper(), @@ -95,7 +100,7 @@ export const catalogLabelsEntityCard = EntityCardBlueprint.make({ export const catalogDependsOnComponentsEntityCard = EntityCardBlueprint.make({ name: 'depends-on-components', params: { - filter: 'kind:component', + filter: { kind: 'component' }, loader: async () => import('../components/DependsOnComponentsCard').then(m => compatWrapper(), @@ -106,7 +111,7 @@ export const catalogDependsOnComponentsEntityCard = EntityCardBlueprint.make({ export const catalogDependsOnResourcesEntityCard = EntityCardBlueprint.make({ name: 'depends-on-resources', params: { - filter: 'kind:component', + filter: { kind: 'component' }, loader: async () => import('../components/DependsOnResourcesCard').then(m => compatWrapper(), @@ -117,7 +122,7 @@ export const catalogDependsOnResourcesEntityCard = EntityCardBlueprint.make({ export const catalogHasComponentsEntityCard = EntityCardBlueprint.make({ name: 'has-components', params: { - filter: 'kind:system', + filter: { kind: 'system' }, loader: async () => import('../components/HasComponentsCard').then(m => compatWrapper(), @@ -128,7 +133,7 @@ export const catalogHasComponentsEntityCard = EntityCardBlueprint.make({ export const catalogHasResourcesEntityCard = EntityCardBlueprint.make({ name: 'has-resources', params: { - filter: 'kind:system', + filter: { kind: 'system' }, loader: async () => import('../components/HasResourcesCard').then(m => compatWrapper(), @@ -139,7 +144,7 @@ export const catalogHasResourcesEntityCard = EntityCardBlueprint.make({ export const catalogHasSubcomponentsEntityCard = EntityCardBlueprint.make({ name: 'has-subcomponents', params: { - filter: 'kind:component', + filter: { kind: 'component' }, loader: async () => import('../components/HasSubcomponentsCard').then(m => compatWrapper(), @@ -150,7 +155,7 @@ export const catalogHasSubcomponentsEntityCard = EntityCardBlueprint.make({ export const catalogHasSubdomainsEntityCard = EntityCardBlueprint.make({ name: 'has-subdomains', params: { - filter: 'kind:domain', + filter: { kind: 'domain' }, loader: async () => import('../components/HasSubdomainsCard').then(m => compatWrapper(), @@ -161,7 +166,7 @@ export const catalogHasSubdomainsEntityCard = EntityCardBlueprint.make({ export const catalogHasSystemsEntityCard = EntityCardBlueprint.make({ name: 'has-systems', params: { - filter: 'kind:domain', + filter: { kind: 'domain' }, loader: async () => import('../components/HasSystemsCard').then(m => compatWrapper(), diff --git a/plugins/catalog/src/alpha/pages.tsx b/plugins/catalog/src/alpha/pages.tsx index c1f5b55341..81ec0c5cb9 100644 --- a/plugins/catalog/src/alpha/pages.tsx +++ b/plugins/catalog/src/alpha/pages.tsx @@ -41,7 +41,22 @@ export const catalogPage = PageBlueprint.makeWithOverrides({ inputs: { filters: createExtensionInput([coreExtensionData.reactElement]), }, - factory(originalFactory, { inputs }) { + config: { + schema: { + pagination: z => + z + .union([ + z.boolean(), + z.object({ + mode: z.enum(['cursor', 'offset']), + limit: z.number().optional(), + offset: z.number().optional(), + }), + ]) + .default(true), + }, + }, + factory(originalFactory, { inputs, config }) { return originalFactory({ defaultPath: '/catalog', routeRef: convertLegacyRouteRef(rootRouteRef), @@ -50,7 +65,12 @@ export const catalogPage = PageBlueprint.makeWithOverrides({ const filters = inputs.filters.map(filter => filter.get(coreExtensionData.reactElement), ); - return compatWrapper({filters}} />); + return compatWrapper( + {filters}} + pagination={config.pagination} + />, + ); }, }); }, diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index ff063bd996..fb09c8bd0d 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-config-schema +## 0.1.70-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.1.70-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.1.69 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.1.69-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.1.68 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 4450aec9db..237493919e 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-config-schema", - "version": "0.1.68", + "version": "0.1.70-next.1", "description": "A Backstage plugin that lets you browse the configuration schema of your app", "backstage": { "role": "frontend-plugin", diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index 0a11e19ddb..10c7787fd7 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,67 @@ # @backstage/plugin-devtools-backend +## 0.5.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.10.2-next.0 + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-devtools-common@0.1.17-next.0 + +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config-loader@1.10.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-common@0.9.0 + +## 0.5.6 + +### Patch Changes + +- 9a99735: Removed the usage of `permissionIntegrationRouter` in favor of using the new `coreServices.permissionsRegistry` +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + +## 0.5.6-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/cli-common@0.1.15 + - @backstage/config@1.3.2 + - @backstage/config-loader@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1-next.1 + ## 0.5.6-next.1 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index 0547b8bc9a..aa8526c2bf 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.5.6-next.1", + "version": "0.5.7-next.1", "backstage": { "role": "backend-plugin", "pluginId": "devtools", diff --git a/plugins/devtools-backend/src/plugin.ts b/plugins/devtools-backend/src/plugin.ts index ee7bf369f5..d3fefda343 100644 --- a/plugins/devtools-backend/src/plugin.ts +++ b/plugins/devtools-backend/src/plugin.ts @@ -19,6 +19,7 @@ import { createBackendPlugin, } from '@backstage/backend-plugin-api'; import { createRouter } from './service/router'; +import { devToolsPermissions } from '@backstage/plugin-devtools-common'; /** * DevTools backend plugin @@ -36,6 +37,7 @@ export const devtoolsPlugin = createBackendPlugin({ httpRouter: coreServices.httpRouter, discovery: coreServices.discovery, httpAuth: coreServices.httpAuth, + permissionsRegistry: coreServices.permissionsRegistry, }, async init({ config, @@ -44,6 +46,7 @@ export const devtoolsPlugin = createBackendPlugin({ httpRouter, discovery, httpAuth, + permissionsRegistry, }) { httpRouter.use( await createRouter({ @@ -58,6 +61,7 @@ export const devtoolsPlugin = createBackendPlugin({ path: '/health', allow: 'unauthenticated', }); + permissionsRegistry.addPermissions(devToolsPermissions); }, }); }, diff --git a/plugins/devtools-backend/src/service/router.ts b/plugins/devtools-backend/src/service/router.ts index ad77495b36..caba2185fc 100644 --- a/plugins/devtools-backend/src/service/router.ts +++ b/plugins/devtools-backend/src/service/router.ts @@ -19,13 +19,11 @@ import { devToolsConfigReadPermission, devToolsExternalDependenciesReadPermission, devToolsInfoReadPermission, - devToolsPermissions, } from '@backstage/plugin-devtools-common'; import { DevToolsBackendApi } from '../api'; import { NotAllowedError } from '@backstage/errors'; import Router from 'express-promise-router'; import express from 'express'; -import { createPermissionIntegrationRouter } from '@backstage/plugin-permission-node'; import { DiscoveryService, HttpAuthService, @@ -59,11 +57,6 @@ export async function createRouter( const router = Router(); router.use(express.json()); - router.use( - createPermissionIntegrationRouter({ - permissions: devToolsPermissions, - }), - ); router.get('/health', (_req, res) => { res.status(200).json({ status: 'ok' }); diff --git a/plugins/devtools-common/CHANGELOG.md b/plugins/devtools-common/CHANGELOG.md index 11007945ce..b728c974a4 100644 --- a/plugins/devtools-common/CHANGELOG.md +++ b/plugins/devtools-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-devtools-common +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/devtools-common/package.json b/plugins/devtools-common/package.json index 52d7bb27d0..bb8a3ac002 100644 --- a/plugins/devtools-common/package.json +++ b/plugins/devtools-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-common", - "version": "0.1.16", + "version": "0.1.17-next.0", "description": "Common functionalities for the devtools plugin", "backstage": { "role": "common-library", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index 7ae5262c69..0753a9c434 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-devtools +## 0.1.29-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-devtools-common@0.1.17-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.1.29-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-react@0.4.35 + +## 0.1.28 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-react@0.4.35 + +## 0.1.28-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/plugin-devtools-common@0.1.16 + - @backstage/plugin-permission-react@0.4.34 + ## 0.1.28-next.1 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 32ac310d53..d0129a11ef 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.28-next.1", + "version": "0.1.29-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "devtools", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 6372fb6b46..f570924fa7 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.4.12 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + ## 0.4.12-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index 7cd59759cc..bda5997791 100644 --- a/plugins/events-backend-module-aws-sqs/package.json +++ b/plugins/events-backend-module-aws-sqs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-aws-sqs", - "version": "0.4.12-next.1", + "version": "0.4.13-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index 791a72a413..4ffa759715 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-azure +## 0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.2.21 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + ## 0.2.21-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 3ccc5436db..2a6745dd81 100644 --- a/plugins/events-backend-module-azure/package.json +++ b/plugins/events-backend-module-azure/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-azure", - "version": "0.2.21-next.1", + "version": "0.2.22-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index c56b358dfa..e1350c02c1 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.2.21 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + ## 0.2.21-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index 68d6823859..e43de98178 100644 --- a/plugins/events-backend-module-bitbucket-cloud/package.json +++ b/plugins/events-backend-module-bitbucket-cloud/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-cloud", - "version": "0.2.21-next.1", + "version": "0.2.22-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md index 7e285f28f8..81d3b484d4 100644 --- a/plugins/events-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-bitbucket-server +## 0.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + ## 0.1.2-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-server/package.json b/plugins/events-backend-module-bitbucket-server/package.json index 501088fd74..b968358168 100644 --- a/plugins/events-backend-module-bitbucket-server/package.json +++ b/plugins/events-backend-module-bitbucket-server/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-bitbucket-server", - "version": "0.1.2-next.1", + "version": "0.1.3-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index b0e7a64255..ff06160cf1 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.2.21 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + ## 0.2.21-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index 33dfe31b19..a049006546 100644 --- a/plugins/events-backend-module-gerrit/package.json +++ b/plugins/events-backend-module-gerrit/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gerrit", - "version": "0.2.21-next.1", + "version": "0.2.22-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index 1521413713..74f3f99635 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-events-backend-module-github +## 0.4.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.4.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-events-node@0.4.12 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + +## 0.4.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + ## 0.4.1-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index ebfefa8965..c7dc71638d 100644 --- a/plugins/events-backend-module-github/package.json +++ b/plugins/events-backend-module-github/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-github", - "version": "0.4.1-next.1", + "version": "0.4.2-next.1", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 7941142c71..68294555d0 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.3.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.3.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/plugin-events-node@0.4.12 + ## 0.3.2-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 23b90b6832..a731c520ea 100644 --- a/plugins/events-backend-module-gitlab/package.json +++ b/plugins/events-backend-module-gitlab/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-gitlab", - "version": "0.3.2-next.1", + "version": "0.3.3-next.0", "backstage": { "role": "backend-plugin-module", "pluginId": "events", diff --git a/plugins/events-backend-module-google-pubsub/CHANGELOG.md b/plugins/events-backend-module-google-pubsub/CHANGELOG.md index aee8ff1327..f28e763d72 100644 --- a/plugins/events-backend-module-google-pubsub/CHANGELOG.md +++ b/plugins/events-backend-module-google-pubsub/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-events-backend-module-google-pubsub +## 0.1.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.1 + +### Patch Changes + +- f983e99: Add an `EventConsumingGooglePubSubPublisher`, for pushing Backstage events to pubsub +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + ## 0.1.1-next.1 ### Patch Changes diff --git a/plugins/events-backend-module-google-pubsub/README.md b/plugins/events-backend-module-google-pubsub/README.md index 67ddcfacb4..9752fa30c6 100644 --- a/plugins/events-backend-module-google-pubsub/README.md +++ b/plugins/events-backend-module-google-pubsub/README.md @@ -19,11 +19,24 @@ events: # The fully qualified name of the subscription subscriptionName: 'projects/my-google-project/subscriptions/github-enterprise-events' # The event system topic to transfer to. This can also be just a plain string - targetTopic: - # This example picks the topic name from a message attribute + a prefix - fromMessageAttribute: - attributeName: 'x-github-event' - withPrefix: 'github.' + targetTopic: 'github.{{ event.attributes.x-github-event }}' +``` + +The following configuration enables the transfer of events from a Backstage events topic into a Google +Pub/Sub topic. + +```yaml +events: + modules: + googlePubSub: + eventConsumingGooglePubSubPublisher: + subscriptions: + # A unique key for your subscription, to be used in logging and metrics + mySubscription: + # The source topic (or array of topics) + sourceTopic: 'github' + # The fully qualified name of the target topic + targetTopicName: 'projects/my-google-project/topics/github-enterprise-events' ``` ## Installation diff --git a/plugins/events-backend-module-google-pubsub/config.d.ts b/plugins/events-backend-module-google-pubsub/config.d.ts index 3e70489b9d..1097b71e00 100644 --- a/plugins/events-backend-module-google-pubsub/config.d.ts +++ b/plugins/events-backend-module-google-pubsub/config.d.ts @@ -90,6 +90,68 @@ export interface Config { }; }; }; + + /** + * Configuration for `EventConsumingGooglePubSubPublisher`, which + * consumes messages from the Backstage events system and forwards them + * into Google Pub/Sub topics. + */ + eventConsumingGooglePubSubPublisher?: { + subscriptions: { + [name: string]: { + /** + * The name of the events backend topic(s) that messages are + * consumed from. + */ + sourceTopic: string | string[]; + + /** + * The complete name of the Google Pub/Sub subscription to forward + * events to, on the form + * `projects/PROJECT_ID/topics/TOPIC_ID`. + * + * The value can contain placeholders on the form `{{ + * message.attributes.foo }}`, to mirror attribute `foo` as the + * whole or part of the topic name. + * + * @example + * + * This example expects the events topic to contain GitHub + * webhook events where the HTTP headers were mapped into + * event metadata fields. The outcome should be that messages + * end up on event topics such as `github.push`, + * `github.repository` etc which matches the [`@backstage/plugin-events-backend-module-github`](https://github.com/backstage/backstage/tree/master/plugins/events-backend-module-github) structure. + * + * ```yaml + * targetTopic: 'projects/my-project/topics/github.{{ event.metadata.x-github-event }}' + * ``` + */ + targetTopicName: string; + + /** + * Event metadata fields are by default copied to the Pub/Sub + * message attribute. This setting allows you to override or amend + * those attributes. + * + * @remarks + * + * The values can contain placeholders on the form `{{ event.metadata.foo }}`, + * to mirror metadata field `foo` as the whole or part of a + * message attribute value. + * + * @example + * + * ```yaml + * messageAttributes: + * x-gitHub-event: '{{ event.metadata.event }}' + * ``` + */ + messageAttributes?: { + [key: string]: string; + }; + }; + }; + }; }; }; }; diff --git a/plugins/events-backend-module-google-pubsub/dev/index.ts b/plugins/events-backend-module-google-pubsub/dev/index.ts index 752217e6db..78a7e775cf 100644 --- a/plugins/events-backend-module-google-pubsub/dev/index.ts +++ b/plugins/events-backend-module-google-pubsub/dev/index.ts @@ -28,20 +28,21 @@ backend.add(import('../src')); backend.add( createBackendPlugin({ - pluginId: 'example-event-consumer', + pluginId: 'example-event-drivers', register(reg) { reg.registerInit({ deps: { events: eventsServiceRef, logger: coreServices.logger, + scheduler: coreServices.scheduler, }, - async init({ events, logger }) { - events.subscribe({ - id: 'example-subscription', - topics: ['example-topic'], + async init({ events, logger, scheduler }) { + await events.subscribe({ + id: 'inbox-subscription', + topics: ['inbox-topic'], async onEvent(event) { logger.info( - `Received event: ${JSON.stringify( + `Received inbox event: ${JSON.stringify( { topic: event.topic, payload: event.eventPayload, @@ -53,6 +54,24 @@ backend.add( ); }, }); + + await scheduler.scheduleTask({ + id: 'outbox-task', + scope: 'local', + frequency: { seconds: 5 }, + timeout: { seconds: 5 }, + fn: async () => { + await events.publish({ + topic: 'outbox-topic', + eventPayload: { + message: 'Hello, world!', + }, + metadata: { + source: 'outbox-task', + }, + }); + }, + }); }, }); }, diff --git a/plugins/events-backend-module-google-pubsub/package.json b/plugins/events-backend-module-google-pubsub/package.json index 591067de48..38f93e6e34 100644 --- a/plugins/events-backend-module-google-pubsub/package.json +++ b/plugins/events-backend-module-google-pubsub/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-module-google-pubsub", - "version": "0.1.1-next.1", + "version": "0.1.2-next.0", "description": "The google-pubsub backend module for the events plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/events-backend-module-google-pubsub/report.api.md b/plugins/events-backend-module-google-pubsub/report.api.md index bdb057d7fc..b42c1f80ff 100644 --- a/plugins/events-backend-module-google-pubsub/report.api.md +++ b/plugins/events-backend-module-google-pubsub/report.api.md @@ -6,6 +6,6 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; // @public -const eventsModuleGooglePubsubConsumingEventPublisher: BackendFeature; -export default eventsModuleGooglePubsubConsumingEventPublisher; +const _default: BackendFeature; +export default _default; ``` diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.test.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.test.ts new file mode 100644 index 0000000000..e5af68add7 --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.test.ts @@ -0,0 +1,111 @@ +/* + * 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 { mockServices } from '@backstage/backend-test-utils'; +import { EventParams } from '@backstage/plugin-events-node'; +import { PubSub } from '@google-cloud/pubsub'; +import waitFor from 'wait-for-expect'; +import { EventConsumingGooglePubSubPublisher } from './EventConsumingGooglePubSubPublisher'; + +describe('EventConsumingGooglePubSubPublisher', () => { + const logger = mockServices.logger.mock(); + const events = mockServices.events.mock(); + + let onEvent: undefined | ((event: EventParams) => void); + events.subscribe.mockImplementation(async options => { + onEvent = options.onEvent; + }); + + const topic = { + publishMessage: jest.fn(), + }; + const pubSub = { + close: jest.fn(), + topic: jest.fn(() => topic), + }; + const pubSubFactory = jest.fn(() => pubSub as unknown as PubSub); + + beforeEach(() => { + onEvent = undefined; + jest.clearAllMocks(); + }); + + it('should go though the expected registration and flows', async () => { + const publisher = new EventConsumingGooglePubSubPublisher({ + logger, + events, + tasks: [ + { + id: 'my-id', + sourceTopics: ['my-topic'], + targetTopicPattern: 'projects/my-project/topics/my-topic', + mapToTopic: () => ({ project: 'my-project', topic: 'my-topic' }), + mapToAttributes: m => ({ ...m.metadata, more: 'yes' }), + }, + ], + pubSubFactory, + }); + + // Start up + + await publisher.start(); + + expect(events.subscribe).toHaveBeenCalledWith({ + id: 'EventConsumingGooglePubSubPublisher.my-id', + topics: ['my-topic'], + onEvent: expect.any(Function), + }); + + // Publish successfully + + onEvent!({ + topic: 'my-topic', + eventPayload: { foo: 'bar' }, + metadata: { extra: 'data', more: 'yes' }, + }); + + await waitFor(() => { + expect(pubSubFactory).toHaveBeenCalledWith('my-project'); + expect(pubSub.topic).toHaveBeenCalledWith('my-topic'); + expect(topic.publishMessage).toHaveBeenCalledWith({ + json: { foo: 'bar' }, + attributes: { extra: 'data', more: 'yes' }, + }); + }); + + // Simulate a failed publish + + topic.publishMessage.mockRejectedValueOnce(new Error('Failed to publish')); + + await expect( + onEvent!({ + topic: 'my-topic', + eventPayload: { foo: 'bar' }, + metadata: { extra: 'data', more: 'yes' }, + }), + ).rejects.toThrowErrorMatchingInlineSnapshot(`"Failed to publish"`); + + expect(logger.error).toHaveBeenCalledWith( + 'Error publishing Google Pub/Sub message', + new Error('Failed to publish'), + ); + + // Shut down + + await publisher.stop(); + expect(pubSub.close).toHaveBeenCalled(); + }); +}); diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.ts new file mode 100644 index 0000000000..4a88bc2e79 --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/EventConsumingGooglePubSubPublisher.ts @@ -0,0 +1,153 @@ +/* + * 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 { + LoggerService, + RootConfigService, + RootLifecycleService, +} from '@backstage/backend-plugin-api'; +import { EventsService } from '@backstage/plugin-events-node'; +import { PubSub } from '@google-cloud/pubsub'; +import { Counter, metrics } from '@opentelemetry/api'; +import { readSubscriptionTasksFromConfig } from './config'; +import { SubscriptionTask } from './types'; + +/** + * Reads messages off of the events system and forwards them into Google Pub/Sub + * topics. + */ +export class EventConsumingGooglePubSubPublisher { + readonly #logger: LoggerService; + readonly #events: EventsService; + readonly #tasks: SubscriptionTask[]; + readonly #pubSubFactory: (projectId: string) => PubSub; + readonly #metrics: { messages: Counter }; + #activeClientsByProjectId: Map; + + static create(options: { + config: RootConfigService; + logger: LoggerService; + rootLifecycle: RootLifecycleService; + events: EventsService; + }) { + const publisher = new EventConsumingGooglePubSubPublisher({ + logger: options.logger, + events: options.events, + tasks: readSubscriptionTasksFromConfig(options.config), + pubSubFactory: projectId => new PubSub({ projectId }), + }); + + options.rootLifecycle.addStartupHook(async () => { + await publisher.start(); + }); + + options.rootLifecycle.addBeforeShutdownHook(async () => { + await publisher.stop(); + }); + + return publisher; + } + + constructor(options: { + logger: LoggerService; + events: EventsService; + tasks: SubscriptionTask[]; + pubSubFactory: (projectId: string) => PubSub; + }) { + this.#logger = options.logger; + this.#events = options.events; + this.#tasks = options.tasks; + this.#pubSubFactory = options.pubSubFactory; + + const meter = metrics.getMeter('default'); + this.#metrics = { + messages: meter.createCounter( + 'events.google.pubsub.publisher.messages.total', + { + description: + 'Number of Pub/Sub messages sent by EventConsumingGooglePubSubPublisher', + unit: 'short', + }, + ), + }; + + this.#activeClientsByProjectId = new Map(); + } + + async start() { + for (const task of this.#tasks) { + this.#logger.info( + `Starting publisher: id=${ + task.id + } sourceTopics=${task.sourceTopics.join(',')} targetTopic=${ + task.targetTopicPattern + }`, + ); + + await this.#events.subscribe({ + id: `EventConsumingGooglePubSubPublisher.${task.id}`, + topics: task.sourceTopics, + onEvent: async event => { + let status: 'success' | 'failed' | 'ignored' = 'failed'; + try { + const topic = task.mapToTopic(event); + if (!topic) { + status = 'ignored'; + return; + } + + let pubsub = this.#activeClientsByProjectId.get(topic.project); + if (!pubsub) { + pubsub = this.#pubSubFactory(topic.project); + this.#activeClientsByProjectId.set(topic.project, pubsub); + } + + await pubsub.topic(topic.topic).publishMessage({ + json: event.eventPayload, + attributes: task.mapToAttributes(event), + }); + + status = 'success'; + } catch (error) { + this.#logger.error( + 'Error publishing Google Pub/Sub message', + error, + ); + status = 'failed'; + throw error; + } finally { + this.#metrics.messages.add(1, { + subscription: task.id, + status: status, + }); + } + }, + }); + } + } + + async stop() { + const clients = Array.from(this.#activeClientsByProjectId.values()); + this.#activeClientsByProjectId = new Map(); + + await Promise.allSettled( + clients.map(async client => { + this.#logger.info(`Closing Google Pub/Sub client: ${client.projectId}`); + await client.close(); + }), + ); + } +} diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.test.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.test.ts new file mode 100644 index 0000000000..c3a5c1a5bb --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.test.ts @@ -0,0 +1,196 @@ +/* + * 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 { mockServices } from '@backstage/backend-test-utils'; +import { readSubscriptionTasksFromConfig } from './config'; + +describe('readSubscriptionTasksFromConfig', () => { + it('reads with basic targetTopic', () => { + const data = { + events: { + modules: { + googlePubSub: { + eventConsumingGooglePubSubPublisher: { + subscriptions: { + subKey1: { + sourceTopic: 'my-topic', + targetTopicName: 'projects/pid/topics/tid', + }, + subKey2: { + sourceTopic: ['my-topic-1', 'my-topic-2'], + targetTopicName: 'projects/pid/topics/tid.{{ event.topic }}', + }, + }, + }, + }, + }, + }, + }; + + const result = readSubscriptionTasksFromConfig( + mockServices.rootConfig({ data }), + ); + + expect(result).toEqual([ + { + id: 'subKey1', + sourceTopics: ['my-topic'], + targetTopicPattern: 'projects/pid/topics/tid', + mapToTopic: expect.any(Function), + mapToAttributes: expect.any(Function), + }, + { + id: 'subKey2', + sourceTopics: ['my-topic-1', 'my-topic-2'], + targetTopicPattern: 'projects/pid/topics/tid.{{ event.topic }}', + mapToTopic: expect.any(Function), + mapToAttributes: expect.any(Function), + }, + ]); + + expect( + result[0].mapToTopic({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { attr: 'yes' }, + }), + ).toEqual({ project: 'pid', topic: 'tid' }); + expect( + result[0].mapToAttributes({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { attr: 'yes' }, + }), + ).toEqual({ attr: 'yes' }); + }); + + it('fills in placeholders', () => { + const data = { + events: { + modules: { + googlePubSub: { + eventConsumingGooglePubSubPublisher: { + subscriptions: { + sub1: { + sourceTopic: 'my-topic', + targetTopicName: 'projects/pid/topics/tid.{{ event.topic }}', + messageAttributes: { + attr1: 'updated.{{ event.metadata.exists }}', + attr2: 'updated.{{ event.metadata.missing }}', + }, + }, + sub2: { + sourceTopic: 'my-topic', + targetTopicName: + 'projects/pid/topics/tid.{{ event.metadata.missing }}', + messageAttributes: { + attr3: 'new', + }, + }, + }, + }, + }, + }, + }, + }; + + const result = readSubscriptionTasksFromConfig( + mockServices.rootConfig({ data }), + ); + + expect(result).toEqual([ + { + id: 'sub1', + sourceTopics: ['my-topic'], + targetTopicPattern: 'projects/pid/topics/tid.{{ event.topic }}', + mapToTopic: expect.any(Function), + mapToAttributes: expect.any(Function), + }, + { + id: 'sub2', + sourceTopics: ['my-topic'], + targetTopicPattern: + 'projects/pid/topics/tid.{{ event.metadata.missing }}', + mapToTopic: expect.any(Function), + mapToAttributes: expect.any(Function), + }, + ]); + + expect( + result[0].mapToTopic({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { exists: 'exists', attr1: 'original1', attr2: 'original2' }, + }), + ).toEqual({ project: 'pid', topic: 'tid.a' }); // Message attribute existed, successfully routed + expect( + result[0].mapToAttributes({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { exists: 'exists', attr1: 'original1', attr2: 'original2' }, + }), + ).toEqual({ + exists: 'exists', + attr1: 'updated.exists', // message attribute existed, was replaced + attr2: 'original2', // message attribute did not exist, was not replaced + }); + + expect( + result[1].mapToTopic({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { exists: 'exists', attr1: 'original1', attr2: 'original2' }, + }), + ).toBeUndefined(); // Message attribute did not exist, could not be routed + expect( + result[1].mapToAttributes({ + topic: 'a', + eventPayload: { foo: 'bar' }, + metadata: { exists: 'exists', attr1: 'original1', attr2: 'original2' }, + }), + ).toEqual({ + exists: 'exists', + attr1: 'original1', + attr2: 'original2', + attr3: 'new', + }); + }); + + it('rejects malformed subscription name', () => { + const data = { + events: { + modules: { + googlePubSub: { + eventConsumingGooglePubSubPublisher: { + subscriptions: { + subKey: { + sourceTopic: 'sid', + targetTopicName: 'foo', + }, + }, + }, + }, + }, + }, + }; + + expect(() => + readSubscriptionTasksFromConfig(mockServices.rootConfig({ data })), + ).toThrowErrorMatchingInlineSnapshot( + `"Expected Google Pub/Sub 'targetTopicName' to be on the form 'projects/PROJECT_ID/topics/TOPIC_ID' but got 'foo'"`, + ); + }); +}); diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.ts new file mode 100644 index 0000000000..7ccd78cfcb --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/config.ts @@ -0,0 +1,146 @@ +/* + * 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 { RootConfigService } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import { InputError } from '@backstage/errors'; +import { EventParams } from '@backstage/plugin-events-node'; +import { createPatternResolver } from '../util/createPatternResolver'; +import { SubscriptionTask } from './types'; + +export function readSubscriptionTasksFromConfig( + rootConfig: RootConfigService, +): SubscriptionTask[] { + const subscriptionsConfig = rootConfig.getOptionalConfig( + 'events.modules.googlePubSub.eventConsumingGooglePubSubPublisher.subscriptions', + ); + if (!subscriptionsConfig) { + return []; + } + + return subscriptionsConfig.keys().map(subscriptionId => { + if (!subscriptionId.match(/^[-_\w]+$/)) { + throw new InputError( + `Expected Google Pub/Sub subscription ID to consist of letters, numbers, dashes and underscores, but got '${subscriptionId}'`, + ); + } + + const config = subscriptionsConfig.getConfig(subscriptionId); + const sourceTopics = readSourceTopics(config); + const mapToTopic = readTopicMapper(config); + const mapToAttributes = readAttributeMapper(config); + + return { + id: subscriptionId, + sourceTopics: sourceTopics, + targetTopicPattern: config.getString('targetTopicName'), + mapToTopic, + mapToAttributes, + }; + }); +} + +function readSourceTopics(config: Config): string[] { + if (Array.isArray(config.getOptional('sourceTopic'))) { + return config.getStringArray('sourceTopic'); + } + return [config.getString('sourceTopic')]; +} + +/** + * Handles the `targetTopicName` configuration field. + */ +function readTopicMapper( + config: Config, +): (event: EventParams) => { project: string; topic: string } | undefined { + const regex = /^projects\/([^/]+)\/topics\/(.+)$/; + + const targetTopicPattern = config.getString('targetTopicName'); + let parts = targetTopicPattern.match(regex); + if (!parts) { + throw new InputError( + `Expected Google Pub/Sub 'targetTopicName' to be on the form 'projects/PROJECT_ID/topics/TOPIC_ID' but got '${targetTopicPattern}'`, + ); + } + + const patternResolver = createPatternResolver(targetTopicPattern); + + return event => { + try { + parts = patternResolver({ event }).match(regex); + if (!parts) { + return undefined; + } + return { + project: parts[1], + topic: parts[2], + }; + } catch { + // could not map to a topic + return undefined; + } + }; +} + +/** + * Handles the `messageAttributes` configuration field. + */ +function readAttributeMapper( + config: Config, +): (event: EventParams) => Record { + const setters = new Array< + (options: { + event: EventParams; + attributes: Record; + }) => void + >(); + + const eventMetadata = config.getOptionalConfig('messageAttributes'); + if (eventMetadata) { + for (const key of eventMetadata?.keys() ?? []) { + const valuePattern = eventMetadata.getString(key); + const patternResolver = createPatternResolver(valuePattern); + setters.push(({ event, attributes }) => { + try { + const value = patternResolver({ event }); + if (value) { + attributes[key] = value; + } + } catch { + // ignore silently, keep original + } + }); + } + } + + return event => { + const result: Record = {}; + for (const [key, value] of Object.entries(event.metadata ?? {})) { + if (value) { + if (typeof value === 'string') { + result[key] = value; + } else if (Array.isArray(value) && value.length > 0) { + // Google Pub/Sub does not support array values + result[key] = value.join(','); + } + } + } + for (const setter of setters) { + setter({ event, attributes: result }); + } + return result; + }; +} diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/index.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/index.ts new file mode 100644 index 0000000000..3e5713a28d --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { eventsModuleEventConsumingGooglePubSubPublisher } from './module'; diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/module.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/module.ts new file mode 100644 index 0000000000..498996fee0 --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/module.ts @@ -0,0 +1,52 @@ +/* + * 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 { eventsServiceRef } from '@backstage/plugin-events-node'; +import { EventConsumingGooglePubSubPublisher } from './EventConsumingGooglePubSubPublisher'; + +/** + * Reads messages off of the events system and forwards them into Google Pub/Sub + * topics. + * + * @public + */ +export const eventsModuleEventConsumingGooglePubSubPublisher = + createBackendModule({ + pluginId: 'events', + moduleId: 'event-consuming-google-pubsub-publisher', + register(reg) { + reg.registerInit({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, + rootLifecycle: coreServices.rootLifecycle, + events: eventsServiceRef, + }, + async init({ config, logger, rootLifecycle, events }) { + EventConsumingGooglePubSubPublisher.create({ + config, + logger, + rootLifecycle, + events, + }); + }, + }); + }, + }); diff --git a/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/types.ts b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/types.ts new file mode 100644 index 0000000000..36b3aeaa78 --- /dev/null +++ b/plugins/events-backend-module-google-pubsub/src/EventConsumingGooglePubSubPublisher/types.ts @@ -0,0 +1,30 @@ +/* + * 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 { EventParams } from '@backstage/plugin-events-node'; + +/** + * A configured subscription task. + */ +export interface SubscriptionTask { + id: string; + sourceTopics: string[]; + targetTopicPattern: string; + mapToTopic: ( + event: EventParams, + ) => { project: string; topic: string } | undefined; + mapToAttributes: (event: EventParams) => Record; +} diff --git a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.test.ts b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.test.ts index bfe3c20773..b4034123f0 100644 --- a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.test.ts +++ b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.test.ts @@ -188,7 +188,7 @@ describe('readSubscriptionTasksFromConfig', () => { expect(() => readSubscriptionTasksFromConfig(mockServices.rootConfig({ data })), ).toThrowErrorMatchingInlineSnapshot( - `"Expected Googoe Pub/Sub 'subscriptionName' to be on the form 'projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID' but got 'sid'"`, + `"Expected Google Pub/Sub 'subscriptionName' to be on the form 'projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID' but got 'sid'"`, ); }); }); diff --git a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.ts b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.ts index fdcab2e2e5..d8dfb2ada2 100644 --- a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.ts +++ b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/config.ts @@ -34,7 +34,7 @@ export function readSubscriptionTasksFromConfig( return subscriptionsConfig.keys().map(subscriptionId => { if (!subscriptionId.match(/^[-_\w]+$/)) { throw new InputError( - `Expected Googoe Pub/Sub subscription ID to consist of letters, numbers, dashes and underscores, but got '${subscriptionId}'`, + `Expected Google Pub/Sub subscription ID to consist of letters, numbers, dashes and underscores, but got '${subscriptionId}'`, ); } @@ -63,7 +63,7 @@ function readSubscriptionName(config: Config): { ); if (!parts) { throw new InputError( - `Expected Googoe Pub/Sub 'subscriptionName' to be on the form 'projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID' but got '${subscriptionName}'`, + `Expected Google Pub/Sub 'subscriptionName' to be on the form 'projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID' but got '${subscriptionName}'`, ); } return { diff --git a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/module.ts b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/module.ts index 14cfdd2bd8..3d5fce14ee 100644 --- a/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/module.ts +++ b/plugins/events-backend-module-google-pubsub/src/GooglePubSubConsumingEventPublisher/module.ts @@ -24,8 +24,6 @@ import { GooglePubSubConsumingEventPublisher } from './GooglePubSubConsumingEven /** * Reads messages off of Google Pub/Sub subscriptions and forwards them into the * Backstage events system. - * - * @public */ export const eventsModuleGooglePubsubConsumingEventPublisher = createBackendModule({ diff --git a/plugins/events-backend-module-google-pubsub/src/index.ts b/plugins/events-backend-module-google-pubsub/src/index.ts index 2b79fcb9a1..0dcbe3c5d8 100644 --- a/plugins/events-backend-module-google-pubsub/src/index.ts +++ b/plugins/events-backend-module-google-pubsub/src/index.ts @@ -14,10 +14,19 @@ * limitations under the License. */ +import { createBackendFeatureLoader } from '@backstage/backend-plugin-api'; +import { eventsModuleEventConsumingGooglePubSubPublisher } from './EventConsumingGooglePubSubPublisher'; +import { eventsModuleGooglePubsubConsumingEventPublisher } from './GooglePubSubConsumingEventPublisher'; + /** * The google-pubsub backend module for the events plugin. * * @packageDocumentation */ -export { eventsModuleGooglePubsubConsumingEventPublisher as default } from './GooglePubSubConsumingEventPublisher'; +export default createBackendFeatureLoader({ + *loader() { + yield eventsModuleGooglePubsubConsumingEventPublisher; + yield eventsModuleEventConsumingGooglePubSubPublisher; + }, +}); diff --git a/plugins/events-backend-module-kafka/.eslintrc.js b/plugins/events-backend-module-kafka/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/events-backend-module-kafka/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/events-backend-module-kafka/CHANGELOG.md b/plugins/events-backend-module-kafka/CHANGELOG.md new file mode 100644 index 0000000000..67e317ec7e --- /dev/null +++ b/plugins/events-backend-module-kafka/CHANGELOG.md @@ -0,0 +1,42 @@ +# @backstage/plugin-events-backend-module-kafka + +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.0 + +### Minor Changes + +- b034b9d: Adds a new module `kafka` for plugin-events-backend + + The module introduces the `KafkaConsumerClient` which creates a Kafka client used to establish consumer connections. It also provides the `KafkaConsumingEventPublisher`, a consumer that subscribes to configured Kafka topics and publishes received messages to the Event Service. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + +## 0.1.0-next.0 + +### Minor Changes + +- b034b9d: Adds a new module `kafka` for plugin-events-backend + + The module introduces the `KafkaConsumerClient` which creates a Kafka client used to establish consumer connections. It also provides the `KafkaConsumingEventPublisher`, a consumer that subscribes to configured Kafka topics and publishes received messages to the Event Service. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12-next.1 diff --git a/plugins/events-backend-module-kafka/README.md b/plugins/events-backend-module-kafka/README.md new file mode 100644 index 0000000000..dbc5bf5607 --- /dev/null +++ b/plugins/events-backend-module-kafka/README.md @@ -0,0 +1,78 @@ +# @backstage/backstage-plugin-events-backend-module-kafka + +Welcome to the `events-backend-module-kafka` backend module! + +This package is a module for the `events-backend` backend plugin and extends the events system with an `KafkaConsumingEventPublisher.` + +This event publisher will allow you to receive events from an Kafka queue and will publish these to the used `EventsService` implementation. + +## Configuration + +To set up Kafka queues, you need to configure the following values: + +```yaml +events: + modules: + kafka: + kafkaConsumingEventPublisher: + clientId: your-client-id # (Required) Client ID used by Backstage to identify when connecting to the Kafka cluster. + brokers: # (Required) List of brokers in the Kafka cluster to connect to. + - broker1 + - broker2 + topics: + - topic: 'backstage.topic' # (Required) Replace with actual topic name as expected by subscribers + kafka: + topics: # (Required) The Kafka topics to subscribe to. + - topic1 + groupId: your-group-id # (Required) The GroupId to be used by the topic consumers. +``` + +For a complete list of all available fields that can be configured, refer to the [config.d.ts file](./config.d.ts). + +### Optional SSL Configuration + +If your Kafka cluster requires SSL, you can configure it as follows: + +```yaml +events: + modules: + kafka: + kafkaConsumingEventPublisher: + ssl: + rejectUnauthorized: true # (Optional) If true, the server certificate is verified against the list of supplied CAs. + ca: [path/to/ca-cert] # (Optional) Array of trusted certificates in PEM format. + key: path/to/client-key # (Optional) Private key in PEM format. + cert: path/to/client-cert # (Optional) Public x509 certificate in PEM format. +``` + +### Optional SASL Authentication Configuration + +If your Kafka cluster requires `SASL` authentication, you can configure it as follows: + +```yaml +events: + modules: + kafka: + kafkaConsumingEventPublisher: + sasl: + mechanism: 'plain' # SASL mechanism ('plain', 'scram-sha-256' or 'scram-sha-512') + username: your-username # SASL username + password: your-password # SASL password +``` + +This section includes optional `SSL` and `SASL` authentication configuration for enhanced security. + +## Installation + +1. Install this module +2. Add your configuration. + +```bash +# From your Backstage root directory +yarn --cwd packages/backend add @backstage/plugin-events-backend-module-kafka +``` + +```typescript +// packages/backend/src/index.ts +backend.add(import('@backstage/plugin-events-backend-module-kafka')); +``` diff --git a/plugins/events-backend-module-kafka/catalog-info.yaml b/plugins/events-backend-module-kafka/catalog-info.yaml new file mode 100644 index 0000000000..72358ec3c3 --- /dev/null +++ b/plugins/events-backend-module-kafka/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-events-backend-module-kafka + title: '@backstage/plugin-events-backend-module-kafka' + description: The kafka backend module for the events plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/events-backend-module-kafka/config.d.ts b/plugins/events-backend-module-kafka/config.d.ts new file mode 100644 index 0000000000..49c417098c --- /dev/null +++ b/plugins/events-backend-module-kafka/config.d.ts @@ -0,0 +1,205 @@ +/* + * 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 { HumanDuration } from '@backstage/types'; + +export interface Config { + events?: { + modules?: { + /** + * events-backend-module-kafka plugin configuration. + */ + kafka?: { + /** + * Configuration for KafkaConsumingEventPublisher + */ + kafkaConsumingEventPublisher?: { + /** + * (Required) Client ID used by Backstage to identify when connecting to the Kafka cluster. + */ + clientId: string; + /** + * (Required) List of brokers in the Kafka cluster to connect to. + */ + brokers: string[]; + /** + * Optional SSL connection parameters to connect to the cluster. Passed directly to Node tls.connect. + * See https://nodejs.org/dist/latest-v8.x/docs/api/tls.html#tls_tls_createsecurecontext_options + */ + ssl?: + | { + ca?: string[]; + /** @visibility secret */ + key?: string; + cert?: string; + rejectUnauthorized?: boolean; + } + | boolean; + /** + * Optional SASL connection parameters. + */ + sasl?: { + mechanism: 'plain' | 'scram-sha-256' | 'scram-sha-512'; + username: string; + /** @visibility secret */ + password: string; + }; + + /** + * Optional retry connection parameters. + */ + retry: { + /** + * (Optional) Maximum wait time for a retry + * Default: 30000 ms. + */ + maxRetryTime: HumanDuration | string; + + /** + * (Optional) Initial value used to calculate the retry (This is still randomized following the randomization factor) + * Default: 300 ms. + */ + initialRetryTime: HumanDuration | string; + + /** + * (Optional) Randomization factor + * Default: 0.2. + */ + factor: number; + + /** + * (Optional) Exponential factor + * Default: 2. + */ + multiplier: number; + + /** + * (Optional) Max number of retries per call + * Default: 5. + */ + retries: number; + }; + + /** + * (Optional) Timeout for authentication requests. + * Default: 10000 ms. + */ + authenticationTimeout: HumanDuration | string; + + /** + * (Optional) Time to wait for a successful connection. + * Default: 1000 ms. + */ + connectionTimeout: HumanDuration | string; + + /** + * (Optional) Time to wait for a successful request. + * Default: 30000 ms. + */ + requestTimeout: HumanDuration | string; + + /** + * (Optional) The request timeout can be disabled by setting enforceRequestTimeout to false. + * Default: true + */ + enforceRequestTimeout: boolean; + + /** + * Contains a object per topic for which an Kafka queue + * should be used as source of events. + */ + topics: Array<{ + /** + * (Required) The Backstage topic to publish to + */ + topic: string; + /** + * (Required) KafkaConsumer-related configuration. + */ + kafka: { + /** + * (Required) The Kafka topics to subscribe to + */ + topics: string[]; + /** + * (Required) The GroupId to be used by the topic consumers + */ + groupId: string; + + /** + * (Optional) Timeout used to detect failures. + * The consumer sends periodic heartbeats to indicate its liveness to the broker. + * If no heartbeats are received by the broker before the expiration of this session timeout, + * then the broker will remove this consumer from the group and initiate a rebalance + * Default: 30000 ms. + */ + sessionTimeout: HumanDuration | string; + + /** + * (Optional) The maximum time that the coordinator will wait for each member to rejoin when rebalancing the group + * Default: 60000 ms. + */ + rebalanceTimeout: HumanDuration | string; + + /** + * (Optional) The expected time between heartbeats to the consumer coordinator. + * Heartbeats are used to ensure that the consumer's session stays active. + * The value must be set lower than session timeout + * Default: 3000 ms. + */ + heartbeatInterval: HumanDuration | string; + + /** + * (Optional) The period of time after which we force a refresh of metadata + * even if we haven't seen any partition leadership changes to proactively discover any new brokers or partitions + * Default: 300000 ms (5 minutes). + */ + metadataMaxAge: HumanDuration | string; + + /** + * (Optional) The maximum amount of data per-partition the server will return. + * This size must be at least as large as the maximum message size the server allows + * or else it is possible for the producer to send messages larger than the consumer can fetch. + * If that happens, the consumer can get stuck trying to fetch a large message on a certain partition + * Default: 1048576 (1MB) + */ + maxBytesPerPartition: number; + + /** + * (Optional) Minimum amount of data the server should return for a fetch request, otherwise wait up to maxWaitTime for more data to accumulate. + * Default: 1 + */ + minBytes: number; + + /** + * (Optional) Maximum amount of bytes to accumulate in the response. Supported by Kafka >= 0.10.1.0 + * Default: 10485760 (10MB) + */ + maxBytes: number; + + /** + * (Optional) The maximum amount of time the server will block before answering the fetch request + * if there isn’t sufficient data to immediately satisfy the requirement given by minBytes + * Default: 5000 + */ + maxWaitTime: HumanDuration | string; + }; + }>; + }; + }; + }; + }; +} diff --git a/plugins/events-backend-module-kafka/package.json b/plugins/events-backend-module-kafka/package.json new file mode 100644 index 0000000000..5e2f232f28 --- /dev/null +++ b/plugins/events-backend-module-kafka/package.json @@ -0,0 +1,51 @@ +{ + "name": "@backstage/plugin-events-backend-module-kafka", + "version": "0.1.1-next.0", + "description": "The kafka backend module for the events plugin.", + "backstage": { + "role": "backend-plugin-module", + "pluginId": "events", + "pluginPackage": "@backstage/plugin-events-backend" + }, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/events-backend-module-kafka" + }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist", + "config.d.ts" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/plugin-events-node": "workspace:^", + "@backstage/types": "workspace:^", + "kafkajs": "^2.2.4", + "luxon": "^3.0.0" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/plugin-events-backend-test-utils": "workspace:^" + }, + "configSchema": "config.d.ts" +} diff --git a/plugins/events-backend-module-kafka/report.api.md b/plugins/events-backend-module-kafka/report.api.md new file mode 100644 index 0000000000..a6b129b82e --- /dev/null +++ b/plugins/events-backend-module-kafka/report.api.md @@ -0,0 +1,11 @@ +## API Report File for "@backstage/plugin-events-backend-module-kafka" + +> 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'; + +// @public +const eventsModuleKafkaConsumingEventPublisher: BackendFeature; +export default eventsModuleKafkaConsumingEventPublisher; +``` diff --git a/plugins/events-backend-module-kafka/src/index.ts b/plugins/events-backend-module-kafka/src/index.ts new file mode 100644 index 0000000000..3ba6207d7d --- /dev/null +++ b/plugins/events-backend-module-kafka/src/index.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. + */ + +/** + * The module "kafka" for the Backstage backend plugin "events" + * adding an Kafka-based publisher, + * receiving events from an Kafka topic and passing it to the + * internal event broker. + * + * @packageDocumentation + */ + +export { eventsModuleKafkaConsumingEventPublisher as default } from './service/eventsModuleKafkaConsumingEventPublisher'; diff --git a/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.test.ts b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.test.ts new file mode 100644 index 0000000000..5824bf4d41 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.test.ts @@ -0,0 +1,132 @@ +/* + * 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 { KafkaConsumerClient } from './KafkaConsumerClient'; +import { ConfigReader } from '@backstage/config'; +import { KafkaConsumingEventPublisher } from './KafkaConsumingEventPublisher'; +import { mockServices } from '@backstage/backend-test-utils'; + +jest.mock('kafkajs'); +jest.mock('./KafkaConsumingEventPublisher'); + +describe('KafkaConsumerClient', () => { + const mockLogger = mockServices.logger.mock(); + const mockEvents = mockServices.events.mock(); + + const mockConfig = new ConfigReader({ + events: { + modules: { + kafka: { + kafkaConsumingEventPublisher: { + clientId: 'backstage-events', + brokers: ['kafka1:9092', 'kafka2:9092'], + topics: [ + { + topic: 'fake1', + kafka: { + topics: ['topic-A'], + groupId: 'my-group', + }, + }, + { + topic: 'fake2', + kafka: { + topics: ['topic-B'], + groupId: 'my-group', + }, + }, + ], + }, + }, + }, + }, + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should create an instance from config', () => { + const client = KafkaConsumerClient.fromConfig({ + config: mockConfig, + events: mockEvents, + logger: mockLogger, + }); + + expect(client).toBeInstanceOf(KafkaConsumerClient); + }); + + it('should not create an instance from config', () => { + const client = KafkaConsumerClient.fromConfig({ + config: new ConfigReader({}), + events: mockEvents, + logger: mockLogger, + }); + + expect(client).toBeUndefined(); + }); + + it('should create a consumer for each topic from config', () => { + KafkaConsumerClient.fromConfig({ + config: mockConfig, + events: mockEvents, + logger: mockLogger, + }); + + expect(KafkaConsumingEventPublisher.fromConfig).toHaveBeenCalledTimes(2); + }); + + it('should start all consumers', async () => { + const mockConsumer = { + start: jest.fn().mockResolvedValue(undefined), + }; + (KafkaConsumingEventPublisher.fromConfig as jest.Mock).mockReturnValue( + mockConsumer, + ); + + const client = KafkaConsumerClient.fromConfig({ + config: mockConfig, + events: mockEvents, + logger: mockLogger, + }); + + expect(client).toBeDefined(); + + await client?.start(); + + expect(mockConsumer.start).toHaveBeenCalled(); + }); + + it('should shutdown all consumers', async () => { + const mockConsumer = { + shutdown: jest.fn().mockResolvedValue(undefined), + }; + (KafkaConsumingEventPublisher.fromConfig as jest.Mock).mockReturnValue( + mockConsumer, + ); + + const client = KafkaConsumerClient.fromConfig({ + config: mockConfig, + events: mockEvents, + logger: mockLogger, + }); + + expect(client).toBeDefined(); + + await client?.shutdown(); + + expect(mockConsumer.shutdown).toHaveBeenCalled(); + }); +}); diff --git a/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.ts b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.ts new file mode 100644 index 0000000000..735b0fc224 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumerClient.ts @@ -0,0 +1,79 @@ +/* + * 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 { LoggerService } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import { EventsService } from '@backstage/plugin-events-node'; +import { Kafka } from 'kafkajs'; +import { KafkaEventSourceConfig, readConfig } from './config'; +import { KafkaConsumingEventPublisher } from './KafkaConsumingEventPublisher'; +import { loggerServiceAdapter } from './LoggerServiceAdapter'; + +/** + * KafkaConsumerClient + * + * This class creates the Kafka client that will be used to create the KafkaConsumingEventPublisher + * + * @public + */ +export class KafkaConsumerClient { + private readonly kafka: Kafka; + private readonly consumers: KafkaConsumingEventPublisher[]; + + static fromConfig(options: { + config: Config; + events: EventsService; + logger: LoggerService; + }): KafkaConsumerClient | undefined { + const kafkaConfig = readConfig(options.config); + + if (!kafkaConfig) { + options.logger.info( + 'Kafka consumer not configured, skipping initialization', + ); + return undefined; + } + + return new KafkaConsumerClient(options.logger, options.events, kafkaConfig); + } + + private constructor( + logger: LoggerService, + events: EventsService, + config: KafkaEventSourceConfig, + ) { + this.kafka = new Kafka({ + ...config.kafkaConfig, + logCreator: loggerServiceAdapter(logger), + }); + + this.consumers = config.kafkaConsumerConfigs.map(consumerConfig => + KafkaConsumingEventPublisher.fromConfig({ + kafkaClient: this.kafka, + config: consumerConfig, + logger, + events, + }), + ); + } + + async start(): Promise { + this.consumers.map(async consumer => await consumer.start()); + } + + async shutdown(): Promise { + this.consumers.map(async consumer => await consumer.shutdown()); + } +} diff --git a/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.test.ts b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.test.ts new file mode 100644 index 0000000000..64c53595ad --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.test.ts @@ -0,0 +1,92 @@ +/* + * 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 { KafkaConsumingEventPublisher } from './KafkaConsumingEventPublisher'; +import { Kafka } from 'kafkajs'; +import { KafkaConsumerConfig } from './config'; +import { mockServices } from '@backstage/backend-test-utils'; + +jest.mock('kafkajs'); + +describe('KafkaConsumingEventPublisher', () => { + const mockLogger = mockServices.logger.mock(); + const mockEvents = mockServices.events.mock(); + + const mockConsumer = { + connect: jest.fn(), + disconnect: jest.fn(), + subscribe: jest.fn(), + run: jest.fn(), + }; + + const mockKafkaClient = { + consumer: jest.fn().mockReturnValue(mockConsumer), + } as unknown as Kafka; + + const kafkaConsumerConfig: KafkaConsumerConfig = { + consumerConfig: { + groupId: 'test-group', + }, + consumerSubscribeTopics: { + topics: ['test-topic'], + }, + backstageTopic: 'backstage-topic', + }; + + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should create an instance from config', () => { + const consumer = KafkaConsumingEventPublisher.fromConfig({ + kafkaClient: mockKafkaClient, + config: kafkaConsumerConfig, + events: mockEvents, + logger: mockLogger, + }); + + expect(consumer).toBeInstanceOf(KafkaConsumingEventPublisher); + }); + + it('should start the consumer', async () => { + const consumer = KafkaConsumingEventPublisher.fromConfig({ + kafkaClient: mockKafkaClient, + config: kafkaConsumerConfig, + events: mockEvents, + logger: mockLogger, + }); + + await consumer.start(); + + expect(mockConsumer.connect).toHaveBeenCalled(); + expect(mockConsumer.subscribe).toHaveBeenCalledWith( + kafkaConsumerConfig.consumerSubscribeTopics, + ); + expect(mockConsumer.run).toHaveBeenCalled(); + }); + + it('should shutdown the consumer', async () => { + const consumer = KafkaConsumingEventPublisher.fromConfig({ + kafkaClient: mockKafkaClient, + config: kafkaConsumerConfig, + events: mockEvents, + logger: mockLogger, + }); + + await consumer.shutdown(); + + expect(mockConsumer.disconnect).toHaveBeenCalled(); + }); +}); diff --git a/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.ts b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.ts new file mode 100644 index 0000000000..223e4e99ac --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/KafkaConsumingEventPublisher.ts @@ -0,0 +1,109 @@ +/* + * 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 { LoggerService } from '@backstage/backend-plugin-api'; +import { EventParams, EventsService } from '@backstage/plugin-events-node'; +import { Consumer, ConsumerSubscribeTopics, IHeaders, Kafka } from 'kafkajs'; +import { KafkaConsumerConfig } from './config'; + +type EventMetadata = EventParams['metadata']; + +/** + * + * This class subscribes to Kafka topics and publishes events received to the registered subscriber. + * The message payload will be used as the event payload and passed to the subscribers. + * + * @public + */ +export class KafkaConsumingEventPublisher { + private readonly kafkaConsumer: Consumer; + private readonly consumerSubscribeTopics: ConsumerSubscribeTopics; + private readonly backstageTopic: string; + private readonly logger: LoggerService; + + static fromConfig(env: { + kafkaClient: Kafka; + config: KafkaConsumerConfig; + events: EventsService; + logger: LoggerService; + }): KafkaConsumingEventPublisher { + return new KafkaConsumingEventPublisher( + env.kafkaClient, + env.logger, + env.events, + env.config, + ); + } + + private constructor( + kafkaClient: Kafka, + logger: LoggerService, + private readonly events: EventsService, + config: KafkaConsumerConfig, + ) { + this.kafkaConsumer = kafkaClient.consumer(config.consumerConfig); + this.consumerSubscribeTopics = config.consumerSubscribeTopics; + this.backstageTopic = config.backstageTopic; + const id = `events.kafka.publisher:${this.backstageTopic}`; + this.logger = logger.child({ + class: KafkaConsumingEventPublisher.prototype.constructor.name, + groupId: config.consumerConfig.groupId, + kafkaTopics: config.consumerSubscribeTopics.topics.toString(), + backstageTopic: config.backstageTopic, + taskId: id, + }); + } + + async start(): Promise { + try { + await this.kafkaConsumer.connect(); + + await this.kafkaConsumer.subscribe(this.consumerSubscribeTopics); + + await this.kafkaConsumer.run({ + eachMessage: async ({ message }) => { + this.events.publish({ + topic: this.backstageTopic, + eventPayload: JSON.parse(message.value?.toString()!), + metadata: this.convertHeadersToMetadata(message.headers), + }); + }, + }); + } catch (error: any) { + this.logger.error('Kafka consumer connection failed ', error); + } + } + + async shutdown(): Promise { + await this.kafkaConsumer.disconnect(); + } + + private convertHeadersToMetadata = ( + headers: IHeaders | undefined, + ): EventParams['metadata'] => { + if (!headers) return undefined; + + const metadata: EventMetadata = {}; + + Object.entries(headers).forEach(([key, value]) => { + // If value is an array use toString() on all values converting any Buffer types to valid strings + if (Array.isArray(value)) metadata[key] = value.map(v => v.toString()); + // Always return the values using toString() to catch all Buffer types that should be converted to strings + else metadata[key] = value?.toString(); + }); + + return metadata; + }; +} diff --git a/plugins/events-backend-module-kafka/src/publisher/LoggerServiceAdapter.ts b/plugins/events-backend-module-kafka/src/publisher/LoggerServiceAdapter.ts new file mode 100644 index 0000000000..b9e9379104 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/LoggerServiceAdapter.ts @@ -0,0 +1,45 @@ +/* + * 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 { LoggerService } from '@backstage/backend-plugin-api'; +import { LogEntry, logLevel } from 'kafkajs'; + +export const loggerServiceAdapter = + (loggerService: LoggerService) => (_level: logLevel) => { + return (entry: LogEntry) => { + const { namespace, level, log } = entry; + const { message, ...extra } = log; + + const logMethods: Record< + logLevel, + (message: string, meta?: object) => void + > = { + [logLevel.ERROR]: loggerService.error, + [logLevel.WARN]: loggerService.warn, + [logLevel.INFO]: loggerService.info, + [logLevel.DEBUG]: loggerService.debug, + [logLevel.NOTHING]: () => {}, + }; + + // Use loggerService method that matches the level + logMethods[level].call( + loggerService, + `Kafka ${namespace} ${log.message}`, + { + ...extra, + }, + ); + }; + }; diff --git a/plugins/events-backend-module-kafka/src/publisher/config.test.ts b/plugins/events-backend-module-kafka/src/publisher/config.test.ts new file mode 100644 index 0000000000..fc7e3049b0 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/config.test.ts @@ -0,0 +1,250 @@ +/* + * 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 { ConfigReader } from '@backstage/config'; +import { readConfig } from './config'; + +describe('readConfig', () => { + it('not configured', () => { + const publisherConfigs = readConfig(new ConfigReader({})); + + expect(publisherConfigs).toBeUndefined(); + }); + + it('only required fields configured', () => { + const config = new ConfigReader({ + events: { + modules: { + kafka: { + kafkaConsumingEventPublisher: { + clientId: 'backstage-events', + brokers: ['kafka1:9092', 'kafka2:9092'], + topics: [ + { + topic: 'fake1', + kafka: { + topics: ['topic-A'], + groupId: 'my-group', + }, + }, + { + topic: 'fake2', + kafka: { + topics: ['topic-B'], + groupId: 'my-group', + }, + }, + ], + }, + }, + }, + }, + }); + + const publisherConfigs = readConfig(config); + + expect(publisherConfigs).toBeDefined(); + + expect(publisherConfigs?.kafkaConsumerConfigs.length).toBe(2); + + expect(publisherConfigs?.kafkaConfig.clientId).toEqual('backstage-events'); + expect(publisherConfigs?.kafkaConfig.brokers).toEqual([ + 'kafka1:9092', + 'kafka2:9092', + ]); + expect(publisherConfigs?.kafkaConsumerConfigs[0].backstageTopic).toEqual( + 'fake1', + ); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.groupId, + ).toEqual('my-group'); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerSubscribeTopics.topics, + ).toEqual(['topic-A']); + }); + + it('all fields configured', () => { + const config = new ConfigReader({ + events: { + modules: { + kafka: { + kafkaConsumingEventPublisher: { + clientId: 'backstage-events', + brokers: ['kafka1:9092', 'kafka2:9092'], + ssl: true, + sasl: { + mechanism: 'plain', + username: 'username', + password: 'password', + }, + retry: { + maxRetryTime: { milliseconds: 20000 }, + initialRetryTime: { milliseconds: 200 }, + factor: '0.4', + multiplier: '4', + retries: '10', + }, + authenticationTimeout: { milliseconds: 20000 }, + connectionTimeout: { milliseconds: 1500 }, + requestTimeout: { milliseconds: 20000 }, + enforceRequestTimeout: false, + topics: [ + { + topic: 'fake1', + kafka: { + topics: ['topic-A'], + groupId: 'my-group', + sessionTimeout: { milliseconds: 20000 }, + rebalanceTimeout: { milliseconds: 50000 }, + heartbeatInterval: { milliseconds: 2000 }, + metadataMaxAge: { milliseconds: 400000 }, + maxBytesPerPartition: 50000, + minBytes: 2, + maxBytes: 500000, + maxWaitTime: { milliseconds: 4000 }, + }, + }, + { + topic: 'fake2', + kafka: { + topics: ['topic-B'], + groupId: 'my-group', + }, + }, + ], + }, + }, + }, + }, + }); + + const publisherConfigs = readConfig(config); + + expect(publisherConfigs).toBeDefined(); + + // Client configuration + expect(publisherConfigs?.kafkaConfig.clientId).toEqual('backstage-events'); + expect(publisherConfigs?.kafkaConfig.brokers).toEqual([ + 'kafka1:9092', + 'kafka2:9092', + ]); + expect(publisherConfigs?.kafkaConfig.ssl).toBeTruthy(); + expect(publisherConfigs?.kafkaConfig.sasl).toStrictEqual({ + mechanism: 'plain', + username: 'username', + password: 'password', + }); + expect(publisherConfigs?.kafkaConfig.authenticationTimeout).toBe(20000); + expect(publisherConfigs?.kafkaConfig.connectionTimeout).toBe(1500); + expect(publisherConfigs?.kafkaConfig.requestTimeout).toBe(20000); + expect(publisherConfigs?.kafkaConfig.enforceRequestTimeout).toBeFalsy(); + expect(publisherConfigs?.kafkaConfig.retry).toStrictEqual({ + maxRetryTime: 20000, + initialRetryTime: 200, + factor: 0.4, + multiplier: 4, + retries: 10, + }); + + // Consumer configuration + expect(publisherConfigs?.kafkaConsumerConfigs.length).toBe(2); + expect(publisherConfigs?.kafkaConsumerConfigs[0].backstageTopic).toEqual( + 'fake1', + ); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.groupId, + ).toEqual('my-group'); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerSubscribeTopics.topics, + ).toEqual(['topic-A']); + + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.sessionTimeout, + ).toBe(20000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.rebalanceTimeout, + ).toBe(50000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig + .heartbeatInterval, + ).toBe(2000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.metadataMaxAge, + ).toBe(400000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig + .maxBytesPerPartition, + ).toBe(50000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.minBytes, + ).toBe(2); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.maxBytes, + ).toBe(500000); + expect( + publisherConfigs?.kafkaConsumerConfigs[0].consumerConfig.maxWaitTimeInMs, + ).toBe(4000); + }); + + it('should handle HumanDuration and string values for durations and timeouts', () => { + const config = new ConfigReader({ + events: { + modules: { + kafka: { + kafkaConsumingEventPublisher: { + clientId: 'backstage-events', + brokers: ['kafka1:9092', 'kafka2:9092'], + retry: { + maxRetryTime: { seconds: 1 }, + initialRetryTime: { minutes: 1 }, + factor: 0.4, + multiplier: 4, + retries: 10, + }, + authenticationTimeout: { hours: 1 }, + connectionTimeout: { days: 1 }, + topics: [], + requestTimeout: '1m', + }, + }, + }, + }, + }); + + const publisherConfigs = readConfig(config); + + expect(publisherConfigs).toBeDefined(); + + // Client configuration + expect(publisherConfigs?.kafkaConfig.clientId).toEqual('backstage-events'); + expect(publisherConfigs?.kafkaConfig.brokers).toEqual([ + 'kafka1:9092', + 'kafka2:9092', + ]); + expect(publisherConfigs?.kafkaConfig.authenticationTimeout).toBe(3600000); + expect(publisherConfigs?.kafkaConfig.connectionTimeout).toBe(86400000); + expect(publisherConfigs?.kafkaConfig.requestTimeout).toBe(60000); + expect(publisherConfigs?.kafkaConfig.retry).toStrictEqual({ + maxRetryTime: 1000, + initialRetryTime: 60000, + factor: 0.4, + multiplier: 4, + retries: 10, + }); + + // Consumer configuration + expect(publisherConfigs?.kafkaConsumerConfigs.length).toBe(0); + }); +}); diff --git a/plugins/events-backend-module-kafka/src/publisher/config.ts b/plugins/events-backend-module-kafka/src/publisher/config.ts new file mode 100644 index 0000000000..aa5921a7f3 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/publisher/config.ts @@ -0,0 +1,159 @@ +/* + * 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 { Config, readDurationFromConfig } from '@backstage/config'; +import { ConsumerConfig, ConsumerSubscribeTopics, KafkaConfig } from 'kafkajs'; +import { Duration } from 'luxon'; + +/** + * @public + */ +export interface KafkaConsumerConfig { + backstageTopic: string; + consumerConfig: ConsumerConfig; + consumerSubscribeTopics: ConsumerSubscribeTopics; +} + +/** + * @public + */ +export interface KafkaEventSourceConfig { + kafkaConfig: KafkaConfig; + kafkaConsumerConfigs: KafkaConsumerConfig[]; +} + +const CONFIG_PREFIX_PUBLISHER = + 'events.modules.kafka.kafkaConsumingEventPublisher'; + +/** + * Reads an optional HumanDuration from the config and returns the value in milliseconds if the key is defined. + * + * @param config - The configuration object to read from. + * @param key - The key to look up in the configuration. + * @returns The duration in milliseconds, or undefined if the key is not defined. + */ +const readOptionalHumanDurationInMs = ( + config: Config, + key: string, +): number | undefined => { + const humanDuration = config.has(key) + ? readDurationFromConfig(config, { key }) + : undefined; + + if (!humanDuration) return undefined; + + return Duration.fromObject(humanDuration).as('milliseconds'); +}; + +export const readConfig = ( + config: Config, +): KafkaEventSourceConfig | undefined => { + const kafkaConfig = config.getOptionalConfig(CONFIG_PREFIX_PUBLISHER); + + if (!kafkaConfig) { + return undefined; + } + + const clientId = kafkaConfig.getString('clientId'); + const brokers = kafkaConfig.getStringArray('brokers'); + + const authenticationTimeout = readOptionalHumanDurationInMs( + kafkaConfig, + 'authenticationTimeout', + ); + + const connectionTimeout = readOptionalHumanDurationInMs( + kafkaConfig, + 'connectionTimeout', + ); + const requestTimeout = readOptionalHumanDurationInMs( + kafkaConfig, + 'requestTimeout', + ); + const enforceRequestTimeout = kafkaConfig.getOptionalBoolean( + 'enforceRequestTimeout', + ); + + const ssl = kafkaConfig.getOptional('ssl') as KafkaConfig['ssl']; + const sasl = kafkaConfig.getOptional('sasl') as KafkaConfig['sasl']; + + const retry: KafkaConfig['retry'] = { + maxRetryTime: readOptionalHumanDurationInMs( + kafkaConfig, + 'retry.maxRetryTime', + ), + initialRetryTime: readOptionalHumanDurationInMs( + kafkaConfig, + 'retry.initialRetryTime', + ), + factor: kafkaConfig.getOptionalNumber('retry.factor'), + multiplier: kafkaConfig.getOptionalNumber('retry.multiplier'), + retries: kafkaConfig.getOptionalNumber('retry.retries'), + }; + + const kafkaConsumerConfigs: KafkaConsumerConfig[] = kafkaConfig + .getConfigArray('topics') + .map(topic => { + return { + backstageTopic: topic.getString('topic'), + consumerConfig: { + groupId: topic.getString('kafka.groupId'), + sessionTimeout: readOptionalHumanDurationInMs( + topic, + 'kafka.sessionTimeout', + ), + rebalanceTimeout: readOptionalHumanDurationInMs( + topic, + 'kafka.rebalanceTimeout', + ), + heartbeatInterval: readOptionalHumanDurationInMs( + topic, + 'kafka.heartbeatInterval', + ), + metadataMaxAge: readOptionalHumanDurationInMs( + topic, + 'kafka.metadataMaxAge', + ), + maxBytesPerPartition: topic.getOptionalNumber( + 'kafka.maxBytesPerPartition', + ), + minBytes: topic.getOptionalNumber('kafka.minBytes'), + maxBytes: topic.getOptionalNumber('kafka.maxBytes'), + maxWaitTimeInMs: readOptionalHumanDurationInMs( + topic, + 'kafka.maxWaitTime', + ), + }, + consumerSubscribeTopics: { + topics: topic.getStringArray('kafka.topics'), + }, + }; + }); + + return { + kafkaConfig: { + clientId, + brokers, + ssl, + sasl, + authenticationTimeout, + connectionTimeout, + requestTimeout, + enforceRequestTimeout, + retry, + }, + kafkaConsumerConfigs, + }; +}; diff --git a/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.test.ts b/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.test.ts new file mode 100644 index 0000000000..816793f635 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.test.ts @@ -0,0 +1,85 @@ +/* + * 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 { createServiceFactory } from '@backstage/backend-plugin-api'; +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; +import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; +import { eventsModuleKafkaConsumingEventPublisher } from './eventsModuleKafkaConsumingEventPublisher'; +import { KafkaConsumerClient } from '../publisher/KafkaConsumerClient'; + +jest.mock('../publisher/KafkaConsumerClient'); + +describe('eventsModuleKafkaConsumingEventPublisher', () => { + it('should be correctly wired and set up', async () => { + const events = new TestEventsService(); + const eventsServiceFactory = createServiceFactory({ + service: eventsServiceRef, + deps: {}, + async factory({}) { + return events; + }, + }); + + const mockKafkaConsumerClient = { + start: jest.fn(), + shutdown: jest.fn(), + }; + (KafkaConsumerClient.fromConfig as jest.Mock).mockReturnValue( + mockKafkaConsumerClient, + ); + + await startTestBackend({ + features: [ + eventsServiceFactory, + eventsModuleKafkaConsumingEventPublisher, + mockServices.rootConfig.factory({ + data: { + events: { + modules: { + kafka: { + kafkaConsumingEventPublisher: { + clientId: 'backstage-events', + brokers: ['kafka1:9092', 'kafka2:9092'], + topics: { + fake1: { + kafka: { + topics: ['topic-A'], + groupId: 'my-group', + }, + }, + fake2: { + kafka: { + topics: ['topic-B'], + groupId: 'my-group', + }, + }, + }, + }, + }, + }, + }, + }, + }), + ], + }); + + // Verify that the Kafka consumer client was started + expect(mockKafkaConsumerClient.start).toHaveBeenCalled(); + + // Verify that the shutdown hook was registered + expect(mockKafkaConsumerClient.shutdown).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.ts b/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.ts new file mode 100644 index 0000000000..b524454ca0 --- /dev/null +++ b/plugins/events-backend-module-kafka/src/service/eventsModuleKafkaConsumingEventPublisher.ts @@ -0,0 +1,56 @@ +/* + * 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 { KafkaConsumerClient } from '../publisher/KafkaConsumerClient'; +import { eventsServiceRef } from '@backstage/plugin-events-node'; + +/** + * Kafka module for the Events plugin. + * + * @public + */ +export const eventsModuleKafkaConsumingEventPublisher = createBackendModule({ + pluginId: 'events', + moduleId: 'kafka-consuming-event-publisher', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + events: eventsServiceRef, + logger: coreServices.logger, + lifecycle: coreServices.lifecycle, + }, + async init({ config, logger, events, lifecycle }) { + const kafka = KafkaConsumerClient.fromConfig({ + config, + events, + logger, + }); + + if (!kafka) { + return; + } + + await kafka.start(); + + lifecycle.addShutdownHook(async () => await kafka.shutdown()); + }, + }); + }, +}); diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index 0acf569d83..ab0af4eb6b 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.46-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.45 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.4.12 + ## 0.1.45-next.1 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index 1662f4f943..c0ad0cbd47 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend-test-utils", - "version": "0.1.45-next.1", + "version": "0.1.46-next.0", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", "backstage": { "role": "node-library", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 90f5f6009f..a0cd37a8ac 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-events-backend +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/backend-openapi-utils@0.5.5-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.5.3 + +### Patch Changes + +- c83cd8b: Fixed some circular or otherwise unclear imports +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + ## 0.5.3-next.1 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 0f57e0da5a..41123adac5 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.5.3-next.1", + "version": "0.5.4-next.0", "backstage": { "role": "backend-plugin", "pluginId": "events", diff --git a/plugins/events-backend/src/schema/openapi/generated/router.ts b/plugins/events-backend/src/schema/openapi/generated/router.ts index 3c43880170..d2cedd2166 100644 --- a/plugins/events-backend/src/schema/openapi/generated/router.ts +++ b/plugins/events-backend/src/schema/openapi/generated/router.ts @@ -18,7 +18,7 @@ // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils'; -import { EndpointMap } from './'; +import { EndpointMap } from './apis'; export const spec = { openapi: '3.0.3', diff --git a/plugins/events-backend/src/service/EventsPlugin.test.ts b/plugins/events-backend/src/service/EventsPlugin.test.ts index 12a0f63002..2c3fb3b909 100644 --- a/plugins/events-backend/src/service/EventsPlugin.test.ts +++ b/plugins/events-backend/src/service/EventsPlugin.test.ts @@ -34,6 +34,8 @@ import { TestEventsService } from '@backstage/plugin-events-backend-test-utils'; import request from 'supertest'; import { eventsPlugin } from './EventsPlugin'; +jest.setTimeout(60_000); + describe('eventsPlugin', () => { it('should be initialized properly', async () => { const eventsService = new TestEventsService(); diff --git a/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts b/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts index 959341cbc0..5664475851 100644 --- a/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts +++ b/plugins/events-backend/src/service/hub/DatabaseEventBusStore.test.ts @@ -21,6 +21,8 @@ import { } from '@backstage/backend-test-utils'; import { DatabaseEventBusStore } from './DatabaseEventBusStore'; +jest.setTimeout(60_000); + const logger = mockServices.logger.mock(); const databases = TestDatabases.create({ diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index 06960faf33..035cfd25ed 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-events-node +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 0.4.12 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.4.12-next.1 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index c60bb7ded1..37fd39dc5a 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-node", - "version": "0.4.12-next.1", + "version": "0.4.13-next.0", "description": "The plugin-events-node module for @backstage/plugin-events-backend", "backstage": { "role": "node-library", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index eb0f46dc95..a774c0e8c1 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @internal/plugin-todo-list-backend +## 1.0.41-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 1.0.40 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + ## 1.0.40-next.1 ### Patch Changes diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index 1e8152f632..4b7785cd40 100644 --- a/plugins/example-todo-list-backend/package.json +++ b/plugins/example-todo-list-backend/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-backend", - "version": "1.0.40-next.1", + "version": "1.0.41-next.0", "backstage": { "role": "backend-plugin", "pluginId": "todo-list", diff --git a/plugins/example-todo-list-common/CHANGELOG.md b/plugins/example-todo-list-common/CHANGELOG.md index 09a053b1ac..a18280ea51 100644 --- a/plugins/example-todo-list-common/CHANGELOG.md +++ b/plugins/example-todo-list-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @internal/plugin-todo-list-common +## 1.0.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + ## 1.0.25 ### Patch Changes diff --git a/plugins/example-todo-list-common/package.json b/plugins/example-todo-list-common/package.json index 6c1b783755..e6c9039730 100644 --- a/plugins/example-todo-list-common/package.json +++ b/plugins/example-todo-list-common/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list-common", - "version": "1.0.25", + "version": "1.0.26-next.0", "backstage": { "role": "common-library", "pluginId": "todo-list", diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 219f418b6e..8b64398dc7 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,37 @@ # @internal/plugin-todo-list +## 1.0.41-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 1.0.41-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + +## 1.0.40 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + +## 1.0.40-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + ## 1.0.39 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 3fbdf8fe9d..731972e7b6 100644 --- a/plugins/example-todo-list/package.json +++ b/plugins/example-todo-list/package.json @@ -1,6 +1,6 @@ { "name": "@internal/plugin-todo-list", - "version": "1.0.39", + "version": "1.0.41-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "todo-list", diff --git a/plugins/gateway-backend/CHANGELOG.md b/plugins/gateway-backend/CHANGELOG.md index 5521af45d0..c4803fc86f 100644 --- a/plugins/gateway-backend/CHANGELOG.md +++ b/plugins/gateway-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-gateway-backend +## 1.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 1.0.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + ## 1.0.2-next.1 ### Patch Changes diff --git a/plugins/gateway-backend/package.json b/plugins/gateway-backend/package.json index c7efdbfaec..0422e4b1d4 100644 --- a/plugins/gateway-backend/package.json +++ b/plugins/gateway-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gateway-backend", - "version": "1.0.2-next.1", + "version": "1.0.3-next.0", "backstage": { "role": "backend-plugin", "pluginId": "gateway", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 42b7f67b3b..f441549652 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,42 @@ # @backstage/plugin-home-react +## 0.1.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.1.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 0.1.27 + +### Patch Changes + +- c83cd8b: Fixed some circular or otherwise unclear imports +- cef60db: Home plugin support i18n +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + +## 0.1.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/core-plugin-api@1.10.7 + ## 0.1.26 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 37180110fe..c9bf1b62b6 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home-react", - "version": "0.1.26", + "version": "0.1.28-next.1", "description": "A Backstage plugin that contains react components helps you build a home page", "backstage": { "role": "web-library", @@ -11,9 +11,7 @@ ] }, "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" }, "keywords": [ "backstage", @@ -27,8 +25,23 @@ }, "license": "Apache-2.0", "sideEffects": false, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, "main": "src/index.ts", "types": "src/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } + }, "files": [ "dist" ], @@ -44,6 +57,7 @@ "dependencies": { "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.9.1", "@rjsf/utils": "5.23.2" diff --git a/plugins/home-react/report-alpha.api.md b/plugins/home-react/report-alpha.api.md new file mode 100644 index 0000000000..fad4c5a2bc --- /dev/null +++ b/plugins/home-react/report-alpha.api.md @@ -0,0 +1,19 @@ +## API Report File for "@backstage/plugin-home-react" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { TranslationRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +export const homeReactTranslationRef: TranslationRef< + 'home-react', + { + readonly 'settingsModal.title': 'Settings'; + readonly 'settingsModal.closeButtonTitle': 'Close'; + readonly 'cardExtension.settingsButtonTitle': 'Settings'; + } +>; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/home-react/src/alpha.ts b/plugins/home-react/src/alpha.ts new file mode 100644 index 0000000000..3a08699f66 --- /dev/null +++ b/plugins/home-react/src/alpha.ts @@ -0,0 +1,16 @@ +/* + * 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 { homeReactTranslationRef } from './translation'; diff --git a/plugins/home-react/src/components/SettingsModal.tsx b/plugins/home-react/src/components/SettingsModal.tsx index 4a6aa46198..37b8d103c2 100644 --- a/plugins/home-react/src/components/SettingsModal.tsx +++ b/plugins/home-react/src/components/SettingsModal.tsx @@ -14,11 +14,13 @@ * limitations under the License. */ +import { useTranslationRef } from '@backstage/frontend-plugin-api'; import Button from '@material-ui/core/Button'; import Dialog from '@material-ui/core/Dialog'; import DialogActions from '@material-ui/core/DialogActions'; import DialogContent from '@material-ui/core/DialogContent'; import DialogTitle from '@material-ui/core/DialogTitle'; +import { homeReactTranslationRef } from '../translation'; /** @public */ export const SettingsModal = (props: { @@ -28,16 +30,18 @@ export const SettingsModal = (props: { children: JSX.Element; }) => { const { open, close, componentName, children } = props; - + const { t } = useTranslationRef(homeReactTranslationRef); return ( close()}> - {componentName ? `Settings - ${componentName}` : 'Settings'} + {componentName + ? `${t('settingsModal.title')} - ${componentName}` + : t('settingsModal.title')} {children} diff --git a/plugins/home-react/src/extensions.tsx b/plugins/home-react/src/extensions.tsx index 610c76bb0e..7795e20c73 100644 --- a/plugins/home-react/src/extensions.tsx +++ b/plugins/home-react/src/extensions.tsx @@ -21,6 +21,8 @@ import { InfoCard } from '@backstage/core-components'; import { SettingsModal } from './components'; import { createReactExtension, useApp } from '@backstage/core-plugin-api'; import { RJSFSchema, UiSchema } from '@rjsf/utils'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeReactTranslationRef } from './translation'; /** * @public @@ -133,6 +135,7 @@ function CardExtension(props: CardExtensionComponentProps) { const app = useApp(); const { Progress } = app.getComponents(); const [settingsOpen, setSettingsOpen] = useState(false); + const { t } = useTranslationRef(homeReactTranslationRef); if (Renderer) { return ( @@ -158,7 +161,9 @@ function CardExtension(props: CardExtensionComponentProps) { ? { action: ( setSettingsOpen(true)}> - Settings + + {t('cardExtension.settingsButtonTitle')} + ), } diff --git a/plugins/home-react/src/overridableComponents.ts b/plugins/home-react/src/overridableComponents.ts index 8a88fd5c94..3653257507 100644 --- a/plugins/home-react/src/overridableComponents.ts +++ b/plugins/home-react/src/overridableComponents.ts @@ -16,7 +16,7 @@ import { Overrides } from '@material-ui/core/styles/overrides'; import { StyleRules } from '@material-ui/core/styles/withStyles'; -import { PluginHomeContentModalClassKey } from './'; +import { PluginHomeContentModalClassKey } from './components/ContentModal'; /** @public */ export type PluginHomeComponentsNameToClassKey = { diff --git a/plugins/home-react/src/translation.ts b/plugins/home-react/src/translation.ts new file mode 100644 index 0000000000..8de5bb1ff1 --- /dev/null +++ b/plugins/home-react/src/translation.ts @@ -0,0 +1,32 @@ +/* + * 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 { createTranslationRef } from '@backstage/frontend-plugin-api'; + +/** + * @alpha + */ +export const homeReactTranslationRef = createTranslationRef({ + id: 'home-react', + messages: { + settingsModal: { + title: 'Settings', + closeButtonTitle: 'Close', + }, + cardExtension: { + settingsButtonTitle: 'Settings', + }, + }, +}); diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index 3b4972c087..7172537e8d 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,89 @@ # @backstage/plugin-home +## 0.8.10-next.2 + +### Patch Changes + +- d52d67f: Added a New Frontend System App Root Element for the `` component +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + +## 0.8.10-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-home-react@0.1.28-next.1 + +## 0.8.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-home-react@0.1.28-next.0 + +## 0.8.9 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- 0ebad54: Fixed the `WelcomeTitle` to properly default to the previous value of `inherit` +- cef60db: Home plugin support i18n +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-home-react@0.1.27 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.1 + - @backstage/core-compat-api@0.4.3 + - @backstage/theme@0.6.6 + +## 0.8.9-next.2 + +### Patch Changes + +- 0ebad54: Fixed the `WelcomeTitle` to properly default to the previous value of `inherit` +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/plugin-home-react@0.1.27-next.0 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-app-api@1.17.0 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + ## 0.8.9-next.1 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index e6550137fc..d5cc3a8e0e 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-home", - "version": "0.8.9-next.1", + "version": "0.8.10-next.2", "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 5e43163e31..98a8cab823 100644 --- a/plugins/home/report-alpha.api.md +++ b/plugins/home/report-alpha.api.md @@ -10,6 +10,7 @@ import { ExtensionInput } from '@backstage/frontend-plugin-api'; import { FrontendPlugin } from '@backstage/frontend-plugin-api'; import { JSX as JSX_2 } from 'react'; import { RouteRef } from '@backstage/frontend-plugin-api'; +import { TranslationRef } from '@backstage/frontend-plugin-api'; // @alpha (undocumented) const _default: FrontendPlugin< @@ -18,6 +19,21 @@ const _default: FrontendPlugin< }, {}, { + 'app-root-element:home/visit-listener': ExtensionDefinition<{ + kind: 'app-root-element'; + name: 'visit-listener'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + params: { + element: JSX.Element | (() => JSX.Element); + }; + }>; 'page:home': ExtensionDefinition<{ config: { path: string | undefined; @@ -69,6 +85,38 @@ const _default: FrontendPlugin< >; export default _default; +// @alpha (undocumented) +export const homeTranslationRef: TranslationRef< + 'home', + { + readonly 'starredEntities.noStarredEntitiesMessage': 'Click the star beside an entity name to add it to this list!'; + readonly 'addWidgetDialog.title': 'Add new widget to dashboard'; + readonly 'customHomepageButtons.clearAll': 'Clear all'; + readonly 'customHomepageButtons.edit': 'Edit'; + readonly 'customHomepageButtons.restoreDefaults': 'Restore defaults'; + readonly 'customHomepageButtons.addWidget': 'Add widget'; + readonly 'customHomepageButtons.save': 'Save'; + readonly 'customHomepage.noWidgets': "No widgets added. Start by clicking the 'Add widget' button."; + readonly 'widgetSettingsOverlay.cancelButtonTitle': 'Cancel'; + readonly 'widgetSettingsOverlay.editSettingsTooptip': 'Edit settings'; + readonly 'widgetSettingsOverlay.deleteWidgetTooltip': 'Delete widget'; + readonly 'widgetSettingsOverlay.submitButtonTitle': 'Submit'; + readonly 'starredEntityListItem.removeFavoriteEntityTitle': 'Remove entity from favorites'; + readonly 'visitList.empty.title': 'There are no visits to show yet.'; + readonly 'visitList.empty.description': 'Once you start using Backstage, your visits will appear here as a quick link to carry on where you left off.'; + readonly 'visitList.few.title': 'The more pages you visit, the more pages will appear here.'; + readonly 'quickStart.title': 'Onboarding'; + readonly 'quickStart.description': 'Get started with Backstage'; + readonly 'quickStart.learnMoreLinkTitle': 'Learn more'; + readonly 'visitedByType.action.viewMore': 'View more'; + readonly 'visitedByType.action.viewLess': 'View less'; + readonly 'featuredDocsCard.empty.title': 'No documents to show'; + readonly 'featuredDocsCard.empty.description': 'Create your own document. Check out our Getting Started Information'; + readonly 'featuredDocsCard.empty.learnMoreLinkTitle': 'DOCS'; + readonly 'featuredDocsCard.learnMoreTitle': 'LEARN MORE'; + } +>; + // @alpha (undocumented) export const titleExtensionDataRef: ConfigurableExtensionDataRef< string, diff --git a/plugins/home/src/alpha.tsx b/plugins/home/src/alpha.tsx index 854a8a7dd3..880b53af40 100644 --- a/plugins/home/src/alpha.tsx +++ b/plugins/home/src/alpha.tsx @@ -21,8 +21,10 @@ import { PageBlueprint, createFrontendPlugin, createRouteRef, + AppRootElementBlueprint, } from '@backstage/frontend-plugin-api'; import { compatWrapper } from '@backstage/core-compat-api'; +import { VisitListener } from './components/'; const rootRouteRef = createRouteRef(); @@ -63,14 +65,23 @@ const homePage = PageBlueprint.makeWithOverrides({ }, }); +const visitListenerAppRootElement = AppRootElementBlueprint.make({ + name: 'visit-listener', + params: { + element: , + }, +}); + /** * @alpha */ export default createFrontendPlugin({ pluginId: 'home', info: { packageJson: () => import('../package.json') }, - extensions: [homePage], + extensions: [homePage, visitListenerAppRootElement], routes: { root: rootRouteRef, }, }); + +export { homeTranslationRef } from './translation'; diff --git a/plugins/home/src/components/CustomHomepage/AddWidgetDialog.tsx b/plugins/home/src/components/CustomHomepage/AddWidgetDialog.tsx index 6519bfe901..4763401fe5 100644 --- a/plugins/home/src/components/CustomHomepage/AddWidgetDialog.tsx +++ b/plugins/home/src/components/CustomHomepage/AddWidgetDialog.tsx @@ -23,6 +23,8 @@ import ListItemAvatar from '@material-ui/core/ListItemAvatar'; import AddIcon from '@material-ui/icons/Add'; import ListItemText from '@material-ui/core/ListItemText'; import Typography from '@material-ui/core/Typography'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; interface AddWidgetDialogProps { widgets: Widget[]; @@ -35,9 +37,10 @@ const getTitle = (widget: Widget) => { export const AddWidgetDialog = (props: AddWidgetDialogProps) => { const { widgets, handleAdd } = props; + const { t } = useTranslationRef(homeTranslationRef); return ( <> - Add new widget to dashboard + {t('addWidgetDialog.title')} {widgets.map(widget => { diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx index d586f94d20..0496e923de 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageButtons.tsx @@ -20,6 +20,8 @@ import DeleteIcon from '@material-ui/icons/Delete'; import AddIcon from '@material-ui/icons/Add'; import EditIcon from '@material-ui/icons/Edit'; import CancelIcon from '@material-ui/icons/Cancel'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -55,6 +57,7 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { restoreDefault, } = props; const styles = useStyles(); + const { t } = useTranslationRef(homeTranslationRef); return ( <> @@ -66,7 +69,7 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { size="small" startIcon={} > - Edit + {t('customHomepageButtons.edit')} ) : ( <> @@ -78,7 +81,7 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { size="small" startIcon={} > - Restore defaults + {t('customHomepageButtons.restoreDefaults')} )} {numWidgets > 0 && ( @@ -90,7 +93,7 @@ export const CustomHomepageButtons = (props: CustomHomepageButtonsProps) => { size="small" startIcon={} > - Clear all + {t('customHomepageButtons.clearAll')} )} {numWidgets > 0 && ( )} diff --git a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx index 675e17357e..4b3d35ed62 100644 --- a/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx +++ b/plugins/home/src/components/CustomHomepage/CustomHomepageGrid.tsx @@ -50,6 +50,8 @@ import { WidgetSchema, } from './types'; import { CardConfig } from '@backstage/plugin-home-react'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; // eslint-disable-next-line new-cap const ResponsiveGrid = WidthProvider(Responsive); @@ -222,6 +224,7 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { const getWidgetNameFromKey = (key: string) => { return key.split('__')[0]; }; + const { t } = useTranslationRef(homeTranslationRef); const handleAdd = (widget: Widget) => { const widgetId = `${widget.name}__${widgets.length + 1}${Math.random() @@ -340,7 +343,7 @@ export const CustomHomepageGrid = (props: CustomHomepageGridProps) => { {!editMode && widgets.length === 0 && ( - No widgets added. Start by clicking the 'Add widget' button. + {t('customHomepage.noWidgets')} )} { const styles = useStyles(); const onClose = () => setSettingsDialogOpen(false); + const { t } = useTranslationRef(homeTranslationRef); return (
@@ -98,10 +101,10 @@ export const WidgetSettingsOverlay = (props: WidgetSettingsOverlayProps) => { > @@ -116,7 +119,7 @@ export const WidgetSettingsOverlay = (props: WidgetSettingsOverlayProps) => { > {widget.settingsSchema && ( - + setSettingsDialogOpen(true)} @@ -128,7 +131,7 @@ export const WidgetSettingsOverlay = (props: WidgetSettingsOverlayProps) => { )} {deletable !== false && ( - + handleRemove(id)}> diff --git a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx index 28f199978d..60433fbddf 100644 --- a/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx +++ b/plugins/home/src/components/StarredEntityListItem/StarredEntityListItem.tsx @@ -26,6 +26,8 @@ import { entityRouteRef } from '@backstage/plugin-catalog-react'; import { useRouteRef } from '@backstage/core-plugin-api'; import { FavoriteToggle } from '@backstage/core-components'; import { makeStyles } from '@material-ui/core/styles'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; type EntityListItemProps = { entity: Entity; @@ -50,6 +52,7 @@ export const StarredEntityListItem = ({ }: EntityListItemProps) => { const classes = useStyles(); const catalogEntityRoute = useRouteRef(entityRouteRef); + const { t } = useTranslationRef(homeTranslationRef); let secondaryText = ''; if (showKind) { @@ -80,7 +83,7 @@ export const StarredEntityListItem = ({ > onToggleStarredEntity(entity)} /> diff --git a/plugins/home/src/components/VisitList/VisitListEmpty.tsx b/plugins/home/src/components/VisitList/VisitListEmpty.tsx index e8b6d369d2..144c9a2066 100644 --- a/plugins/home/src/components/VisitList/VisitListEmpty.tsx +++ b/plugins/home/src/components/VisitList/VisitListEmpty.tsx @@ -14,16 +14,20 @@ * limitations under the License. */ +import { useTranslationRef } from '@backstage/frontend-plugin-api'; import Typography from '@material-ui/core/Typography'; +import { homeTranslationRef } from '../../translation'; -export const VisitListEmpty = () => ( - <> - - There are no visits to show yet. - - - Once you start using Backstage, your visits will appear here as a quick - link to carry on where you left off. - - -); +export const VisitListEmpty = () => { + const { t } = useTranslationRef(homeTranslationRef); + return ( + <> + + {t('visitList.empty.title')} + + + {t('visitList.empty.description')} + + + ); +}; diff --git a/plugins/home/src/components/VisitList/VisitListFew.tsx b/plugins/home/src/components/VisitList/VisitListFew.tsx index 26fdb92293..7f741f3af5 100644 --- a/plugins/home/src/components/VisitList/VisitListFew.tsx +++ b/plugins/home/src/components/VisitList/VisitListFew.tsx @@ -14,12 +14,17 @@ * limitations under the License. */ +import { useTranslationRef } from '@backstage/frontend-plugin-api'; import Typography from '@material-ui/core/Typography'; +import { homeTranslationRef } from '../../translation'; -export const VisitListFew = () => ( - <> - - The more pages you visit, the more pages will appear here. - - -); +export const VisitListFew = () => { + const { t } = useTranslationRef(homeTranslationRef); + return ( + <> + + {t('visitList.few.title')} + + + ); +}; diff --git a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx index 475ef4b47e..a854203fdb 100644 --- a/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx +++ b/plugins/home/src/homePageComponents/FeaturedDocsCard/Content.tsx @@ -33,6 +33,8 @@ import { EntityFilterQuery } from '@backstage/catalog-client'; import Typography from '@material-ui/core/Typography'; import { makeStyles } from '@material-ui/core/styles'; import { stringifyEntityRef } from '@backstage/catalog-model'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; /** * Props customizing the component. @@ -88,7 +90,8 @@ const useStyles = makeStyles( export const Content = (props: FeaturedDocsCardProps): JSX.Element => { const { emptyState, filter, linkDestination, responseLimit, subLinkText } = props; - const linkText = subLinkText || 'LEARN MORE'; + const { t } = useTranslationRef(homeTranslationRef); + const linkText = subLinkText || t('featuredDocsCard.learnMoreTitle'); const styles = useStyles(); const catalogApi = useApi(catalogApiRef); const { @@ -153,14 +156,14 @@ export const Content = (props: FeaturedDocsCardProps): JSX.Element => { emptyState || ( - DOCS + {t('featuredDocsCard.empty.learnMoreLinkTitle')} } /> diff --git a/plugins/home/src/homePageComponents/QuickStart/Content.tsx b/plugins/home/src/homePageComponents/QuickStart/Content.tsx index b923970888..5317312626 100644 --- a/plugins/home/src/homePageComponents/QuickStart/Content.tsx +++ b/plugins/home/src/homePageComponents/QuickStart/Content.tsx @@ -20,6 +20,8 @@ import Typography from '@material-ui/core/Typography'; import Grid from '@material-ui/core/Grid'; import { ContentModal } from '@backstage/plugin-home-react'; import { useStyles } from './styles'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; /** * Props customizing the component. @@ -54,14 +56,15 @@ export type QuickStartCardProps = { */ export const Content = (props: QuickStartCardProps): JSX.Element => { const styles = useStyles(); + const { t } = useTranslationRef(homeTranslationRef); return ( <> - {props.cardDescription || 'Get started with Backstage'} + {props.cardDescription || t('quickStart.description')} { variant="h6" className={styles.link} > - {props.docsLinkTitle || 'Learn more'} + {props.docsLinkTitle || t('quickStart.learnMoreLinkTitle')} diff --git a/plugins/home/src/homePageComponents/StarredEntities/Content.tsx b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx index 80f5ee74b5..ffe1b36b36 100644 --- a/plugins/home/src/homePageComponents/StarredEntities/Content.tsx +++ b/plugins/home/src/homePageComponents/StarredEntities/Content.tsx @@ -29,6 +29,8 @@ import { ReactNode, useState } from 'react'; import useAsync from 'react-use/esm/useAsync'; import { StarredEntityListItem } from '../../components/StarredEntityListItem/StarredEntityListItem'; import { makeStyles } from '@material-ui/core/styles'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; const useStyles = makeStyles(theme => ({ tabs: { @@ -63,6 +65,7 @@ export const Content = ({ const catalogApi = useApi(catalogApiRef); const { starredEntities, toggleStarredEntity } = useStarredEntities(); const [activeTab, setActiveTab] = useState(0); + const { t } = useTranslationRef(homeTranslationRef); // Grab starred entities from catalog to ensure they still exist and also retrieve display titles const entities = useAsync(async () => { @@ -89,7 +92,7 @@ export const Content = ({ return ( {noStarredEntitiesMessage || - 'Click the star beside an entity name to add it to this list!'} + t('starredEntities.noStarredEntitiesMessage')} ); diff --git a/plugins/home/src/homePageComponents/VisitedByType/Actions.tsx b/plugins/home/src/homePageComponents/VisitedByType/Actions.tsx index a8e8bf7722..4c874f93a7 100644 --- a/plugins/home/src/homePageComponents/VisitedByType/Actions.tsx +++ b/plugins/home/src/homePageComponents/VisitedByType/Actions.tsx @@ -17,6 +17,8 @@ import { useCallback } from 'react'; import Button from '@material-ui/core/Button'; import { useContext } from './Context'; +import { useTranslationRef } from '@backstage/frontend-plugin-api'; +import { homeTranslationRef } from '../../translation'; export const Actions = () => { const { collapsed, setCollapsed, visits, numVisitsOpen, loading } = @@ -25,7 +27,10 @@ export const Actions = () => { () => setCollapsed(prevCollapsed => !prevCollapsed), [setCollapsed], ); - const label = collapsed ? 'View More' : 'View Less'; + const { t } = useTranslationRef(homeTranslationRef); + const label = collapsed + ? t('visitedByType.action.viewMore') + : t('visitedByType.action.viewLess'); if (!loading && visits.length <= numVisitsOpen) return <>; diff --git a/plugins/home/src/translation.ts b/plugins/home/src/translation.ts new file mode 100644 index 0000000000..4070dde28c --- /dev/null +++ b/plugins/home/src/translation.ts @@ -0,0 +1,81 @@ +/* + * 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 { createTranslationRef } from '@backstage/frontend-plugin-api'; + +/** + * @alpha + */ +export const homeTranslationRef = createTranslationRef({ + id: 'home', + messages: { + addWidgetDialog: { + title: 'Add new widget to dashboard', + }, + customHomepageButtons: { + edit: 'Edit', + restoreDefaults: 'Restore defaults', + clearAll: 'Clear all', + addWidget: 'Add widget', + save: 'Save', + }, + customHomepage: { + noWidgets: "No widgets added. Start by clicking the 'Add widget' button.", + }, + widgetSettingsOverlay: { + editSettingsTooptip: 'Edit settings', + deleteWidgetTooltip: 'Delete widget', + submitButtonTitle: 'Submit', + cancelButtonTitle: 'Cancel', + }, + starredEntityListItem: { + removeFavoriteEntityTitle: 'Remove entity from favorites', + }, + visitList: { + empty: { + title: 'There are no visits to show yet.', + description: + 'Once you start using Backstage, your visits will appear here as a quick link to carry on where you left off.', + }, + few: { + title: 'The more pages you visit, the more pages will appear here.', + }, + }, + quickStart: { + title: 'Onboarding', + description: 'Get started with Backstage', + learnMoreLinkTitle: 'Learn more', + }, + starredEntities: { + noStarredEntitiesMessage: + 'Click the star beside an entity name to add it to this list!', + }, + visitedByType: { + action: { + viewMore: 'View more', + viewLess: 'View less', + }, + }, + featuredDocsCard: { + learnMoreTitle: 'LEARN MORE', + empty: { + title: 'No documents to show', + description: + 'Create your own document. Check out our Getting Started Information', + learnMoreLinkTitle: 'DOCS', + }, + }, + }, +}); diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index eab166f2b5..36837719ff 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,41 @@ # @backstage/plugin-kubernetes-backend +## 0.19.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-kubernetes-node@0.3.2-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + +## 0.19.7 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-kubernetes-node@0.3.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + ## 0.19.7-next.1 ### Patch Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index bbd01f2c45..84f38b7bc8 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.7-next.1", + "version": "0.19.8-next.0", "description": "A Backstage backend plugin that integrates towards Kubernetes", "backstage": { "role": "backend-plugin", diff --git a/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts b/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts index 462312a404..50c50d7ffa 100644 --- a/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts +++ b/plugins/kubernetes-backend/src/routes/resourceRoutes.test.ts @@ -18,30 +18,18 @@ import request from 'supertest'; import { mockCredentials, mockServices, - type ServiceMock, startTestBackend, } from '@backstage/backend-test-utils'; import { kubernetesObjectsProviderExtensionPoint } from '@backstage/plugin-kubernetes-node'; -import { - createBackendModule, - type PermissionsService, -} from '@backstage/backend-plugin-api'; +import { createBackendModule } from '@backstage/backend-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { ExtendedHttpServer } from '@backstage/backend-defaults/rootHttpRouter'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; describe('resourcesRoutes', () => { let app: ExtendedHttpServer; - const permissionsMock: ServiceMock = - mockServices.permissions.mock({ - authorize: jest.fn(), - authorizeConditional: jest.fn(), - }); const startPermissionDeniedTestServer = async () => { - permissionsMock.authorize.mockResolvedValue([ - { result: AuthorizeResult.DENY }, - ]); const { server } = await startTestBackend({ features: [ mockServices.rootConfig.factory({ @@ -52,7 +40,7 @@ describe('resourcesRoutes', () => { }, }, }), - permissionsMock.factory, + mockServices.permissions.factory({ result: AuthorizeResult.DENY }), import('@backstage/plugin-kubernetes-backend'), ], }); diff --git a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts index 3046232bad..a3353eec43 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesBuilder.test.ts @@ -35,7 +35,6 @@ import { } from './KubernetesProxy'; import { setupServer } from 'msw/node'; import { - ServiceMock, mockCredentials, mockServices, registerMswTestHooks, @@ -43,10 +42,7 @@ import { } from '@backstage/backend-test-utils'; import { rest } from 'msw'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; -import { - PermissionsService, - createBackendModule, -} from '@backstage/backend-plugin-api'; +import { createBackendModule } from '@backstage/backend-plugin-api'; import { AuthMetadata, KubernetesObjectsProvider, @@ -64,11 +60,6 @@ describe('API integration tests', () => { const happyK8SResult = { items: [{ clusterOne: { pods: [{ metadata: { name: 'pod1' } }] } }], }; - const permissionsMock: ServiceMock = - mockServices.permissions.mock({ - authorize: jest.fn(), - authorizeConditional: jest.fn(), - }); const minimalValidConfigService = mockServices.rootConfig.factory({ data: { kubernetes: { @@ -93,13 +84,10 @@ describe('API integration tests', () => { }, }); const startPermissionDeniedTestServer = async () => { - permissionsMock.authorize.mockResolvedValue([ - { result: AuthorizeResult.DENY }, - ]); const { server } = await startTestBackend({ features: [ minimalValidConfigService, - permissionsMock.factory, + mockServices.permissions.factory({ result: AuthorizeResult.DENY }), import('@backstage/plugin-kubernetes-backend'), ], }); diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index 3ed8b25c56..3ef7f95b7d 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -22,7 +22,6 @@ import { registerMswTestHooks, } from '@backstage/backend-test-utils'; import { NotFoundError } from '@backstage/errors'; -import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { ANNOTATION_KUBERNETES_AUTH_PROVIDER, KubernetesRequestAuth, @@ -78,7 +77,7 @@ describe('KubernetesProxy', () => { >(), }; - const permissionApi = mockServices.permissions.mock(); + const permissionApi = mockServices.permissions(); const mockDisocveryApi = mockServices.discovery.mock(); registerMswTestHooks(worker); @@ -156,9 +155,6 @@ describe('KubernetesProxy', () => { authStrategy, discovery: mockDisocveryApi, }); - permissionApi.authorize.mockResolvedValue([ - { result: AuthorizeResult.ALLOW }, - ]); }); it('should return a ERROR_NOT_FOUND if no clusters are found', async () => { diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index bb7a443367..3476853941 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.27-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + - @backstage/plugin-kubernetes-react@0.5.9-next.1 + +## 0.0.27-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-kubernetes-react@0.5.9-next.0 + - @backstage/plugin-permission-react@0.4.35 + +## 0.0.26 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/plugin-kubernetes-react@0.5.8 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-permission-react@0.4.35 + +## 0.0.26-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-kubernetes-react@0.5.8-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.7 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-permission-react@0.4.34 + ## 0.0.26-next.1 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index bb31ebeceb..57758cd37a 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.26-next.1", + "version": "0.0.27-next.1", "description": "A Backstage plugin that shows details of Kubernetes clusters", "backstage": { "role": "frontend-plugin", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index d9c4ff3c5b..c0c9e00901 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-kubernetes-common +## 0.9.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + ## 0.9.5 ### Patch Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 1a5266a280..e8ab01e99c 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-common", - "version": "0.9.5", + "version": "0.9.6-next.0", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index 887629d69a..6b45f69bd0 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-kubernetes-node +## 0.3.2-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + +## 0.3.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5 + ## 0.3.1-next.1 ### Patch Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 598c3e4a7f..134b58768c 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.1-next.1", + "version": "0.3.2-next.0", "description": "Node.js library for the kubernetes plugin", "backstage": { "role": "node-library", diff --git a/plugins/kubernetes-node/report.api.md b/plugins/kubernetes-node/report.api.md index af0c2c599a..5758db9ea1 100644 --- a/plugins/kubernetes-node/report.api.md +++ b/plugins/kubernetes-node/report.api.md @@ -5,7 +5,6 @@ ```ts import { AuthenticationStrategy as AuthenticationStrategy_2 } from '@backstage/plugin-kubernetes-node'; import { BackstageCredentials } from '@backstage/backend-plugin-api'; -import { ClusterDetails as ClusterDetails_2 } from '@backstage/plugin-kubernetes-node'; import { CustomResourceMatcher } from '@backstage/plugin-kubernetes-common'; import { Entity } from '@backstage/catalog-model'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; @@ -256,7 +255,7 @@ export class PinnipedHelper { constructor(logger: LoggerService); // (undocumented) tokenCredentialRequest( - clusterDetails: ClusterDetails_2, + clusterDetails: ClusterDetails, pinnipedParams: PinnipedParameters, ): Promise; } diff --git a/plugins/kubernetes-node/src/auth/PinnipedHelper.ts b/plugins/kubernetes-node/src/auth/PinnipedHelper.ts index 39421ecd62..d6c39d55de 100644 --- a/plugins/kubernetes-node/src/auth/PinnipedHelper.ts +++ b/plugins/kubernetes-node/src/auth/PinnipedHelper.ts @@ -15,9 +15,9 @@ */ import { LoggerService } from '@backstage/backend-plugin-api'; -import { ClusterDetails } from '@backstage/plugin-kubernetes-node'; import * as https from 'https'; import fetch, { RequestInit } from 'node-fetch'; +import { ClusterDetails } from '../types/types'; /** * diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index 497439cf10..b3f379bd12 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,52 @@ # @backstage/plugin-kubernetes-react +## 0.5.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + +## 0.5.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5 + +## 0.5.8 + +### Patch Changes + +- 3ea5d1b: chore(deps): Update deprecated xterm to new package under @xterm +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5 + +## 0.5.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-kubernetes-common@0.9.5 + ## 0.5.7 ### Patch Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 28dd746e3b..728e6357a0 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes-react", - "version": "0.5.7", + "version": "0.5.9-next.1", "description": "Web library for the kubernetes-react plugin", "backstage": { "role": "web-library", @@ -66,15 +66,15 @@ "@material-ui/core": "^4.9.13", "@material-ui/icons": "^4.11.3", "@material-ui/lab": "^4.0.0-alpha.61", + "@xterm/addon-attach": "^0.11.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/xterm": "^5.5.0", "cronstrue": "^2.32.0", "js-yaml": "^4.1.0", "kubernetes-models": "^4.3.1", "lodash": "^4.17.21", "luxon": "^3.0.0", - "react-use": "^17.4.0", - "xterm": "^5.3.0", - "xterm-addon-attach": "^0.9.0", - "xterm-addon-fit": "^0.8.0" + "react-use": "^17.4.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.tsx b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.tsx index eeaf535a05..a3ef823356 100644 --- a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.tsx +++ b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminal.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import 'xterm/css/xterm.css'; +import '@xterm/xterm/css/xterm.css'; import { discoveryApiRef, useApi } from '@backstage/core-plugin-api'; import { ClusterAttributes } from '@backstage/plugin-kubernetes-common'; import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; import { useRef, useEffect, useMemo, useState } from 'react'; -import { Terminal } from 'xterm'; -import { FitAddon } from 'xterm-addon-fit'; +import { Terminal } from '@xterm/xterm'; +import { FitAddon } from '@xterm/addon-fit'; import { PodExecTerminalAttachAddon } from './PodExecTerminalAttachAddon'; diff --git a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalAttachAddon.ts b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalAttachAddon.ts index dba58a6746..55171e1cb1 100644 --- a/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalAttachAddon.ts +++ b/plugins/kubernetes-react/src/components/PodExecTerminal/PodExecTerminalAttachAddon.ts @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { AttachAddon, IAttachOptions } from 'xterm-addon-attach'; +import { AttachAddon, IAttachOptions } from '@xterm/addon-attach'; export class PodExecTerminalAttachAddon extends AttachAddon { #textEncoder = new TextEncoder(); diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index 4d6678299d..923f93dae5 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,67 @@ # @backstage/plugin-kubernetes +## 0.12.9-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-kubernetes-common@0.9.6-next.0 + - @backstage/plugin-kubernetes-react@0.5.9-next.1 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.12.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-kubernetes-react@0.5.9-next.0 + - @backstage/plugin-permission-react@0.4.35 + +## 0.12.8 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- 3ea5d1b: chore(deps): Update deprecated xterm to new package under @xterm +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/plugin-kubernetes-react@0.5.8 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-permission-react@0.4.35 + +## 0.12.8-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/plugin-kubernetes-react@0.5.8-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/plugin-kubernetes-common@0.9.5 + - @backstage/plugin-permission-react@0.4.34 + ## 0.12.8-next.1 ### Patch Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index d0fe8bc9d9..3baf628c86 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-kubernetes", - "version": "0.12.8-next.1", + "version": "0.12.9-next.1", "description": "A Backstage plugin that integrates towards Kubernetes", "backstage": { "role": "frontend-plugin", @@ -71,14 +71,14 @@ "@kubernetes-models/base": "^5.0.0", "@kubernetes/client-node": "1.1.2", "@material-ui/core": "^4.12.2", + "@xterm/addon-attach": "^0.11.0", + "@xterm/addon-fit": "^0.10.0", + "@xterm/xterm": "^5.5.0", "cronstrue": "^2.2.0", "js-yaml": "^4.0.0", "kubernetes-models": "^4.1.0", "lodash": "^4.17.21", - "luxon": "^3.0.0", - "xterm": "^5.2.1", - "xterm-addon-attach": "^0.9.0", - "xterm-addon-fit": "^0.8.0" + "luxon": "^3.0.0" }, "devDependencies": { "@backstage/cli": "workspace:^", diff --git a/plugins/mcp-actions-backend/.eslintrc.js b/plugins/mcp-actions-backend/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/mcp-actions-backend/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/mcp-actions-backend/CHANGELOG.md b/plugins/mcp-actions-backend/CHANGELOG.md new file mode 100644 index 0000000000..0022dbb891 --- /dev/null +++ b/plugins/mcp-actions-backend/CHANGELOG.md @@ -0,0 +1,40 @@ +# @backstage/plugin-mcp-actions-backend + +## 0.1.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.1.1-next.0 + +### Patch Changes + +- 6bc0799: Fixed the example in the README for generating a static token by adding a subject field +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/catalog-client@1.10.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.1.0 + +### Minor Changes + +- 4ed0fb6: Initial implementation of an `mcp-actions` backend + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 diff --git a/plugins/mcp-actions-backend/README.md b/plugins/mcp-actions-backend/README.md new file mode 100644 index 0000000000..c44bd7f0a6 --- /dev/null +++ b/plugins/mcp-actions-backend/README.md @@ -0,0 +1,135 @@ +# MCP Actions Backend + +This plugin exposes Backstage actions as MCP (Model Context Protocol) tools, allowing AI clients to discover and invoke registered actions in your Backstage backend. + +## Installation + +This plugin is installed via the `@backstage/plugin-mcp-actions-backend` package. To install it to your backend package, run the following command: + +```bash +# From your root directory +yarn --cwd packages/backend add @backstage/plugin-mcp-actions-backend +``` + +Then add the plugin to your backend in `packages/backend/src/index.ts`: + +```ts +const backend = createBackend(); +// ... +backend.add(import('@backstage/plugin-mcp-actions-backend')); +``` + +## Configuration + +### Configuring Actions Registry + +The MCP Actions Backend exposes actions that are registered with the Actions Registry. You can register actions from specific plugins by configuring the `pluginSources` in your app configuration: + +```yaml +backend: + actions: + pluginSources: + - 'catalog' + - 'my-custom-plugin' +``` + +Actions from these plugins will be discovered and exposed as MCP tools. Each action must be registered using the Actions Registry Service in the respective plugin: + +```ts +// In your plugin +import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha'; + +export const myPlugin = createBackendPlugin({ + pluginId: 'my-custom-plugin', + register(env) { + env.registerInit({ + deps: { + actionsRegistry: actionsRegistryServiceRef, + }, + async init({ actionsRegistry }) { + actionsRegistry.register({ + name: 'greet-user', + title: 'Greet User', + description: 'Generate a personalized greeting', + schema: { + input: z => + z.object({ + name: z.string().describe('The name of the person to greet'), + }), + output: z => + z.object({ + greeting: z.string().describe('The generated greeting'), + }), + }, + action: async ({ input }) => ({ + output: { greeting: `Hello ${input.name}!` }, + }), + }); + }, + }); + }, +}); +``` + +### Authentication Configuration + +By default, the Backstage backend requires authentication for all requests. + +#### External Access with Static Tokens + +> This is meant to be a temporary workaround until work on [device authentication](https://github.com/backstage/backstage/pull/27680) is completed. +> This will make authentication for MCP clients and CLI's in Backstage easier than having to configure static tokens. + +Configure external access with static tokens in your app configuration: + +```yaml +backend: + auth: + externalAccess: + - type: static + options: + token: ${MCP_TOKEN} + subject: mcp-clients + accessRestrictions: + - plugin: mcp-actions + - plugin: catalog +``` + +Generate a secure token: + +```bash +node -p 'require("crypto").randomBytes(24).toString("base64")' +``` + +Set the `MCP_TOKEN` environment variable with this token, and configure your MCP client to use it in the [Authorization header](#configuring-mcp-clients) + +## Configuring MCP Clients + +The MCP server supports both Server-Sent Events (SSE) and Streamable HTTP protocols. + +The SSE protocol is deprecated, and should be avoided as it will be removed in a future release. + +- `Streamable HTTP`: `http://localhost:7007/api/mcp-actions/v1` +- `SSE`: `http://localhost:7007/api/mcp-actions/v1/sse` + +There's a few different ways to configure MCP tools, but here's a snippet of the most common. + +```json +{ + "mcpServers": { + "backstage-actions": { + // you can also replace this with the public / internal URL of the deployed backend. + "url": "http://localhost:7007/api/mcp-actions/v1", + "headers": { + "Authorization": "Bearer ${MCP_TOKEN}" + } + } + } +} +``` + +## Development + +This plugin backend can be started in a standalone mode from directly in this package with `yarn start`. It is a limited setup that is most convenient when developing the plugin backend itself. + +If you want to run the entire project, including the frontend, run `yarn start` from the root directory. diff --git a/plugins/mcp-actions-backend/catalog-info.yaml b/plugins/mcp-actions-backend/catalog-info.yaml new file mode 100644 index 0000000000..1a0199210e --- /dev/null +++ b/plugins/mcp-actions-backend/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-mcp-actions-backend + title: '@backstage/plugin-mcp-actions-backend' +spec: + lifecycle: experimental + type: backstage-backend-plugin + owner: maintainers diff --git a/plugins/mcp-actions-backend/dev/index.ts b/plugins/mcp-actions-backend/dev/index.ts new file mode 100644 index 0000000000..74a6d814f8 --- /dev/null +++ b/plugins/mcp-actions-backend/dev/index.ts @@ -0,0 +1,84 @@ +/* + * 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 { createBackend } from '@backstage/backend-defaults'; +import { createBackendPlugin } from '@backstage/backend-plugin-api'; +import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha'; +import { mockServices } from '@backstage/backend-test-utils'; +import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; + +const backend = createBackend(); + +backend.add(mockServices.auth.factory()); +backend.add(mockServices.httpAuth.factory()); + +// TEMPLATE NOTE: +// Rather than using a real catalog you can use a mock with a fixed set of entities. +backend.add( + catalogServiceMock.factory({ + entities: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'sample', + title: 'Sample Component', + }, + spec: { + type: 'service', + }, + }, + ], + }), +); + +backend.add( + createBackendPlugin({ + pluginId: 'local', + register({ registerInit }) { + registerInit({ + deps: { + actionsRegistry: actionsRegistryServiceRef, + }, + async init({ actionsRegistry }) { + actionsRegistry.register({ + name: 'make-greeting', + title: 'Test Action', + description: 'Test Action', + schema: { + input: z => + z.object({ + name: z.string(), + }), + output: z => + z.object({ + greeting: z.string(), + }), + }, + action: async ({ input }) => ({ + output: { + greeting: `Hello ${input.name}!`, + }, + }), + }); + }, + }); + }, + }), +); + +backend.add(import('../src')); + +backend.start(); diff --git a/plugins/mcp-actions-backend/package.json b/plugins/mcp-actions-backend/package.json new file mode 100644 index 0000000000..5617360268 --- /dev/null +++ b/plugins/mcp-actions-backend/package.json @@ -0,0 +1,53 @@ +{ + "name": "@backstage/plugin-mcp-actions-backend", + "version": "0.1.1-next.1", + "backstage": { + "role": "backend-plugin", + "pluginId": "mcp-actions", + "pluginPackages": [ + "@backstage/plugin-mcp-actions-backend" + ] + }, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/mcp-actions-backend" + }, + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "files": [ + "dist" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/backend-defaults": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/catalog-client": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "@backstage/types": "workspace:^", + "@modelcontextprotocol/sdk": "^1.12.3", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "zod": "^3.22.4" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@types/express": "^4.17.6" + } +} diff --git a/plugins/mcp-actions-backend/report.api.md b/plugins/mcp-actions-backend/report.api.md new file mode 100644 index 0000000000..2f1b9fcbf0 --- /dev/null +++ b/plugins/mcp-actions-backend/report.api.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-mcp-actions-backend" + +> 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'; + +// @public +const mcpPlugin: BackendFeature; +export default mcpPlugin; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/mcp-actions-backend/src/index.ts b/plugins/mcp-actions-backend/src/index.ts new file mode 100644 index 0000000000..75fa097c85 --- /dev/null +++ b/plugins/mcp-actions-backend/src/index.ts @@ -0,0 +1,16 @@ +/* + * 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 { mcpPlugin as default } from './plugin'; diff --git a/plugins/mcp-actions-backend/src/plugin.test.ts b/plugins/mcp-actions-backend/src/plugin.test.ts new file mode 100644 index 0000000000..ace988cfc1 --- /dev/null +++ b/plugins/mcp-actions-backend/src/plugin.test.ts @@ -0,0 +1,165 @@ +/* + * 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 { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { mcpPlugin } from './plugin'; +import { actionsRegistryServiceRef } from '@backstage/backend-plugin-api/alpha'; +import { createBackendPlugin } from '@backstage/backend-plugin-api'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; +import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js'; +import { ListToolsResultSchema } from '@modelcontextprotocol/sdk/types'; + +describe('Mcp Backend', () => { + const mockPluginWithActions = createBackendPlugin({ + pluginId: 'local', + register({ registerInit }) { + registerInit({ + deps: { actionsRegistry: actionsRegistryServiceRef }, + async init({ actionsRegistry }) { + actionsRegistry.register({ + name: 'make-greeting', + title: 'Make Greeting', + description: 'Make a greeting', + schema: { + input: z => z.object({ name: z.string() }), + output: z => z.object({ greeting: z.string() }), + }, + action: async ({ input }) => ({ + output: { greeting: `Hello ${input.name}!` }, + }), + }); + }, + }); + }, + }); + + const getContext = async () => { + const { server } = await startTestBackend({ + features: [ + mcpPlugin, + mockPluginWithActions, + mockServices.rootConfig.factory({ + data: { + backend: { + actions: { + pluginSources: ['local'], + }, + }, + }, + }), + ], + }); + + const client = new Client({ + name: 'test client', + version: '1.0', + }); + + const address = server.address(); + if (typeof address !== 'object' || !('port' in address!)) { + throw new Error('server broke'); + } + + return { + client, + serverAddress: `http://localhost:${address.port}`, + }; + }; + + it('should support streamable spec', async () => { + const { client, serverAddress } = await getContext(); + const transport = new StreamableHTTPClientTransport( + new URL(`${serverAddress}/api/mcp-actions/v1`), + ); + + await client.connect(transport); + + const result = await client.request( + { + method: 'tools/list', + }, + ListToolsResultSchema, + ); + + expect(result.tools).toEqual([ + { + annotations: { + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, + readOnlyHint: false, + title: 'Make Greeting', + }, + description: 'Make a greeting', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + type: 'object', + }, + name: 'make-greeting', + }, + ]); + }); + + it('should support sse spec', async () => { + const { client, serverAddress } = await getContext(); + const transport = new SSEClientTransport( + new URL(`${serverAddress}/api/mcp-actions/v1/sse`), + ); + + await client.connect(transport); + + const result = await client.request( + { + method: 'tools/list', + }, + ListToolsResultSchema, + ); + + await client.close(); + + expect(result.tools).toEqual([ + { + annotations: { + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, + readOnlyHint: false, + title: 'Make Greeting', + }, + description: 'Make a greeting', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], + type: 'object', + }, + name: 'make-greeting', + }, + ]); + }); +}); diff --git a/plugins/mcp-actions-backend/src/plugin.ts b/plugins/mcp-actions-backend/src/plugin.ts new file mode 100644 index 0000000000..f1e89417e9 --- /dev/null +++ b/plugins/mcp-actions-backend/src/plugin.ts @@ -0,0 +1,72 @@ +/* + * 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, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { json, Router } from 'express'; +import { McpService } from './services/McpService'; +import { createStreamableRouter } from './routers/createStreamableRouter'; +import { createSseRouter } from './routers/createSseRouter'; +import { + actionsRegistryServiceRef, + actionsServiceRef, +} from '@backstage/backend-plugin-api/alpha'; + +/** + * mcpPlugin backend plugin + * + * @public + */ +export const mcpPlugin = createBackendPlugin({ + pluginId: 'mcp-actions', + register(env) { + env.registerInit({ + deps: { + logger: coreServices.logger, + auth: coreServices.auth, + httpAuth: coreServices.httpAuth, + httpRouter: coreServices.httpRouter, + actions: actionsServiceRef, + registry: actionsRegistryServiceRef, + }, + async init({ actions, logger, httpRouter, httpAuth }) { + const mcpService = await McpService.create({ + actions, + }); + + const sseRouter = createSseRouter({ + mcpService, + httpAuth, + }); + + const streamableRouter = createStreamableRouter({ + mcpService, + httpAuth, + logger, + }); + + const router = Router(); + router.use(json()); + + router.use('/v1/sse', sseRouter); + router.use('/v1', streamableRouter); + + httpRouter.use(router); + }, + }); + }, +}); diff --git a/plugins/mcp-actions-backend/src/routers/createSseRouter.ts b/plugins/mcp-actions-backend/src/routers/createSseRouter.ts new file mode 100644 index 0000000000..19bcf288e9 --- /dev/null +++ b/plugins/mcp-actions-backend/src/routers/createSseRouter.ts @@ -0,0 +1,73 @@ +/* + * 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 PromiseRouter from 'express-promise-router'; +import { Router } from 'express'; +import { McpService } from '../services/McpService'; +import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js'; +import { HttpAuthService } from '@backstage/backend-plugin-api'; + +/** + * Legacy SSE endpoint for older clients, hopefully will not be needed for much longer. + */ +export const createSseRouter = ({ + mcpService, + httpAuth, +}: { + mcpService: McpService; + httpAuth: HttpAuthService; +}): Router => { + const router = PromiseRouter(); + const transportsToSessionId = new Map(); + + router.get('/', async (req, res) => { + const server = mcpService.getServer({ + credentials: await httpAuth.credentials(req), + }); + + const transport = new SSEServerTransport( + `${req.originalUrl}/messages`, + res, + ); + + transportsToSessionId.set(transport.sessionId, transport); + + res.on('close', () => { + transportsToSessionId.delete(transport.sessionId); + }); + + await server.connect(transport); + }); + + router.post('/messages', async (req, res) => { + const sessionId = req.query.sessionId as string; + + if (!sessionId) { + res.status(400).contentType('text/plain').write('sessionId is required'); + return; + } + + const transport = transportsToSessionId.get(sessionId); + if (transport) { + await transport.handlePostMessage(req, res, req.body); + } else { + res + .status(400) + .contentType('text/plain') + .write(`No transport found for sessionId "${sessionId}"`); + } + }); + return router; +}; diff --git a/plugins/mcp-actions-backend/src/routers/createStreamableRouter.ts b/plugins/mcp-actions-backend/src/routers/createStreamableRouter.ts new file mode 100644 index 0000000000..9d6fd1840b --- /dev/null +++ b/plugins/mcp-actions-backend/src/routers/createStreamableRouter.ts @@ -0,0 +1,100 @@ +/* + * 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 PromiseRouter from 'express-promise-router'; +import { Router } from 'express'; +import { McpService } from '../services/McpService'; +import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; +import { HttpAuthService, LoggerService } from '@backstage/backend-plugin-api'; +import { isError } from '@backstage/errors'; + +export const createStreamableRouter = ({ + mcpService, + httpAuth, + logger, +}: { + mcpService: McpService; + logger: LoggerService; + httpAuth: HttpAuthService; +}): Router => { + const router = PromiseRouter(); + + router.post('/', async (req, res) => { + try { + const server = mcpService.getServer({ + credentials: await httpAuth.credentials(req), + }); + + const transport = new StreamableHTTPServerTransport({ + // stateless implementation for now, so that we can support multiple + // instances of the server backend, and avoid sticky sessions. + sessionIdGenerator: undefined, + }); + + await server.connect(transport); + await transport.handleRequest(req, res, req.body); + + res.on('close', () => { + transport.close(); + server.close(); + }); + } catch (error) { + if (isError(error)) { + logger.error(error.message); + } + + if (!res.headersSent) { + res.status(500).json({ + jsonrpc: '2.0', + error: { + code: -32603, + message: 'Internal server error', + }, + id: null, + }); + } + } + }); + + router.get('/', async (_, res) => { + // We only support POST requests, so we return a 405 error for all other methods. + res.writeHead(405).end( + JSON.stringify({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not allowed.', + }, + id: null, + }), + ); + }); + + router.delete('/', async (_, res) => { + // We only support POST requests, so we return a 405 error for all other methods. + res.writeHead(405).end( + JSON.stringify({ + jsonrpc: '2.0', + error: { + code: -32000, + message: 'Method not allowed.', + }, + id: null, + }), + ); + }); + + return router; +}; diff --git a/plugins/mcp-actions-backend/src/services/McpService.test.ts b/plugins/mcp-actions-backend/src/services/McpService.test.ts new file mode 100644 index 0000000000..e3339be1d5 --- /dev/null +++ b/plugins/mcp-actions-backend/src/services/McpService.test.ts @@ -0,0 +1,191 @@ +/* + * 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 { mockCredentials } from '@backstage/backend-test-utils'; +import { McpService } from './McpService'; +import { actionsRegistryServiceMock } from '@backstage/backend-test-utils/alpha'; +import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; +import { Client } from '@modelcontextprotocol/sdk/client/index.js'; +import { + CallToolResultSchema, + ListToolsResultSchema, +} from '@modelcontextprotocol/sdk/types.js'; + +describe('McpService', () => { + it('should list the available actions as tools in the mcp backend', async () => { + const mockActionsRegistry = actionsRegistryServiceMock(); + mockActionsRegistry.register({ + name: 'mock-action', + title: 'Test', + description: 'Test', + schema: { + input: z => z.object({ input: z.string() }), + output: z => z.object({ output: z.string() }), + }, + action: async () => ({ output: { output: 'test' } }), + }); + + const mcpService = await McpService.create({ + actions: mockActionsRegistry, + }); + + const server = mcpService.getServer({ + credentials: mockCredentials.user(), + }); + + const client = new Client({ + name: 'test client', + version: '1.0', + }); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + server.connect(serverTransport), + ]); + + const result = await client.request( + { + method: 'tools/list', + }, + ListToolsResultSchema, + ); + + expect(result.tools).toEqual([ + { + annotations: { + destructiveHint: true, + idempotentHint: false, + openWorldHint: false, + readOnlyHint: false, + title: 'Test', + }, + description: 'Test', + inputSchema: { + $schema: 'http://json-schema.org/draft-07/schema#', + additionalProperties: false, + properties: { + input: { + type: 'string', + }, + }, + required: ['input'], + type: 'object', + }, + name: 'mock-action', + }, + ]); + }); + + it('should call the action when the tool is invoked', async () => { + const mockActionsRegistry = actionsRegistryServiceMock(); + const mockAction = jest.fn(async () => ({ output: { output: 'test' } })); + + mockActionsRegistry.register({ + name: 'mock-action', + title: 'Test', + description: 'Test', + schema: { + input: z => z.object({ input: z.string() }), + output: z => z.object({ output: z.string() }), + }, + action: mockAction, + }); + + const mcpService = await McpService.create({ + actions: mockActionsRegistry, + }); + + const server = mcpService.getServer({ + credentials: mockCredentials.user(), + }); + + const client = new Client({ + name: 'test client', + version: '1.0', + }); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + server.connect(serverTransport), + ]); + + const result = await client.request( + { + method: 'tools/call', + params: { name: 'mock-action', arguments: { input: 'test' } }, + }, + CallToolResultSchema, + ); + + expect(mockAction).toHaveBeenCalledWith( + expect.objectContaining({ + credentials: mockCredentials.user(), + input: { input: 'test' }, + logger: expect.anything(), + }), + ); + + expect(result.content).toEqual([ + { + type: 'text', + text: [ + '```json', + JSON.stringify({ output: 'test' }, null, 2), + '```', + ].join('\n'), + }, + ]); + }); + + it('should return an error when the action is not found', async () => { + const mcpService = await McpService.create({ + actions: actionsRegistryServiceMock(), + }); + + const server = mcpService.getServer({ + credentials: mockCredentials.user(), + }); + + const client = new Client({ + name: 'test client', + version: '1.0', + }); + + const [clientTransport, serverTransport] = + InMemoryTransport.createLinkedPair(); + + await Promise.all([ + client.connect(clientTransport), + server.connect(serverTransport), + ]); + + await expect( + client.request( + { + method: 'tools/call', + params: { name: 'mock-action', arguments: { input: 'test' } }, + }, + CallToolResultSchema, + ), + ).rejects.toThrow('Action "mock-action" not found'); + }); +}); diff --git a/plugins/mcp-actions-backend/src/services/McpService.ts b/plugins/mcp-actions-backend/src/services/McpService.ts new file mode 100644 index 0000000000..9c44e6d4c1 --- /dev/null +++ b/plugins/mcp-actions-backend/src/services/McpService.ts @@ -0,0 +1,98 @@ +/* + * 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 { BackstageCredentials } from '@backstage/backend-plugin-api'; +import { Server as McpServer } from '@modelcontextprotocol/sdk/server/index.js'; +import { + ListToolsRequestSchema, + CallToolRequestSchema, +} from '@modelcontextprotocol/sdk/types.js'; +import { JsonObject } from '@backstage/types'; +import { ActionsService } from '@backstage/backend-plugin-api/alpha'; +import { version } from '@backstage/plugin-mcp-actions-backend/package.json'; +import { NotFoundError } from '@backstage/errors'; + +export class McpService { + constructor(private readonly actions: ActionsService) {} + + static async create({ actions }: { actions: ActionsService }) { + return new McpService(actions); + } + + getServer({ credentials }: { credentials: BackstageCredentials }) { + const server = new McpServer( + { + name: 'backstage', + // TODO: this version will most likely change in the future. + version, + }, + { capabilities: { tools: {} } }, + ); + + server.setRequestHandler(ListToolsRequestSchema, async () => { + // TODO: switch this to be configuration based later + const { actions } = await this.actions.list({ credentials }); + + return { + tools: actions.map(action => ({ + inputSchema: action.schema.input, + // todo(blam): this is unfortunately not supported by most clients yet. + // When this is provided you need to provide structuredContent instead. + // outputSchema: action.schema.output, + name: action.name, + description: action.description, + annotations: { + title: action.title, + destructiveHint: action.attributes.destructive, + idempotentHint: action.attributes.idempotent, + readOnlyHint: action.attributes.readOnly, + openWorldHint: false, + }, + })), + }; + }); + + server.setRequestHandler(CallToolRequestSchema, async ({ params }) => { + const { actions } = await this.actions.list({ credentials }); + const action = actions.find(a => a.name === params.name); + + if (!action) { + throw new NotFoundError(`Action "${params.name}" not found`); + } + + const { output } = await this.actions.invoke({ + id: action.id, + input: params.arguments as JsonObject, + credentials, + }); + + return { + // todo(blam): unfortunately structuredContent is not supported by most clients yet. + // so the validation for the output happens in the default actions registry + // and we return it as json text instead for now. + content: [ + { + type: 'text', + text: ['```json', JSON.stringify(output, null, 2), '```'].join( + '\n', + ), + }, + ], + }; + }); + + return server; + } +} diff --git a/plugins/notifications-backend-module-email/CHANGELOG.md b/plugins/notifications-backend-module-email/CHANGELOG.md index 32bb0891e4..03e44141d7 100644 --- a/plugins/notifications-backend-module-email/CHANGELOG.md +++ b/plugins/notifications-backend-module-email/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-notifications-backend-module-email +## 0.3.11-next.1 + +### Patch Changes + +- f92c9fc: Add optional config for `ses` mail options with `sourceArn`, `fromArn`, `configurationSetName` + +## 0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-notifications-node@0.2.17-next.0 + +## 0.3.10 + +### Patch Changes + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + +## 0.3.10-next.2 + +### Patch Changes + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-notifications-node@0.2.16-next.1 + ## 0.3.10-next.1 ### Patch Changes diff --git a/plugins/notifications-backend-module-email/README.md b/plugins/notifications-backend-module-email/README.md index ed5bc22af3..c1ead48876 100644 --- a/plugins/notifications-backend-module-email/README.md +++ b/plugins/notifications-backend-module-email/README.md @@ -79,6 +79,11 @@ notifications: # Who to send email for broadcast notifications broadcastConfig: receiver: 'users' + # Optional SES config + # sesConfig: + # sourceArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com' + # fromArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com' + # configurationSetName: 'custom-config' # How many emails to send concurrently, defaults to 2 concurrencyLimit: 10 # How much to throttle between emails, defaults to 100ms diff --git a/plugins/notifications-backend-module-email/config.d.ts b/plugins/notifications-backend-module-email/config.d.ts index 97a6495246..7116826c5d 100644 --- a/plugins/notifications-backend-module-email/config.d.ts +++ b/plugins/notifications-backend-module-email/config.d.ts @@ -132,6 +132,23 @@ export interface Config { */ receiverEmails?: string[]; }; + /** + * Optional SES config for mail options. Allows for delegated sender + */ + sesConfig?: { + /** + * ARN of the identity to use as the source of the email + */ + sourceArn?: string; + /** + * ARN of the identity to use for the "From"/sender address of the email + */ + fromArn?: string; + /** + * Name of the configuration set to use when sending email via ses + */ + configurationSetName?: string; + }; cache?: { /** * Email cache TTL, defaults to 1 hour diff --git a/plugins/notifications-backend-module-email/package.json b/plugins/notifications-backend-module-email/package.json index b841dafc2e..6708b99cef 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.10-next.1", + "version": "0.3.11-next.1", "description": "The email backend module for the notifications plugin.", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts index 1408abcf9c..ebe1b90f35 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.test.ts @@ -442,4 +442,64 @@ describe('NotificationsEmailProcessor', () => { to: 'mock@backstage.io', }); }); + + it('should send email with ses config', async () => { + const SES_SENDMAIL_CONFIG = { + app: { + baseUrl: 'https://example.org', + }, + notifications: { + processors: { + email: { + transportConfig: { + transport: 'ses', + region: 'us-west-2', + }, + sender: 'backstage@backstage.io', + replyTo: 'no-reply@backstage.io', + sesConfig: { + sourceArn: + 'arn:aws:ses:us-west-2:123456789012:identity/example.com', + fromArn: + 'arn:aws:ses:us-west-2:123456789012:identity/example.com', + }, + }, + }, + }, + }; + (createTransport as jest.Mock).mockReturnValue(mockTransport); + const processor = new NotificationsEmailProcessor( + logger, + mockServices.rootConfig({ data: SES_SENDMAIL_CONFIG }), + catalogServiceMock({ entities: [DEFAULT_ENTITIES_RESPONSE.items[0]] }), + auth, + ); + + await processor.postProcess( + { + origin: 'plugin', + id: '1234', + user: 'user:default/mock', + created: new Date(), + payload: { title: 'notification' }, + }, + { + recipients: { type: 'entity', entityRef: 'user:default/mock' }, + payload: { title: 'notification' }, + }, + ); + + expect(sendmailMock).toHaveBeenCalledWith({ + from: 'backstage@backstage.io', + html: '

https://example.org/notifications

', + replyTo: 'no-reply@backstage.io', + subject: 'notification', + text: 'https://example.org/notifications', + to: 'mock@backstage.io', + ses: { + SourceArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com', + FromArn: 'arn:aws:ses:us-west-2:123456789012:identity/example.com', + }, + }); + }); }); diff --git a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts index 3496d431e3..39a0c5da02 100644 --- a/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts +++ b/plugins/notifications-backend-module-email/src/processor/NotificationsEmailProcessor.ts @@ -52,6 +52,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { private readonly transportConfig: Config; private readonly sender: string; private readonly replyTo?: string; + private readonly sesConfig?: Config; private readonly cacheTtl: number; private readonly concurrencyLimit: number; private readonly throttleInterval: number; @@ -76,6 +77,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { emailProcessorConfig.getOptionalConfig('broadcastConfig'); this.sender = emailProcessorConfig.getString('sender'); this.replyTo = emailProcessorConfig.getOptionalString('replyTo'); + this.sesConfig = emailProcessorConfig.getOptionalConfig('sesConfig'); this.concurrencyLimit = emailProcessorConfig.getOptionalNumber('concurrencyLimit') ?? 2; this.throttleInterval = emailProcessorConfig.has('throttleInterval') @@ -292,6 +294,24 @@ export class NotificationsEmailProcessor implements NotificationProcessor { return contentParts.join('\n\n'); } + private async getSesOptions() { + if (!this.sesConfig) { + return undefined; + } + const ses: Record = {}; + const sourceArn = this.sesConfig.getOptionalString('sourceArn'); + const fromArn = this.sesConfig.getOptionalString('fromArn'); + const configurationSetName = this.sesConfig.getOptionalString( + 'configurationSetName', + ); + + if (sourceArn) ses.SourceArn = sourceArn; + if (fromArn) ses.FromArn = fromArn; + if (configurationSetName) ses.ConfigurationSetName = configurationSetName; + + return Object.keys(ses).length > 0 ? ses : undefined; + } + private async sendPlainEmail(notification: Notification, emails: string[]) { const mailOptions = { from: this.sender, @@ -299,6 +319,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { html: this.getHtmlContent(notification), text: this.getTextContent(notification), replyTo: this.replyTo, + ses: await this.getSesOptions(), }; await this.sendMails(mailOptions, emails); @@ -316,6 +337,7 @@ export class NotificationsEmailProcessor implements NotificationProcessor { html: await this.templateRenderer?.getHtml?.(notification), text: await this.templateRenderer?.getText?.(notification), replyTo: this.replyTo, + ses: await this.getSesOptions(), }; await this.sendMails(mailOptions, emails); diff --git a/plugins/notifications-backend-module-slack/CHANGELOG.md b/plugins/notifications-backend-module-slack/CHANGELOG.md index 2effe53d63..b409402848 100644 --- a/plugins/notifications-backend-module-slack/CHANGELOG.md +++ b/plugins/notifications-backend-module-slack/CHANGELOG.md @@ -1,5 +1,31 @@ # @backstage/plugin-notifications-backend-module-slack +## 0.1.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-notifications-node@0.2.17-next.0 + +## 0.1.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + ## 0.1.2-next.1 ### Patch Changes diff --git a/plugins/notifications-backend-module-slack/package.json b/plugins/notifications-backend-module-slack/package.json index bb5ed460df..8d29c21438 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.2-next.1", + "version": "0.1.3-next.0", "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 065282a44c..ab216842e3 100644 --- a/plugins/notifications-backend/CHANGELOG.md +++ b/plugins/notifications-backend/CHANGELOG.md @@ -1,5 +1,88 @@ # @backstage/plugin-notifications-backend +## 0.5.8-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-signals-node@0.1.22-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-notifications-node@0.2.17-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.5.8-next.0 + +### Patch Changes + +- 9a5a73f: Fix `addTopic` migration when `user_settings` present +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-signals-node@0.1.21 + +## 0.5.7 + +### Patch Changes + +- 41d4d6e: Notifications are now automatically deleted after 1 year by default. + + There is a new scheduled task that runs every 24 hours to delete notifications older than 1 year. + This can be configured by setting the `notifications.retention` in the `app-config.yaml` file. + + ```yaml + notifications: + retention: 1y + ``` + + If the retention is set to false, notifications will not be automatically deleted. + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- 1fb5f06: Adds ability for user to turn on/off notifications for specific topics within an origin. +- ef9ab82: Notifications API will now return user as null always for broadcast notifications +- Updated dependencies + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-signals-node@0.1.21 + +## 0.5.7-next.2 + +### Patch Changes + +- 8a150bf: Internal changes to switch to the non-alpha `catalogServiceRef` +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.4-next.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-notifications-node@0.2.16-next.1 + - @backstage/plugin-signals-node@0.1.21-next.1 + ## 0.5.7-next.1 ### Patch Changes diff --git a/plugins/notifications-backend/config.d.ts b/plugins/notifications-backend/config.d.ts index 11040bed17..2b6f874f72 100644 --- a/plugins/notifications-backend/config.d.ts +++ b/plugins/notifications-backend/config.d.ts @@ -44,5 +44,10 @@ export interface Config { }[]; }[]; }; + /* + * Time to keep the notifications in the database, defaults to 365 days. + * Can be disabled by setting to false. + */ + retention?: HumanDuration | string | false; }; } diff --git a/plugins/notifications-backend/migrations/20250317_addTopic.js b/plugins/notifications-backend/migrations/20250317_addTopic.js index c8f78a5640..e17b8d0789 100644 --- a/plugins/notifications-backend/migrations/20250317_addTopic.js +++ b/plugins/notifications-backend/migrations/20250317_addTopic.js @@ -18,14 +18,10 @@ const crypto = require('crypto'); exports.up = async function up(knex) { await knex.schema.alterTable('user_settings', table => { table.string('topic').nullable().after('origin'); - table.string('settings_key_hash', 64).notNullable(); + table.string('settings_key_hash', 64).nullable(); table.dropUnique([], 'user_settings_unique_idx'); }); - await knex.schema.alterTable('user_settings', table => { - table.unique(['settings_key_hash'], 'user_settings_unique_idx'); - }); - const rows = await knex('user_settings').select('user', 'channel', 'origin'); for (const row of rows) { const rawKey = `${row.user}|${row.channel}|${row.origin}|}`; @@ -35,10 +31,14 @@ exports.up = async function up(knex) { user: row.user, channel: row.channel, origin: row.origin, - topic: row.topic, }) .update({ settings_key_hash: hash }); } + + await knex.schema.alterTable('user_settings', table => { + table.string('settings_key_hash', 64).notNullable().alter(); + table.unique(['settings_key_hash'], 'user_settings_unique_idx'); + }); }; exports.down = async function down(knex) { diff --git a/plugins/notifications-backend/package.json b/plugins/notifications-backend/package.json index f94548d377..255a8097df 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.7-next.1", + "version": "0.5.8-next.1", "backstage": { "role": "backend-plugin", "pluginId": "notifications", diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts index c7819552cb..d8ae3faf57 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.test.ts @@ -228,6 +228,13 @@ describe.each(databases.eachSupportedId())( ]); }); + it('should return null value for user for broadcast notifications', async () => { + await storage.saveBroadcast({ ...testNotification1, read: new Date() }); + const notifications = await storage.getNotifications({ user }); + expect(notifications).toHaveLength(1); + expect(notifications[0].user).toBeNull(); + }); + it('should return read notifications for user', async () => { await storage.saveNotification(testNotification1); await storage.saveBroadcast(testNotification2); @@ -298,7 +305,7 @@ describe.each(databases.eachSupportedId())( user: otherUser, }); expect(otherUserNotifications.map(idOnly)).toEqual([id0, id2]); - expect(otherUserNotifications[1].user).toBe(otherUser); + expect(otherUserNotifications[1].user).toBeNull(); }); it('should allow searching for notifications', async () => { @@ -635,7 +642,7 @@ describe.each(databases.eachSupportedId())( expect(notification?.user).toBeNull(); await storage.markRead({ ids: [id1], user }); notification = await storage.getNotification({ id: id1, user }); - expect(notification?.user).toBe(user); + expect(notification?.user).toBeNull(); const otherNotification = await storage.getNotification({ id: id1, @@ -798,5 +805,24 @@ describe.each(databases.eachSupportedId())( }); }); }); + + describe('clearNotifications', () => { + it('should clear notifications older than specified days', async () => { + const oldDate = new Date(); + oldDate.setDate(oldDate.getDate() - 10); // 10 days ago + await storage.saveNotification({ + ...testNotification1, + created: oldDate, + }); + await storage.saveNotification(testNotification2); + + const result = await storage.clearNotifications({ + maxAge: { days: 5 }, + }); // Clear notifications older than 5 days + expect(result.deletedCount).toBe(1); // Only the first notification should be cleared + const remainingNotifications = await storage.getNotifications({ user }); + expect(remainingNotifications.map(idOnly)).toEqual([id2]); + }); + }); }, ); diff --git a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts index 35ecf1d778..94131ca257 100644 --- a/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts +++ b/plugins/notifications-backend/src/database/DatabaseNotificationsStore.ts @@ -31,6 +31,7 @@ import { } from '@backstage/plugin-notifications-common'; import { Knex } from 'knex'; import crypto from 'crypto'; +import { durationToMilliseconds, HumanDuration } from '@backstage/types'; const migrationsDir = resolvePackagePath( '@backstage/plugin-notifications-backend', @@ -149,7 +150,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { private mapToNotifications = (rows: any[]): Notification[] => { return rows.map(row => ({ id: row.id, - user: row.user, + user: row.type === 'broadcast' ? null : row.user, created: new Date(row.created), saved: row.saved, read: row.read, @@ -252,7 +253,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { join.andOnVal('user', '=', user); } }) - .select(NOTIFICATION_COLUMNS); + .select([...NOTIFICATION_COLUMNS, this.db.raw("'broadcast' as type")]); }; private getNotificationsBaseQuery = ( @@ -261,7 +262,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { const { user, orderField } = options; const subQuery = this.db('notification') - .select(NOTIFICATION_COLUMNS) + .select([...NOTIFICATION_COLUMNS, this.db.raw("'entity' as type")]) .unionAll([this.getBroadcastUnion(user)]) .as('notifications'); @@ -331,7 +332,10 @@ export class DatabaseNotificationsStore implements NotificationsStore { async getNotifications(options: NotificationGetOptions) { const notificationQuery = this.getNotificationsBaseQuery(options); - const notifications = await notificationQuery.select(NOTIFICATION_COLUMNS); + const notifications = await notificationQuery.select([ + ...NOTIFICATION_COLUMNS, + 'type', + ]); return this.mapToNotifications(notifications); } @@ -462,7 +466,7 @@ export class DatabaseNotificationsStore implements NotificationsStore { .select('*') .from( this.db('notification') - .select(NOTIFICATION_COLUMNS) + .select([...NOTIFICATION_COLUMNS, this.db.raw("'entity' as type")]) .unionAll([this.getBroadcastUnion(options.user)]) .as('notifications'), ) @@ -653,4 +657,24 @@ export class DatabaseNotificationsStore implements NotificationsStore { .distinct(['topic']); return { topics: topics.map(row => row.topic) }; } + + async clearNotifications(options: { + maxAge: HumanDuration; + }): Promise<{ deletedCount: number }> { + const ms = durationToMilliseconds(options.maxAge); + const now = new Date(new Date().getTime() - ms); + const notificationsCount = await this.db('notification') + .where(builder => { + builder.where('created', '<=', now).whereNull('updated'); + }) + .orWhere('updated', '<=', now) + .delete(); + const broadcastsCount = await this.db('broadcast') + .where(builder => { + builder.where('created', '<=', now).whereNull('updated'); + }) + .orWhere('updated', '<=', now) + .delete(); + return { deletedCount: notificationsCount + broadcastsCount }; + } } diff --git a/plugins/notifications-backend/src/database/NotificationsStore.ts b/plugins/notifications-backend/src/database/NotificationsStore.ts index a1f4eb6999..1f25b28bbe 100644 --- a/plugins/notifications-backend/src/database/NotificationsStore.ts +++ b/plugins/notifications-backend/src/database/NotificationsStore.ts @@ -20,6 +20,7 @@ import { NotificationSeverity, NotificationStatus, } from '@backstage/plugin-notifications-common'; +import { HumanDuration } from '@backstage/types'; /** @internal */ export type EntityOrder = { @@ -99,6 +100,10 @@ export interface NotificationsStore { user: string; }): Promise<{ origins: string[] }>; + getUserNotificationTopics(options: { + user: string; + }): Promise<{ topics: { origin: string; topic: string }[] }>; + getNotificationSettings(options: { user: string; }): Promise; @@ -109,4 +114,8 @@ export interface NotificationsStore { }): Promise; getTopics(options: TopicGetOptions): Promise<{ topics: string[] }>; + + clearNotifications(options: { + maxAge: HumanDuration; + }): Promise<{ deletedCount: number }>; } diff --git a/plugins/notifications-backend/src/plugin.ts b/plugins/notifications-backend/src/plugin.ts index a5a31d92ed..5f430318b5 100644 --- a/plugins/notifications-backend/src/plugin.ts +++ b/plugins/notifications-backend/src/plugin.ts @@ -26,6 +26,8 @@ import { NotificationsProcessingExtensionPoint, } from '@backstage/plugin-notifications-node'; import { catalogServiceRef } from '@backstage/plugin-catalog-node'; +import { DatabaseNotificationsStore } from './database'; +import { NotificationCleaner } from './service/NotificationCleaner.ts'; class NotificationsProcessingExtensionPointImpl implements NotificationsProcessingExtensionPoint @@ -69,6 +71,7 @@ export const notificationsPlugin = createBackendPlugin({ signals: signalsServiceRef, config: coreServices.rootConfig, catalog: catalogServiceRef, + scheduler: coreServices.scheduler, }, async init({ auth, @@ -80,7 +83,10 @@ export const notificationsPlugin = createBackendPlugin({ signals, config, catalog, + scheduler, }) { + const store = await DatabaseNotificationsStore.create({ database }); + httpRouter.use( await createRouter({ auth, @@ -88,7 +94,7 @@ export const notificationsPlugin = createBackendPlugin({ userInfo, logger, config, - database, + store, catalog, signals, processors: processingExtensions.processors, @@ -98,6 +104,14 @@ export const notificationsPlugin = createBackendPlugin({ path: '/health', allow: 'unauthenticated', }); + + const cleaner = new NotificationCleaner( + config, + scheduler, + logger, + store, + ); + await cleaner.initTaskRunner(); }, }); }, diff --git a/plugins/notifications-backend/src/service/NotificationCleaner.test.ts b/plugins/notifications-backend/src/service/NotificationCleaner.test.ts new file mode 100644 index 0000000000..bfd3cc62b9 --- /dev/null +++ b/plugins/notifications-backend/src/service/NotificationCleaner.test.ts @@ -0,0 +1,118 @@ +/* + * 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 { LoggerService, SchedulerService } from '@backstage/backend-plugin-api'; +import { Config } from '@backstage/config'; +import { mockServices } from '@backstage/backend-test-utils'; +import { NotificationsStore } from '../database'; +import { NotificationCleaner } from './NotificationCleaner.ts'; + +describe('NotificationCleaner', () => { + let mockConfig: Config; + let mockScheduler: SchedulerService; + let mockLogger: LoggerService; + let mockDatabase: NotificationsStore; + + beforeEach(() => { + mockConfig = mockServices.rootConfig(); + mockScheduler = mockServices.scheduler.mock(); + mockLogger = mockServices.logger.mock(); + mockDatabase = { + clearNotifications: jest.fn(), + } as unknown as NotificationsStore; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('initNotificationCleaner', () => { + it('should initialize the notification cleaner with the correct schedule', async () => { + const mockTaskRunner = { + run: jest.fn(), + }; + mockScheduler.createScheduledTaskRunner = jest + .fn() + .mockReturnValue(mockTaskRunner); + + const cleaner = new NotificationCleaner( + mockConfig, + mockScheduler, + mockLogger, + mockDatabase, + ); + expect(cleaner).toBeInstanceOf(NotificationCleaner); + await cleaner.initTaskRunner(); + + expect(mockScheduler.createScheduledTaskRunner).toHaveBeenCalled(); + expect(mockTaskRunner.run).toHaveBeenCalledWith( + expect.objectContaining({ + id: 'notification-cleaner', + fn: expect.any(Function), + }), + ); + }); + + it('should not create a task runner if retention is disabled', async () => { + mockConfig = mockServices.rootConfig({ + data: { notifications: { retention: false } }, + }); + const cleaner = new NotificationCleaner( + mockConfig, + mockScheduler, + mockLogger, + mockDatabase, + ); + await cleaner.initTaskRunner(); + + expect(mockScheduler.createScheduledTaskRunner).not.toHaveBeenCalled(); + expect(mockLogger.info).toHaveBeenCalledWith( + 'Notification retention is disabled, skipping notification cleaner task', + ); + }); + }); + + describe('clearNotifications', () => { + it('should clear notifications', async () => { + mockDatabase.clearNotifications = jest + .fn() + .mockResolvedValue({ deletedCount: 1 }); + const mockTaskRunner = { + run: jest.fn().mockImplementation(({ fn }) => fn()), + }; + mockScheduler.createScheduledTaskRunner = jest + .fn() + .mockReturnValue(mockTaskRunner); + + const cleaner = new NotificationCleaner( + mockConfig, + mockScheduler, + mockLogger, + mockDatabase, + ); + await cleaner.initTaskRunner(); + + expect(mockLogger.info).toHaveBeenCalledWith( + 'Starting notification cleaner task', + ); + expect(mockLogger.info).toHaveBeenCalledWith( + 'Notification cleaner task completed successfully, deleted 1 notifications', + ); + expect(mockDatabase.clearNotifications).toHaveBeenCalledWith({ + maxAge: { years: 1 }, + }); + }); + }); +}); diff --git a/plugins/notifications-backend/src/service/NotificationCleaner.ts b/plugins/notifications-backend/src/service/NotificationCleaner.ts new file mode 100644 index 0000000000..6733a843b5 --- /dev/null +++ b/plugins/notifications-backend/src/service/NotificationCleaner.ts @@ -0,0 +1,91 @@ +/* + * 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 { + LoggerService, + SchedulerService, + SchedulerServiceTaskScheduleDefinition, +} from '@backstage/backend-plugin-api'; +import { Config, readDurationFromConfig } from '@backstage/config'; +import { NotificationsStore } from '../database'; +import { HumanDuration } from '@backstage/types'; +import { ForwardedError } from '@backstage/errors'; + +export class NotificationCleaner { + private readonly retention: HumanDuration = { years: 1 }; + private readonly enabled: boolean = true; + + constructor( + config: Config, + private readonly scheduler: SchedulerService, + private readonly logger: LoggerService, + private readonly database: NotificationsStore, + ) { + if (config.has('notifications.retention')) { + const retentionConfig = config.get('notifications.retention'); + if (typeof retentionConfig === 'boolean' && !retentionConfig) { + logger.info( + 'Notification retention is disabled, skipping notification cleaner task', + ); + this.enabled = false; + return; + } + this.retention = readDurationFromConfig(config, { + key: 'notifications.retention', + }); + } + } + + async initTaskRunner() { + if (!this.enabled) { + return; + } + + const schedule: SchedulerServiceTaskScheduleDefinition = { + frequency: { cron: '0 0 * * *' }, + timeout: { hours: 1 }, + initialDelay: { hours: 1 }, + scope: 'global', + }; + + const taskRunner = this.scheduler.createScheduledTaskRunner(schedule); + await taskRunner.run({ + id: 'notification-cleaner', + fn: async () => { + await this.clearNotifications( + this.logger, + this.database, + this.retention, + ); + }, + }); + } + + private async clearNotifications( + logger: LoggerService, + database: NotificationsStore, + retention: HumanDuration, + ) { + logger.info('Starting notification cleaner task'); + try { + const result = await database.clearNotifications({ maxAge: retention }); + logger.info( + `Notification cleaner task completed successfully, deleted ${result.deletedCount} notifications`, + ); + } catch (error) { + throw new ForwardedError('Notification cleaner task failed', error); + } + } +} diff --git a/plugins/notifications-backend/src/service/router.test.ts b/plugins/notifications-backend/src/service/router.test.ts index 7f2f7ab6cc..6c74fed41c 100644 --- a/plugins/notifications-backend/src/service/router.test.ts +++ b/plugins/notifications-backend/src/service/router.test.ts @@ -29,8 +29,10 @@ import { NotificationSendOptions } from '@backstage/plugin-notifications-node'; import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; import { DatabaseService } from '@backstage/backend-plugin-api'; import { v4 as uuid } from 'uuid'; +import { DatabaseNotificationsStore } from '../database'; const databases = TestDatabases.create(); +let store: DatabaseNotificationsStore; async function createDatabase( databaseId: TestDatabaseId, @@ -100,6 +102,9 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { beforeAll(async () => { database = await createDatabase(databaseId); + store = await DatabaseNotificationsStore.create({ + database, + }); }); describe('POST /notifications', () => { @@ -110,7 +115,7 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - database, + store, signals: signalService, userInfo, config, @@ -498,7 +503,7 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - database, + store, signals: signalService, userInfo, config, @@ -588,7 +593,7 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - database, + store, signals: signalService, userInfo, config, @@ -726,7 +731,7 @@ describe.each(databases.eachSupportedId())('createRouter (%s)', databaseId => { beforeAll(async () => { const router = await createRouter({ logger: mockServices.logger.mock(), - database, + store, signals: signalService, userInfo, config, diff --git a/plugins/notifications-backend/src/service/router.ts b/plugins/notifications-backend/src/service/router.ts index 1e9a822bb5..5805092096 100644 --- a/plugins/notifications-backend/src/service/router.ts +++ b/plugins/notifications-backend/src/service/router.ts @@ -17,9 +17,9 @@ import express, { Request, Response } from 'express'; import Router from 'express-promise-router'; import { - DatabaseNotificationsStore, normalizeSeverity, NotificationGetOptions, + NotificationsStore, TopicGetOptions, } from '../database'; import { v4 as uuid } from 'uuid'; @@ -31,7 +31,6 @@ import { import { InputError, NotFoundError } from '@backstage/errors'; import { AuthService, - DatabaseService, HttpAuthService, LoggerService, UserInfoService, @@ -58,7 +57,7 @@ import pThrottle from 'p-throttle'; export interface RouterOptions { logger: LoggerService; config: Config; - database: DatabaseService; + store: NotificationsStore; auth: AuthService; httpAuth: HttpAuthService; userInfo: UserInfoService; @@ -74,7 +73,7 @@ export async function createRouter( const { config, logger, - database, + store, auth, httpAuth, userInfo, @@ -84,7 +83,6 @@ export async function createRouter( } = options; const WEB_NOTIFICATION_CHANNEL = 'Web'; - const store = await DatabaseNotificationsStore.create({ database }); const frontendBaseUrl = config.getString('app.baseUrl'); const concurrencyLimit = config.getOptionalNumber('notifications.concurrencyLimit') ?? 10; diff --git a/plugins/notifications-backend/src/tests/migrations.test.ts b/plugins/notifications-backend/src/tests/migrations.test.ts new file mode 100644 index 0000000000..aa3ba7ae9a --- /dev/null +++ b/plugins/notifications-backend/src/tests/migrations.test.ts @@ -0,0 +1,99 @@ +/* + * 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 { Knex } from 'knex'; +import { TestDatabases } from '@backstage/backend-test-utils'; +import fs from 'fs'; + +const migrationsDir = `${__dirname}/../../migrations`; +const migrationsFiles = fs.readdirSync(migrationsDir).sort(); + +async function migrateUpOnce(knex: Knex): Promise { + await knex.migrate.up({ directory: migrationsDir }); +} + +async function migrateDownOnce(knex: Knex): Promise { + await knex.migrate.down({ directory: migrationsDir }); +} + +async function migrateUntilBefore(knex: Knex, target: string): Promise { + const index = migrationsFiles.indexOf(target); + if (index === -1) { + throw new Error(`Migration ${target} not found`); + } + for (let i = 0; i < index; i++) { + await migrateUpOnce(knex); + } +} + +jest.setTimeout(60_000); + +describe('migrations', () => { + const databases = TestDatabases.create(); + + it.each(databases.eachSupportedId())( + '20221109192547_search_add_original_value_column.js, %p', + async databaseId => { + const knex = await databases.init(databaseId); + + await migrateUntilBefore(knex, '20250317_addTopic.js'); + + await knex + .insert({ + user: 'user1', + channel: 'channel1', + origin: 'origin1', + enabled: true, + }) + .into('user_settings'); + + await migrateUpOnce(knex); + + let rows = await knex('user_settings'); + let normalized = rows.map(r => ({ ...r, enabled: !!r.enabled })); + + expect(normalized).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + user: 'user1', + channel: 'channel1', + origin: 'origin1', + enabled: true, + settings_key_hash: + '73f97aff883b8b08a7f4e366234ef4f86827702b0016574ac4c1bf313c703d15', + topic: null, + }), + ]), + ); + + await migrateDownOnce(knex); + + rows = await knex('user_settings'); + normalized = rows.map(r => ({ ...r, enabled: !!r.enabled })); + + expect(normalized).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + user: 'user1', + channel: 'channel1', + origin: 'origin1', + enabled: true, + }), + ]), + ); + }, + ); +}); diff --git a/plugins/notifications-common/CHANGELOG.md b/plugins/notifications-common/CHANGELOG.md index 0bcbc7e926..a30de18b1b 100644 --- a/plugins/notifications-common/CHANGELOG.md +++ b/plugins/notifications-common/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-notifications-common +## 0.0.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + +## 0.0.9 + +### Patch Changes + +- 1fb5f06: Adds ability for user to turn on/off notifications for specific topics within an origin. +- Updated dependencies + - @backstage/config@1.3.2 + ## 0.0.9-next.0 ### Patch Changes diff --git a/plugins/notifications-common/package.json b/plugins/notifications-common/package.json index cdca935d38..443dda4514 100644 --- a/plugins/notifications-common/package.json +++ b/plugins/notifications-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications-common", - "version": "0.0.9-next.0", + "version": "0.0.10-next.0", "description": "Common functionalities for the notifications plugin", "backstage": { "role": "common-library", diff --git a/plugins/notifications-node/CHANGELOG.md b/plugins/notifications-node/CHANGELOG.md index abfa9e282b..465a05d67f 100644 --- a/plugins/notifications-node/CHANGELOG.md +++ b/plugins/notifications-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-notifications-node +## 0.2.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-signals-node@0.1.22-next.0 + +## 0.2.16 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/plugin-signals-node@0.1.21 + ## 0.2.16-next.1 ### Patch Changes diff --git a/plugins/notifications-node/package.json b/plugins/notifications-node/package.json index ce823ffe0d..27775c9859 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.16-next.1", + "version": "0.2.17-next.0", "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 a9df09639b..9ca133d03c 100644 --- a/plugins/notifications/CHANGELOG.md +++ b/plugins/notifications/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-notifications +## 0.5.7-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-signals-react@0.0.15-next.0 + +## 0.5.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/plugin-signals-react@0.0.14 + +## 0.5.6 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- b00c160: Remove React import form notification and scaffolder plugin +- 1fb5f06: Adds ability for user to turn on/off notifications for specific topics within an origin. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.14 + +## 0.5.6-next.2 + +### Patch Changes + +- b00c160: Remove React import form notification and scaffolder plugin +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-signals-react@0.0.13 + ## 0.5.6-next.1 ### Patch Changes diff --git a/plugins/notifications/package.json b/plugins/notifications/package.json index 4a9f2fc583..60e397c9a0 100644 --- a/plugins/notifications/package.json +++ b/plugins/notifications/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-notifications", - "version": "0.5.6-next.1", + "version": "0.5.7-next.1", "backstage": { "role": "frontend-plugin", "pluginId": "notifications", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 93db912391..a5a3165af9 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-org-react +## 0.1.40-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.1.40-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.8 + +## 0.1.39 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + +## 0.1.39-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.7 + ## 0.1.39-next.1 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 78e690e390..76c3b80d2c 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.39-next.1", + "version": "0.1.40-next.1", "backstage": { "role": "web-library", "pluginId": "org", diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index ff423ab5f6..186850baeb 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-org +## 0.6.41-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 0.6.41-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.6.40 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- 4fe364f: Org plugin support i18n +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/plugin-catalog-common@1.1.4 + +## 0.6.40-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/plugin-catalog-common@1.1.4 + ## 0.6.40-next.1 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index ea9d705feb..bbc932dc3f 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-org", - "version": "0.6.40-next.1", + "version": "0.6.41-next.1", "description": "A Backstage plugin that helps you create entity pages for your organization", "backstage": { "role": "frontend-plugin", diff --git a/plugins/org/src/alpha.tsx b/plugins/org/src/alpha.tsx index e5d6aa6675..92ae7d8801 100644 --- a/plugins/org/src/alpha.tsx +++ b/plugins/org/src/alpha.tsx @@ -26,7 +26,8 @@ import { EntityCardBlueprint } from '@backstage/plugin-catalog-react/alpha'; const EntityGroupProfileCard = EntityCardBlueprint.make({ name: 'group-profile', params: { - filter: 'kind:group', + type: 'info', + filter: { kind: 'group' }, loader: async () => import('./components/Cards/Group/GroupProfile/GroupProfileCard').then(m => compatWrapper(), @@ -38,7 +39,7 @@ const EntityGroupProfileCard = EntityCardBlueprint.make({ const EntityMembersListCard = EntityCardBlueprint.make({ name: 'members-list', params: { - filter: 'kind:group', + filter: { kind: 'group' }, loader: async () => import('./components/Cards/Group/MembersList/MembersListCard').then(m => compatWrapper(), @@ -50,7 +51,7 @@ const EntityMembersListCard = EntityCardBlueprint.make({ const EntityOwnershipCard = EntityCardBlueprint.make({ name: 'ownership', params: { - filter: 'kind:group,user', + filter: { kind: { $in: ['group', 'user'] } }, loader: async () => import('./components/Cards/OwnershipCard/OwnershipCard').then(m => compatWrapper(), @@ -69,7 +70,8 @@ const EntityUserProfileCard = EntityCardBlueprint.makeWithOverrides({ }, factory(originalFactory, { config }) { return originalFactory({ - filter: 'kind:user', + type: 'info', + filter: { kind: 'user' }, loader: async () => import('./components/Cards/User/UserProfileCard/UserProfileCard').then( m => diff --git a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx index f5b01e918a..6ea0f0cbc9 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/OwnershipCard.test.tsx @@ -14,10 +14,6 @@ * limitations under the License. */ -import { - GetEntitiesRequest, - GetEntitiesResponse, -} from '@backstage/catalog-client'; import { Entity, GroupEntity, UserEntity } from '@backstage/catalog-model'; import { catalogApiRef, EntityProvider } from '@backstage/plugin-catalog-react'; import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; @@ -104,18 +100,6 @@ const items = [ }, ] as Entity[]; -const getEntitiesMock = ( - request?: GetEntitiesRequest, -): Promise => { - const filterKinds = - Array.isArray(request?.filter) && Array.isArray(request?.filter[0].kind) - ? request?.filter[0].kind ?? [] - : []; // we expect the request to be like { filter: [{ kind: ['API','System'], 'relations.ownedBy': [group:default/my-team], .... }]. If changed in OwnerShipCard, let's change in also here - return Promise.resolve({ - items: items.filter(item => filterKinds.find(k => k === item.kind)), - } as GetEntitiesResponse); -}; - describe('OwnershipCard', () => { const groupEntity: GroupEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -157,7 +141,8 @@ describe('OwnershipCard', () => { }; it('displays entity counts', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); + const mockedGetEntities = jest.spyOn(catalogApi, 'getEntities'); const { getByText } = await renderInTestApp( @@ -172,7 +157,7 @@ describe('OwnershipCard', () => { }, ); - expect(catalogApi.getEntities).toHaveBeenCalledWith({ + expect(mockedGetEntities).toHaveBeenCalledWith({ filter: [ { kind: ['Component', 'API', 'System'], @@ -207,7 +192,7 @@ describe('OwnershipCard', () => { }); it('applies CustomFilterDefinition', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByText } = await renderInTestApp( @@ -240,7 +225,7 @@ describe('OwnershipCard', () => { }); it('links to the catalog with the group filter', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByText } = await renderInTestApp( @@ -263,7 +248,7 @@ describe('OwnershipCard', () => { }); it('links to the catalog with the user and groups filters from an user profile', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByText } = await renderInTestApp( @@ -288,7 +273,7 @@ describe('OwnershipCard', () => { describe('OwnershipCard relations', () => { it('shows relations toggle', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByTitle } = await renderInTestApp( @@ -307,7 +292,7 @@ describe('OwnershipCard', () => { }); it('hides relations toggle', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const rendered = await renderInTestApp( @@ -326,7 +311,7 @@ describe('OwnershipCard', () => { }); it('overrides relation type', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByTitle } = await renderInTestApp( @@ -345,7 +330,7 @@ describe('OwnershipCard', () => { }); it('defaults to aggregated for User entity kind', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByLabelText } = await renderInTestApp( @@ -364,7 +349,7 @@ describe('OwnershipCard', () => { }); it('defaults to direct for all entity kinds except User', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByLabelText } = await renderInTestApp( @@ -383,7 +368,7 @@ describe('OwnershipCard', () => { }); it('defaults to provided relationsType', async () => { - const catalogApi = catalogApiMock.mock({ getEntities: getEntitiesMock }); + const catalogApi = catalogApiMock({ entities: items }); const { getByLabelText } = await renderInTestApp( diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 0d5f459c4c..33d439d77f 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.2.9 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + ## 0.2.9-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 3ef2bcd7e6..550ffe8602 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.9-next.1", + "version": "0.2.10-next.0", "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 8d0849da56..0d8cf4d167 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-permission-backend +## 0.7.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.7.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + ## 0.7.1-next.1 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index ef970b4c5c..7c634cdf15 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.1-next.1", + "version": "0.7.2-next.0", "backstage": { "role": "backend-plugin", "pluginId": "permission", diff --git a/plugins/permission-common/CHANGELOG.md b/plugins/permission-common/CHANGELOG.md index a8355d2dd7..b086f22ad0 100644 --- a/plugins/permission-common/CHANGELOG.md +++ b/plugins/permission-common/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-permission-common +## 0.9.1-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + ## 0.9.0 ### Minor Changes diff --git a/plugins/permission-common/package.json b/plugins/permission-common/package.json index 95c1fa9a00..e9dbf2daca 100644 --- a/plugins/permission-common/package.json +++ b/plugins/permission-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-common", - "version": "0.9.0", + "version": "0.9.1-next.0", "description": "Isomorphic types and client for Backstage permissions and authorization", "backstage": { "role": "common-library", diff --git a/plugins/permission-common/src/types/deprecated.ts b/plugins/permission-common/src/types/deprecated.ts new file mode 100644 index 0000000000..e2a4e404e4 --- /dev/null +++ b/plugins/permission-common/src/types/deprecated.ts @@ -0,0 +1,30 @@ +/* + * 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 { EvaluatePermissionRequest, EvaluatePermissionResponse } from './api'; +import { AuthorizeRequestOptions } from './permission'; + +/** + * A client interacting with the permission backend can implement this authorizer interface. + * @public + * @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead + */ +export interface PermissionAuthorizer { + authorize( + requests: EvaluatePermissionRequest[], + options?: AuthorizeRequestOptions, + ): Promise; +} diff --git a/plugins/permission-common/src/types/index.ts b/plugins/permission-common/src/types/index.ts index f5380541df..60a588ffd2 100644 --- a/plugins/permission-common/src/types/index.ts +++ b/plugins/permission-common/src/types/index.ts @@ -48,8 +48,8 @@ export type { BasicPermission, PermissionAttributes, Permission, - PermissionAuthorizer, PermissionBase, ResourcePermission, AuthorizeRequestOptions, } from './permission'; +export type { PermissionAuthorizer } from './deprecated'; diff --git a/plugins/permission-common/src/types/permission.ts b/plugins/permission-common/src/types/permission.ts index eb1c0eb50d..69a0b0ebbd 100644 --- a/plugins/permission-common/src/types/permission.ts +++ b/plugins/permission-common/src/types/permission.ts @@ -14,8 +14,6 @@ * limitations under the License. */ -import { EvaluatePermissionRequest, EvaluatePermissionResponse } from './api'; - /** * The attributes related to a given permission; these should be generic and widely applicable to * all permissions in the system. @@ -88,18 +86,6 @@ export type ResourcePermission = } >; -/** - * A client interacting with the permission backend can implement this authorizer interface. - * @public - * @deprecated Use {@link @backstage/plugin-permission-common#PermissionEvaluator} instead - */ -export interface PermissionAuthorizer { - authorize( - requests: EvaluatePermissionRequest[], - options?: AuthorizeRequestOptions, - ): Promise; -} - /** * Options for authorization requests. * @public diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index c86d51f9c2..96263abc6a 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-permission-node +## 0.10.2-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + +## 0.10.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0 + ## 0.10.1-next.1 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 805b1c919b..41785c18ce 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.1-next.1", + "version": "0.10.2-next.0", "description": "Common permission and authorization utilities for backend plugins", "backstage": { "role": "node-library", diff --git a/plugins/permission-node/src/index.ts b/plugins/permission-node/src/index.ts index 32d71ddeb3..ab97b57021 100644 --- a/plugins/permission-node/src/index.ts +++ b/plugins/permission-node/src/index.ts @@ -21,5 +21,5 @@ */ export * from './integration'; export * from './policy'; -export * from './types'; +export type { PermissionRule, PermissionRuleset } from './types'; export { ServerPermissionClient } from './ServerPermissionClient'; diff --git a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts index 4ee937866b..522e60bac5 100644 --- a/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts +++ b/plugins/permission-node/src/integration/createPermissionIntegrationRouter.ts @@ -30,9 +30,8 @@ import { PermissionCriteria, PolicyDecision, } from '@backstage/plugin-permission-common'; -import { PermissionRule, PermissionRuleset } from '../types'; +import { NoInfer, PermissionRule, PermissionRuleset } from '../types'; import { - NoInfer, createGetRule, isAndCriteria, isNotCriteria, diff --git a/plugins/permission-node/src/integration/createPermissionRule.ts b/plugins/permission-node/src/integration/createPermissionRule.ts index ba6f41145b..36f8205cf8 100644 --- a/plugins/permission-node/src/integration/createPermissionRule.ts +++ b/plugins/permission-node/src/integration/createPermissionRule.ts @@ -18,10 +18,9 @@ import { PermissionCriteria, PermissionRuleParams, } from '@backstage/plugin-permission-common'; -import { PermissionRule } from '../types'; +import { NoInfer, PermissionRule } from '../types'; import { z } from 'zod'; import { PermissionResourceRef } from './createPermissionResourceRef'; -import { NoInfer } from './util'; /** * @public diff --git a/plugins/permission-node/src/integration/util.ts b/plugins/permission-node/src/integration/util.ts index 9be90e15fc..c2fa200237 100644 --- a/plugins/permission-node/src/integration/util.ts +++ b/plugins/permission-node/src/integration/util.ts @@ -22,14 +22,6 @@ import { } from '@backstage/plugin-permission-common'; import { PermissionRule } from '../types'; -/** - * Prevent use of type parameter from contributing to type inference. - * - * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795 - * @ignore - */ -export type NoInfer = T extends infer S ? S : never; - /** * Utility function used to parse a PermissionCriteria * @param criteria - a PermissionCriteria diff --git a/plugins/permission-node/src/types.ts b/plugins/permission-node/src/types.ts index b1585d1827..578117a4f1 100644 --- a/plugins/permission-node/src/types.ts +++ b/plugins/permission-node/src/types.ts @@ -19,7 +19,15 @@ import type { PermissionRuleParams, } from '@backstage/plugin-permission-common'; import { z } from 'zod'; -import { NoInfer } from './integration/util'; + +/** + * Prevent use of type parameter from contributing to type inference. + * + * https://github.com/Microsoft/TypeScript/issues/14829#issuecomment-980401795 + * + * @ignore + */ +export type NoInfer = T extends infer S ? S : never; /** * A conditional rule that can be provided in an diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index 8c29c0cc6d..fe1cb33fac 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-permission-react +## 0.4.36-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.4.35 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/config@1.3.2 + - @backstage/plugin-permission-common@0.9.0 + ## 0.4.34 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index b9cb46ac47..67cc676443 100644 --- a/plugins/permission-react/package.json +++ b/plugins/permission-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-react", - "version": "0.4.34", + "version": "0.4.36-next.0", "backstage": { "role": "web-library", "pluginId": "permission", diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 924fc7c193..b2ef24851b 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-proxy-backend +## 0.6.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-proxy-node@0.1.6-next.0 + +## 0.6.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/types@1.2.1 + - @backstage/plugin-proxy-node@0.1.5 + ## 0.6.3-next.1 ### Patch Changes diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index fc3c2d3147..31ede2ed62 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.3-next.1", + "version": "0.6.4-next.0", "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 c1cfc3a736..c9f10ddb09 100644 --- a/plugins/proxy-node/CHANGELOG.md +++ b/plugins/proxy-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-proxy-node +## 0.1.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + ## 0.1.5-next.1 ### Patch Changes diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json index a99b2b6913..2b13efce22 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.5-next.1", + "version": "0.1.6-next.0", "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 a5e9c298e9..4c3a43f986 100644 --- a/plugins/scaffolder-backend-module-azure/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-azure/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-scaffolder-backend-module-azure +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- e89d7b6: Migrating `azure` actions to using the new `zod` schema format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-azure/package.json b/plugins/scaffolder-backend-module-azure/package.json index 975a446efc..f875c1dcd5 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.10-next.1", + "version": "0.2.11-next.2", "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 852df7a904..c44c554d95 100644 --- a/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,61 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-cloud +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.1-next.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- ca9fdc0: Migrate `bitbucket-cloud` to new actions format +- 9c8ff0c: Update pull request creation filter to include .gitignore files in the created pull request +- 280611d: Fix `bitbucketCloudBranchRestrictions` API calls to accept null to prevent 400 errors for some branch restriction kinds defined. +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-cloud/package.json b/plugins/scaffolder-backend-module-bitbucket-cloud/package.json index 2c4c83f5b6..de43a60a39 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.10-next.1", + "version": "0.2.11-next.2", "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 7d56244457..0ee2a663ca 100644 --- a/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket-server +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- d8169fc: Migrate the actions to the new format +- 9c8ff0c: Update pull request creation filter to include .gitignore files in the created pull request +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket-server/package.json b/plugins/scaffolder-backend-module-bitbucket-server/package.json index 5a6cf1f317..6b24bb1ba0 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.10-next.1", + "version": "0.2.11-next.2", "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 0f55793ec6..1f37112f82 100644 --- a/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,66 @@ # @backstage/plugin-scaffolder-backend-module-bitbucket +## 0.3.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.2 + +## 0.3.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.1 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.3.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.3.11 + +### Patch Changes + +- 7f710d2: Migrating `bitbucket` actions to use the new `zod` format +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10 + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.3.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10-next.2 + ## 0.3.11-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-bitbucket/package.json b/plugins/scaffolder-backend-module-bitbucket/package.json index ef8ecb4e7c..b0cdf2bdec 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.11-next.1", + "version": "0.3.12-next.2", "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 50ba4a5f6c..a90ca1fc8b 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,57 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.3.11-next.2 + +### Patch Changes + +- 642282d: Added support for new link format for on-prem Confluence +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.3.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.3.10 + +### Patch Changes + +- 4a86bca: Migrate actions to new format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.3.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.3.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md index d3722f1227..7ea832b80b 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/README.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/README.md @@ -106,7 +106,7 @@ spec: properties: confluenceUrls: type: array - description: Urls for Confluence doc to be converted to markdown. In format /display// or /spaces//pages// for Confluence cloud + description: Urls for Confluence doc to be converted to markdown. In format /display// or /spaces//pages// items: type: string ui:options: diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index e95d610f48..0ffcb3d5f9 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.10-next.1", + "version": "0.3.11-next.2", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.test.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.test.ts index 42d83c18a8..9acf40d6c2 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.test.ts +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.test.ts @@ -50,6 +50,17 @@ describe('createConfluenceVariables', () => { expect(title).toEqual('Cloud+Page+Title'); expect(titleWithSpaces).toEqual('Cloud Page Title'); }); + + it('should return values for Confluence Url using spaces (non-cloud)', () => { + const url = + 'https://confluence.example.com/spaces/SPACEKEY/pages/1234567/Confluence+Page+Title'; + + const { spacekey, title, titleWithSpaces } = createConfluenceVariables(url); + + expect(spacekey).toEqual('SPACEKEY'); + expect(title).toEqual('Confluence+Page+Title'); + expect(titleWithSpaces).toEqual('Confluence Page Title'); + }); }); describe('getConfluenceConfig', () => { diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts index bbc722b1d5..04d475677c 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/src/actions/confluence/helpers.ts @@ -15,7 +15,7 @@ */ import { Config } from '@backstage/config'; -import { ResponseError, ConflictError, InputError } from '@backstage/errors'; +import { ResponseError, ConflictError } from '@backstage/errors'; import fs from 'fs-extra'; import { Readable } from 'stream'; @@ -188,26 +188,24 @@ export const createConfluenceVariables = (url: string) => { let title: string | undefined = undefined; let titleWithSpaces: string | undefined = ''; const params = new URL(url); - if (params.pathname.split('/')[1] === 'display') { - // https://confluence.example.com/display/SPACEKEY/Page+Title - spacekey = params.pathname.split('/')[2]; - title = params.pathname.split('/')[3]; - titleWithSpaces = title?.replace(/\+/g, ' '); - return { spacekey, title, titleWithSpaces }; - } else if (params.pathname.split('/')[2] === 'display') { - // https://confluence.example.com/prefix/display/SPACEKEY/Page+Title - spacekey = params.pathname.split('/')[3]; - title = params.pathname.split('/')[4]; - titleWithSpaces = title?.replace(/\+/g, ' '); - return { spacekey, title, titleWithSpaces }; - } else if (params.pathname.split('/')[2] === 'spaces') { - // https://example.atlassian.net/wiki/spaces/SPACEKEY/pages/1234567/Page+Title - spacekey = params.pathname.split('/')[3]; - title = params.pathname.split('/')[6]; - titleWithSpaces = title?.replace(/\+/g, ' '); - return { spacekey, title, titleWithSpaces }; + const pathParts = params.pathname.split('/').filter(Boolean); + + if (pathParts.includes('display')) { + // /display// + const idx = pathParts.indexOf('display'); + spacekey = pathParts[idx + 1]; + title = pathParts[idx + 2]; + } else if (pathParts.includes('spaces')) { + // /spaces/<SPACEKEY>/pages/<PAGEID>/<TITLE> + const idx = pathParts.indexOf('spaces'); + spacekey = pathParts[idx + 1]; + title = pathParts[pathParts.length - 1]; + } else { + throw new Error( + 'The Url format for Confluence is incorrect. Acceptable format is `<CONFLUENCE_BASE_URL>/display/<SPACEKEY>/<PAGE+TITLE>` or `<CONFLUENCE_BASE_URL>/spaces/<SPACEKEY>/pages/<PAGEID>/<PAGE+TITLE>`', + ); } - throw new InputError( - 'The Url format for Confluence is incorrect. Acceptable format is `<CONFLUENCE_BASE_URL>/display/<SPACEKEY>/<PAGE+TITLE>` or `<CONFLUENCE_BASE_URL>/spaces/<SPACEKEY>/pages/<PAGEID>/<PAGE+TITLE>` for Confluence cloud', - ); + + titleWithSpaces = title?.replace(/\+/g, ' '); + return { spacekey, title, titleWithSpaces }; }; diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 721b05c601..3c3d1e6a63 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,64 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.3.13-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.3.13-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.3.13-next.0 + +### Patch Changes + +- cf9ba6f: Fixing the typescript issue with using `z.unknown()` +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.3.11 + +### Patch Changes + +- 0b2bbf1: Migrate using new actions format +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + +## 0.3.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + ## 0.3.11-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 21b95c0641..21b962fb19 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.11-next.1", + "version": "0.3.13-next.2", "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-cookiecutter/report.api.md b/plugins/scaffolder-backend-module-cookiecutter/report.api.md index 2dfba3f42f..a60a8c0396 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/report.api.md +++ b/plugins/scaffolder-backend-module-cookiecutter/report.api.md @@ -4,10 +4,12 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; +import { objectOutputType } from 'zod'; import { ScmIntegrations } from '@backstage/integration'; import { TemplateAction } from '@backstage/plugin-scaffolder-node'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { Writable } from 'stream'; +import { ZodTypeAny } from 'zod'; // @public export interface ContainerRunner { @@ -47,7 +49,7 @@ export function createFetchCookiecutterAction(options: { }): TemplateAction< { url: string; - values: Record<string, unknown>; + values: objectOutputType<{}, ZodTypeAny, 'passthrough'>; targetPath?: string | undefined; copyWithoutRender?: string[] | undefined; extensions?: string[] | undefined; diff --git a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts index 3a4ee8c3d7..9a9df95dea 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend-module-cookiecutter/src/actions/fetch/cookiecutter.ts @@ -169,9 +169,14 @@ export function createFetchCookiecutterAction(options: { }) .optional(), values: z => - z.record(z.unknown(), { - description: 'Values to pass on to cookiecutter for templating', - }), + z + .object( + {}, + { + description: 'Values to pass on to cookiecutter for templating', + }, + ) + .passthrough(), copyWithoutRender: z => z .array(z.string(), { diff --git a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md index bf9576fd11..06146bcd85 100644 --- a/plugins/scaffolder-backend-module-gcp/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-scaffolder-backend-module-gcp +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gcp/package.json b/plugins/scaffolder-backend-module-gcp/package.json index 4d1b921556..1edf6b2202 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.10-next.1", + "version": "0.2.11-next.2", "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 bcf48115c0..83a7d9e856 100644 --- a/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-scaffolder-backend-module-gerrit +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- e24b29b: Migrating to use new format for actions +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gerrit/package.json b/plugins/scaffolder-backend-module-gerrit/package.json index 091345a30a..2565ad2633 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.10-next.1", + "version": "0.2.11-next.2", "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 94172f1fdb..27c06fa3f1 100644 --- a/plugins/scaffolder-backend-module-gitea/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitea/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-scaffolder-backend-module-gitea +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- ed41017: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.2.10-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitea/package.json b/plugins/scaffolder-backend-module-gitea/package.json index 545a778646..632d85c79a 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.10-next.1", + "version": "0.2.11-next.2", "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 3944c6d88a..8eec283cb3 100644 --- a/plugins/scaffolder-backend-module-github/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-github/CHANGELOG.md @@ -1,5 +1,156 @@ # @backstage/plugin-scaffolder-backend-module-github +## 0.8.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.8.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 0.8.1-next.0 + +### Patch Changes + +- f36bcf9: Added support for file deletion to `publish:github:pull-request` action. + + Example usage: + + ```diff + - action: publish:github:pull-request + id: clean-up-pr + input: + description: This is the description + + filesToDelete: + + - outdated/changelog.md + + - sample-file.txt + owner: owner + repo: repo + title: Title Goes Here + + ``` + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.8.0 + +### Minor Changes + +- 5863b04: **BREAKING CHANGES** + + The `createGithubEnvironmentAction` action no longer requires an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. + + Unless you're providing your own override action to the default, this should be a non-breaking change. + + You can migrate using the following if you're getting typescript errors: + + ```ts + import { catalogServiceRef } from '@backstage/plugin-catalog-node'; + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + export const myModule = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'test', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + catalog: catalogServiceRef, + }, + async init({ scaffolder, catalog }) { + scaffolder.addActions( + createGithubEnvironmentAction({ + catalog, + }), + ); + }, + }); + }, + }); + ``` + +### Patch Changes + +- 575c76b: Migrate to using new actions +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + +## 0.8.0-next.2 + +### Minor Changes + +- 5863b04: **BREAKING CHANGES** + + The `createGithubEnvironmentAction` action no longer requires an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. + + Unless you're providing your own override action to the default, this should be a non-breaking change. + + You can migrate using the following if you're getting typescript errors: + + ```ts + import { catalogServiceRef } from '@backstage/plugin-catalog-node'; + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + export const myModule = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'test', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + catalog: catalogServiceRef, + }, + async init({ scaffolder, catalog }) { + scaffolder.addActions( + createGithubEnvironmentAction({ + catalog, + }), + ); + }, + }); + }, + }); + ``` + +### Patch Changes + +- 575c76b: Migrate to using new actions +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + ## 0.7.2-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-github/package.json b/plugins/scaffolder-backend-module-github/package.json index 1df39a57be..19b29ae60a 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.2-next.1", + "version": "0.8.1-next.2", "description": "The github module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/scaffolder-backend-module-github/report.api.md b/plugins/scaffolder-backend-module-github/report.api.md index 28f710557a..2941be7702 100644 --- a/plugins/scaffolder-backend-module-github/report.api.md +++ b/plugins/scaffolder-backend-module-github/report.api.md @@ -446,6 +446,7 @@ export const createPublishGithubPullRequestAction: ( branchName: string; title: string; description: string; + filesToDelete?: string[] | undefined; targetBranchName?: string | undefined; draft?: boolean | undefined; sourcePath?: string | undefined; diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts index 670af15595..5a8019579b 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.test.ts @@ -24,9 +24,11 @@ import { TemplateAction, } from '@backstage/plugin-scaffolder-node'; import fs from 'fs-extra'; +import path from 'node:path'; import { createPublishGithubPullRequestAction } from './githubPullRequest'; import { createMockDirectory } from '@backstage/backend-test-utils'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; +import { DELETE_FILE } from 'octokit-plugin-create-pull-request'; type GithubPullRequestActionInput = ReturnType< typeof createPublishGithubPullRequestAction @@ -50,8 +52,6 @@ describe('createPublishGithubPullRequestAction', () => { const workspacePath = mockDir.resolve('workspace'); beforeEach(() => { - mockDir.clear(); - config = new ConfigReader({}); integrations = ScmIntegrations.fromConfig(config); fakeClient = { @@ -92,6 +92,7 @@ describe('createPublishGithubPullRequestAction', () => { }); afterEach(() => { + mockDir.clear(); jest.resetAllMocks(); }); @@ -304,6 +305,118 @@ describe('createPublishGithubPullRequestAction', () => { }); }); + describe('with filesToDelete', () => { + let input: GithubPullRequestActionInput; + let ctx: ActionContext<GithubPullRequestActionInput, any, any>; + + beforeEach(() => { + input = { + filesToDelete: ['changed-file-to-delete.txt', 'delete-me-too.md'], + repoUrl: 'github.com?owner=myorg&repo=myrepo', + title: 'Create my new app', + branchName: 'new-app', + description: 'This PR is really good', + }; + + mockDir.setContent({ + [workspacePath]: { + 'catpants.md': 'cat + pants', + 'changed-file-to-delete.txt': 'file is changed and deleted', + 'foobar.txt': 'Hello there!', + }, + }); + + ctx = createMockActionContext({ input, workspacePath }); + }); + + it('should delete named files', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: input.title, + head: input.branchName, + body: input.description, + changes: [ + { + commit: input.title, + files: { + 'catpants.md': { + content: Buffer.from('cat + pants').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + 'foobar.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + 'changed-file-to-delete.txt': DELETE_FILE, + 'delete-me-too.md': DELETE_FILE, + }, + }, + ], + }); + }); + + describe('with targetPath', () => { + const targetPath = `target-path-${Date.now()}`; + + beforeEach(() => { + Object.assign(input, { + filesToDelete: [ + path.posix.join('nested', 'catpants.md'), + path.posix.join('nested', 'delete-me.too'), + ], + targetPath, + }); + + mockDir.setContent({ + [workspacePath]: { + 'catpants.md': 'cat + pants', + 'foobar.txt': 'Hello there!', + [path.posix.join('nested', 'catpants.md')]: 'delete me', + [path.posix.join('nested', 'delete-me.too')]: 'delete me too', + }, + }); + }); + + it('should delete named files', async () => { + await instance.handler(ctx); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'myorg', + repo: 'myrepo', + title: input.title, + head: input.branchName, + body: input.description, + changes: [ + { + commit: input.title, + files: { + [path.posix.join(targetPath, 'catpants.md')]: { + content: Buffer.from('cat + pants').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + [path.posix.join(targetPath, 'foobar.txt')]: { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + [path.posix.join(targetPath, 'nested', 'catpants.md')]: + DELETE_FILE, + [path.posix.join(targetPath, 'nested', 'delete-me.too')]: + DELETE_FILE, + }, + }, + ], + }); + }); + }); + }); + describe('with repoUrl', () => { let input: GithubPullRequestActionInput; let ctx: ActionContext<GithubPullRequestActionInput, any, any>; diff --git a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts index 54b90e3544..7d1e88d0c7 100644 --- a/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts +++ b/plugins/scaffolder-backend-module-github/src/actions/githubPullRequest.ts @@ -27,7 +27,10 @@ import { } from '@backstage/plugin-scaffolder-node'; import { Octokit } from 'octokit'; import { CustomErrorBase, InputError } from '@backstage/errors'; -import { createPullRequest } from 'octokit-plugin-create-pull-request'; +import { + createPullRequest, + DELETE_FILE, +} from 'octokit-plugin-create-pull-request'; import { getOctokitOptions } from '../util'; import { examples } from './githubPullRequest.examples'; import { @@ -143,6 +146,12 @@ export const createPublishGithubPullRequestAction = ( z.string({ description: 'The name for the branch', }), + filesToDelete: z => + z + .array(z.string(), { + description: 'List of files that will be deleted', + }) + .optional(), targetBranchName: z => z .string({ @@ -269,6 +278,7 @@ export const createPublishGithubPullRequestAction = ( const { repoUrl, branchName, + filesToDelete, targetBranchName, title, description, @@ -323,8 +333,8 @@ export const createPublishGithubPullRequestAction = ( file: SerializedFile, ): 'utf-8' | 'base64' => (file.symlink ? 'utf-8' : 'base64'); - const files = Object.fromEntries( - directoryContents.map(file => [ + const files = Object.fromEntries([ + ...directoryContents.map(file => [ targetPath ? path.posix.join(targetPath, file.path) : file.path, { // See the properties of tree items @@ -341,7 +351,13 @@ export const createPublishGithubPullRequestAction = ( content: file.content.toString(determineFileEncoding(file)), }, ]), - ); + // order of arrays is important so filesToDelete will overwrite + // changes from files above + ...(filesToDelete || []).map(filePath => [ + targetPath ? path.posix.join(targetPath, filePath) : filePath, + DELETE_FILE, + ]), + ]); // If this is a dry run, log and return if (ctx.isDryRun) { diff --git a/plugins/scaffolder-backend-module-github/src/util.ts b/plugins/scaffolder-backend-module-github/src/util.ts index c25ca99f5f..260c93993f 100644 --- a/plugins/scaffolder-backend-module-github/src/util.ts +++ b/plugins/scaffolder-backend-module-github/src/util.ts @@ -89,7 +89,7 @@ export async function getOctokitOptions(options: { if (!owner || !repo) { throw new InputError( - `No owner and/or owner provided, which is required if a token is not provided`, + `No owner and/or repo provided, which is required if a token is not provided`, ); } diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index a2fb152ee4..bcb024d124 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.9.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.9.3-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.9.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.9.2 + +### Patch Changes + +- 063b2d3: Fixed dependency to frontend package from tests +- a296637: Migrate to new action format +- 3d6493a: Support merge request labels in publish:gitlab:merge-request +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + +## 0.9.2-next.2 + +### Patch Changes + +- a296637: Migrate to new action format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + ## 0.9.2-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 03965d92cd..021924a829 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.2-next.1", + "version": "0.9.3-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", @@ -59,7 +59,6 @@ "devDependencies": { "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/core-app-api": "workspace:^", "@backstage/plugin-scaffolder-node-test-utils": "workspace:^" } } diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts index 4d8e0fd0c0..449c5980a4 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import yaml from 'yaml'; import { createGitlabGroupEnsureExistsAction } from './gitlabGroupEnsureExists'; import { examples } from './gitlabGroupEnsureExists.examples'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Groups: { @@ -50,15 +50,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -91,15 +93,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -138,15 +142,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -170,15 +176,17 @@ describe('gitlab:group:ensureExists', () => { }); it(`Should ${examples[3].description}`, async () => { - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -213,15 +221,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); @@ -264,15 +274,17 @@ describe('gitlab:group:ensureExists', () => { full_path: 'group1/group2/group3/group4', }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://api.gitlab.com', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts index 3d703eb91f..8e52ee999e 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabGroupEnsureExists.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createGitlabGroupEnsureExistsAction } from './gitlabGroupEnsureExists'; import { getClient } from '../util'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Groups: { @@ -45,15 +45,17 @@ describe('gitlab:group:ensureExists', () => { jest.clearAllMocks(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'tokenlols', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts index 917ba61d91..858adb4b99 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createGitlabIssueAction } from './gitlabIssueCreate'; import { examples } from './gitlabIssueCreate.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -46,15 +46,17 @@ describe('gitlab:issues:create', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'sample-token', - apiBaseUrl: 'https://gitlab.com/api/v1', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'sample-token', + apiBaseUrl: 'https://gitlab.com/api/v1', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts index d5aa2d1e17..e09bfaf249 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueCreate.test.ts @@ -14,11 +14,11 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { IssueType } from '../commonGitlabConfig'; import { createGitlabIssueAction } from './gitlabIssueCreate'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -45,15 +45,17 @@ describe('gitlab:issues:create', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts index 7e50eac66c..fa53609c19 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.examples.test.ts @@ -15,11 +15,11 @@ */ import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { editGitlabIssueAction } from './gitlabIssueEdit'; import { examples } from './gitlabIssueEdit.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -46,15 +46,17 @@ describe('gitlab:issue:edit', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts index 9604fe8fe2..a16236a509 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabIssueEdit.test.ts @@ -15,10 +15,10 @@ */ import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { IssueType } from '../commonGitlabConfig'; import { editGitlabIssueAction } from './gitlabIssueEdit'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { Issues: { @@ -45,15 +45,17 @@ describe('gitlab:issue:edit', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'myIntegrationsToken', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'myIntegrationsToken', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts index 53cbfb0b87..41535479fe 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.examples.test.ts @@ -14,12 +14,12 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createTriggerGitlabPipelineAction } from './gitlabPipelineTrigger'; import { examples } from './gitlabPipelineTrigger.examples'; import yaml from 'yaml'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { PipelineTriggerTokens: { @@ -49,15 +49,17 @@ describe('gitlab:pipeline:trigger', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'glpat-abcdef', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'glpat-abcdef', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts index 3803198ec8..89498c52f2 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/gitlabPipelineTrigger.test.ts @@ -14,10 +14,10 @@ * limitations under the License. */ -import { ConfigReader } from '@backstage/core-app-api'; import { ScmIntegrations } from '@backstage/integration'; import { createMockActionContext } from '@backstage/plugin-scaffolder-node-test-utils'; import { createTriggerGitlabPipelineAction } from './gitlabPipelineTrigger'; +import { mockServices } from '@backstage/backend-test-utils'; const mockGitlabClient = { PipelineTriggerTokens: { @@ -47,15 +47,17 @@ describe('gitlab:pipeline:trigger', () => { jest.useRealTimers(); }); - const config = new ConfigReader({ - integrations: { - gitlab: [ - { - host: 'gitlab.com', - token: 'glpat-abcdef', - apiBaseUrl: 'https://gitlab.com/api/v4', - }, - ], + const config = mockServices.rootConfig({ + data: { + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'glpat-abcdef', + apiBaseUrl: 'https://gitlab.com/api/v4', + }, + ], + }, }, }); const integrations = ScmIntegrations.fromConfig(config); diff --git a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md index cc8b465587..d2776c1732 100644 --- a/plugins/scaffolder-backend-module-notifications/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-notifications/CHANGELOG.md @@ -1,5 +1,55 @@ # @backstage/plugin-scaffolder-backend-module-notifications +## 0.1.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.1.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-notifications-common@0.0.10-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + - @backstage/plugin-notifications-node@0.2.17-next.0 + +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + - @backstage/plugin-notifications-common@0.0.9 + +## 0.1.11 + +### Patch Changes + +- 1fb5f06: Adds ability for user to turn on/off notifications for specific topics within an origin. +- 3f56115: Removed `octokit` dependency as it was not being used +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/plugin-notifications-common@0.0.9 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-notifications-node@0.2.16 + +## 0.1.11-next.2 + +### Patch Changes + +- 3f56115: Removed `octokit` dependency as it was not being used +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/plugin-notifications-common@0.0.9-next.0 + - @backstage/plugin-notifications-node@0.2.16-next.1 + ## 0.1.11-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-notifications/package.json b/plugins/scaffolder-backend-module-notifications/package.json index a89f504f96..3f09e36e30 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.11-next.1", + "version": "0.1.12-next.2", "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 7aeb534e06..d9aafeadf2 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,60 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.5.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.5.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.5.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + +## 0.5.10 + +### Patch Changes + +- a579693: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + +## 0.5.10-next.2 + +### Patch Changes + +- a579693: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + ## 0.5.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index f2293a966a..38c750941a 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.10-next.1", + "version": "0.5.11-next.2", "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 433f6060d8..b1ee9639af 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,53 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.2.11-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.2.11-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.2.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10 + +### Patch Changes + +- 6c6fb4a: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + +## 0.2.10-next.2 + +### Patch Changes + +- 6c6fb4a: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + ## 0.2.10-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index b5b56d213f..72e2e95f82 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.10-next.1", + "version": "0.2.11-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend-module-sentry/report.api.md b/plugins/scaffolder-backend-module-sentry/report.api.md index 8c716b3ab3..98b3c8b599 100644 --- a/plugins/scaffolder-backend-module-sentry/report.api.md +++ b/plugins/scaffolder-backend-module-sentry/report.api.md @@ -16,6 +16,7 @@ export function createSentryCreateProjectAction(options: { teamSlug: string; name: string; slug?: string | undefined; + platform?: string | undefined; authToken?: string | undefined; }, { diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.test.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.test.ts index 3e31aef3bc..6fab15f59f 100644 --- a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.test.ts +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.test.ts @@ -43,6 +43,7 @@ describe('sentry:project:create action', () => { teamSlug: string; name: string; slug?: string; + platform?: string; authToken?: string; }> => createMockActionContext({ @@ -56,12 +57,14 @@ describe('sentry:project:create action', () => { }, }); - it(`should ${examples[0].description}`, async () => { + it(`should ${examples[0].description} ${ + yaml.parse(examples[0].example).steps[2].name + }`, async () => { expect.assertions(3); let input; try { - input = yaml.parse(examples[0].example).steps[1].input; + input = yaml.parse(examples[0].example).steps[2].input; } catch (error) { console.error('Failed to parse YAML:', error); } @@ -97,7 +100,9 @@ describe('sentry:project:create action', () => { }); }); - it(`should ${examples[0].description}`, async () => { + it(`should ${examples[0].description} ${ + yaml.parse(examples[0].example).steps[0].name + }`, async () => { expect.assertions(3); let input; @@ -140,6 +145,51 @@ describe('sentry:project:create action', () => { }); }); + it(`should ${examples[0].description} ${ + yaml.parse(examples[0].example).steps[1].name + }`, async () => { + expect.assertions(3); + + let input; + try { + input = yaml.parse(examples[0].example).steps[1].input; + } catch (error) { + console.error('Failed to parse YAML:', error); + } + + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + actionContext.input = { ...actionContext.input, platform: 'platform-a' }; + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${input.organizationSlug}/${input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer c25711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d5de85`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: 'Scaffolded project A', + platform: 'platform-a', + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await action.handler({ + ...actionContext, + input: { + ...actionContext.input, + ...input, + }, + }); + }); + it(`should ${examples[1].description}`, async () => { expect.assertions(3); diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts index 3af88a4663..1744a995a9 100644 --- a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.examples.ts @@ -38,7 +38,20 @@ export const examples: TemplateExample[] = [ { id: 'create-sentry-project', action: 'sentry:project:create', - name: 'Create a Sentry project without providing a project slug.', + name: 'Create a Sentry project with provided platform.', + input: { + organizationSlug: 'my-org', + teamSlug: 'team-a', + name: 'Scaffolded project A', + platform: 'platform-a', + authToken: + 'c25711beb516e1e910d2ede554dc1bf725654ef3c75e5a9106de9aec13d5de85', + }, + }, + { + id: 'create-sentry-project', + action: 'sentry:project:create', + name: 'Create a Sentry project without optional parameters.', input: { organizationSlug: 'my-org', teamSlug: 'team-b', diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts index 48c437d2a4..0279c198a9 100644 --- a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.test.ts @@ -42,6 +42,7 @@ describe('sentry:project:create action', () => { teamSlug: string; name: string; slug?: string; + platform?: string; authToken?: string; }> => createMockActionContext({ @@ -113,6 +114,37 @@ describe('sentry:project:create action', () => { await action.handler(actionContext); }); + it('should request sentry project create with added optional specified platform', async () => { + expect.assertions(3); + + const action = createSentryCreateProjectAction(createScaffolderConfig()); + const actionContext = getActionContext(); + actionContext.input = { ...actionContext.input, platform: 'platform-slug' }; + + worker.use( + http.post( + `https://sentry.io/api/0/teams/${actionContext.input.organizationSlug}/${actionContext.input.teamSlug}/projects/`, + async ({ request }) => { + expect(request.headers.get('Authorization')).toBe( + `Bearer ${actionContext.input.authToken}`, + ); + expect(request.headers.get('Content-Type')).toBe(`application/json`); + await expect(request.json()).resolves.toEqual({ + name: actionContext.input.name, + slug: actionContext.input.slug, + platform: actionContext.input.platform, + }); + return HttpResponse.json( + { detail: 'project creation mocked result' }, + { status: 201 }, + ); + }, + ), + ); + + await action.handler(actionContext); + }); + it('should take Sentry auth token from scaffolder config when input authToken is missing.', async () => { expect.assertions(3); diff --git a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts index 226c31bd05..57f8b349e0 100644 --- a/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts +++ b/plugins/scaffolder-backend-module-sentry/src/actions/createProject.ts @@ -54,6 +54,12 @@ export function createSentryCreateProjectAction(options: { config: Config }) { 'Optional slug for the new project. If not provided a slug is generated from the name', }) .optional(), + platform: z => + z + .string({ + description: 'Optional sentry platform for the new project. ', + }) + .optional(), authToken: z => z .string({ @@ -64,7 +70,8 @@ export function createSentryCreateProjectAction(options: { config: Config }) { }, }, async handler(ctx) { - const { organizationSlug, teamSlug, name, slug, authToken } = ctx.input; + const { organizationSlug, teamSlug, name, slug, platform, authToken } = + ctx.input; const body: any = { name: name, @@ -74,6 +81,10 @@ export function createSentryCreateProjectAction(options: { config: Config }) { body.slug = slug; } + if (platform) { + body.platform = platform; + } + const token = authToken ? authToken : config.getOptionalString('scaffolder.sentry.token'); diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index 5faceb7501..866398946f 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.4.12-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + - @backstage/plugin-scaffolder-node-test-utils@0.3.1-next.2 + +## 0.4.12-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + - @backstage/plugin-scaffolder-node-test-utils@0.3.1-next.1 + +## 0.4.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node-test-utils@0.3.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/types@1.2.1 + +## 0.4.11 + +### Patch Changes + +- b392a34: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node-test-utils@0.3.0 + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/types@1.2.1 + +## 0.4.11-next.2 + +### Patch Changes + +- b392a34: Migrate to new actions format +- Updated dependencies + - @backstage/plugin-scaffolder-node-test-utils@0.3.0-next.2 + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/types@1.2.1 + ## 0.4.11-next.1 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index a121d7ef0c..58ff0094ee 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.11-next.1", + "version": "0.4.12-next.2", "backstage": { "role": "backend-plugin-module", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-backend/.eslintrc.js b/plugins/scaffolder-backend/.eslintrc.js index 953af54f90..3c5416068f 100644 --- a/plugins/scaffolder-backend/.eslintrc.js +++ b/plugins/scaffolder-backend/.eslintrc.js @@ -5,13 +5,13 @@ module.exports = require('@backstage/cli/config/eslint-factory')(__dirname, { name: 'path', importNames: ['resolve'], message: - 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues', + 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-plugin-api` instead as it prevents security issues', }, ], restrictedSrcSyntax: [ { message: - 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-common` instead as it prevents security issues', + 'Do not use path.resolve, use `resolveSafeChildPath` from `@backstage/backend-plugin-api` instead as it prevents security issues', selector: 'MemberExpression[object.name="path"][property.name="resolve"]', }, ], diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index 629f1a23bd..3e05651276 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,310 @@ # @backstage/plugin-scaffolder-backend +## 2.1.0-next.2 + +### Minor Changes + +- c1ce316: BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + + BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + + BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.10-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.12-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.11-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.8.1-next.2 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.3-next.2 + +## 2.0.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.12-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.11-next.1 + - @backstage/plugin-scaffolder-backend-module-github@0.8.1-next.1 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.3-next.1 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.10-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 2.0.1-next.0 + +### Patch Changes + +- dbde180: An internal refactor which adds additional types to experimental checkpoints +- fc70b43: Replaced deprecated uses of `@backstage/backend-common` with the equivalents in `@backstage/backend-defaults` and `@backstage/backend-plugin-api`. +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/plugin-scaffolder-backend-module-github@0.8.1-next.0 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.9 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.12-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.11-next.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.11-next.0 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.11-next.0 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.11-next.0 + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.3-next.0 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.11-next.0 + - @backstage/plugin-bitbucket-cloud-common@0.3.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 2.0.0 + +### Major Changes + +- 33394db: **BREAKING CHANGES** + + Removal of deprecated re-exports from module packages. + + The following functions have been re-exported from the `scaffolder-backend` plugin for quite some time, and now it's time to clean them up. They've been moved as follows: + + - `createPublishAzureAction` should be imported from `@backstage/plugin-scaffolder-backend-module-azure` instead. + + - `createPublishBitbucketCloudAction` should be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket-cloud` instead. + + - `createPublishBitbucketServerAction` and `createPublishBitbucketServerPullRequestAction` can be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket-server` instead. + + - `createPublishBitbucketAction` should be imported from `@backstage/plugin-scaffolder-backend-module-bitbucket` instead. + + - `createPublishGerritAction` and `createPublishGerritReviewAction` can be imported from `@backstage/plugin-scaffolder-backend-module-gerrit` instead. + + - `createGithubActionsDispatchAction`, `createGithubDeployKeyAction`, `createGithubEnvironmentAction`, `createGithubIssuesLabelAction`, `CreateGithubPullRequestActionOptions`, `createGithubRepoCreateAction`, `createGithubRepoPushAction`, `createGithubWebhookAction`, and `createPublishGithubAction` can be imported from `@backstage/plugin-scaffolder-backend-module-github` instead. + + - `createPublishGitlabAction` should be imported from `@backstage/plugin-scaffolder-backend-module-gitlab` instead. + + - `ActionContext`. `createTemplateAction`, `executeShellCommand`, `ExecuteShellCommandOptions`, `fetchContents`, `TaskSecrets`, and `TemplateAction` should be imported from `@backstage/plugin-scaffolder-node` instead. + + - `ScaffolderEntitiesProcessor` should be imported from `@backstage/plugin-catalog-backend-module-scaffolder-entity-model` instead. + +- a8fcf04: **BREAKING ALPHA**: The `/alpha` export no longer exports the plugin. Please use `import('@backstage/plugin-scaffolder-backend')` instead as this has been removed. + + **BREAKING CHANGES**: The old `createRouter` function which was used in the old backend system has been removed along with the `RouterOptions` type. + +- 73b94d7: **BREAKING CHANGES** + + The following functions have been re-exported from the `scaffolder-backend` plugin for quite some time, and now it's time to clean them up. They've been moved as follows: + + - `SerializedTask`, `SerializedTaskEvent`, `TaskBroker`, `TaskBrokerDispatchOptions`, `TaskBrokerDispatchResult`, `TaskCompletionState`, `TaskContext`, `TaskEventType`, `TaskStatus`, `TemplateFilter`, and `TemplateGlobal` should be imported from `@backstage/plugin-scaffolder-node` instead. + + - The deprecated `copyWithoutRender` option has been removed from `fetch:template` action. You should rename the option to `copyWithoutTemplating` instead. + +- 5863b04: **BREAKING CHANGES** + + - The `createBuiltinActions` method has been removed, as this should no longer be needed with the new backend system route, and was only useful when passing the default list of actions again in the old backend system. You should be able to rely on the default behaviour of the new backend system which is to merge the actions. + + - The `createCatalogRegisterAction` and `createFetchCatalogEntityAction` actions no longer require an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. + + Unless you're providing your own override action to the default, this should be a non-breaking change. + + You can migrate using the following if you're getting typescript errors: + + ```ts + import { catalogServiceRef } from '@backstage/plugin-catalog-node'; + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + export const myModule = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'test', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + catalog: catalogServiceRef, + }, + async init({ scaffolder, catalog }) { + scaffolder.addActions( + createCatalogRegisterAction({ + catalog, + }), + createFetchCatalogEntityAction({ + catalog, + integrations, + }), + ); + }, + }); + }, + }); + ``` + +### Minor Changes + +- 73b94d7: **DEPRECATIONS** + + The following types and implementations have been deprecated, either because they're no longer relevant, or because upcoming changes to the `scaffolder-backend` after `2.0.0` will influence the changes to these API surfaces. + + - `CreateWorkerOptions` + - `DatabaseTaskStore` + - `DatabaseTaskStoreOptions` + - `TaskManager` + - `TaskStoreCreateTaskOptions` + - `TaskStoreCreateTaskResult` + - `TaskStoreEmitOptions` + - `TaskStoreListEventsOptions` + - `TaskStoreRecoverTaskOptions` + - `TaskStoreShutDownTaskOptions` + + There is no current path off deprecation, these types are going to be removed and rethought with a better way to define workers in the new backend system. + +### Patch Changes + +- 89a941d: Migrating to latest action format +- 023629e: Enable usage of secrets within 'each' step of software templates. For example, you can now structure your `each` step like this: + + ``` + each: + [ + { name: "Service1", token: "${{ secrets.token1 }}" }, + { name: "Service2", token: "${{ secrets.token2 }}" }, + ] + ``` + +- e92e481: Add tests for Scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.2 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.10 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10 + - @backstage/plugin-scaffolder-backend-module-github@0.8.0 + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.10 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.10 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.11 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.9 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 2.0.0-next.2 + +### Major Changes + +- 5863b04: **BREAKING CHANGES** + + - The `createBuiltinActions` method has been removed, as this should no longer be needed with the new backend system route, and was only useful when passing the default list of actions again in the old backend system. You should be able to rely on the default behaviour of the new backend system which is to merge the actions. + + - The `createCatalogRegisterAction` and `createFetchCatalogEntityAction` actions no longer require an `AuthService`, and now accepts a `CatalogService` instead of `CatalogClient`. + + Unless you're providing your own override action to the default, this should be a non-breaking change. + + You can migrate using the following if you're getting typescript errors: + + ```ts + import { catalogServiceRef } from '@backstage/plugin-catalog-node'; + import { scaffolderActionsExtensionPoint } from '@backstage/plugin-scaffolder-node/alpha'; + + export const myModule = createBackendModule({ + pluginId: 'scaffolder', + moduleId: 'test', + register({ registerInit }) { + registerInit({ + deps: { + scaffolder: scaffolderActionsExtensionPoint, + catalog: catalogServiceRef, + }, + async init({ scaffolder, catalog }) { + scaffolder.addActions( + createCatalogRegisterAction({ + catalog, + }), + createFetchCatalogEntityAction({ + catalog, + integrations, + }), + ); + }, + }); + }, + }); + ``` + +### Patch Changes + +- 89a941d: Migrating to latest action format +- 023629e: Enable usage of secrets within 'each' step of software templates. For example, you can now structure your `each` step like this: + + ``` + each: + [ + { name: "Service1", token: "${{ secrets.token1 }}" }, + { name: "Service2", token: "${{ secrets.token2 }}" }, + ] + ``` + +- e92e481: Add tests for Scaffolder +- Updated dependencies + - @backstage/plugin-scaffolder-backend-module-gitlab@0.9.2-next.2 + - @backstage/plugin-scaffolder-backend-module-github@0.8.0-next.2 + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.4-next.1 + - @backstage/plugin-bitbucket-cloud-common@0.3.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.9-next.1 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-events-node@0.4.12-next.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1-next.1 + - @backstage/plugin-scaffolder-backend-module-azure@0.2.10-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket@0.3.11-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-cloud@0.2.10-next.2 + - @backstage/plugin-scaffolder-backend-module-bitbucket-server@0.2.10-next.2 + - @backstage/plugin-scaffolder-backend-module-gerrit@0.2.10-next.2 + - @backstage/plugin-scaffolder-backend-module-gitea@0.2.10-next.2 + - @backstage/plugin-scaffolder-common@1.5.11 + ## 2.0.0-next.1 ### Major Changes diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d3db857d91..62d72203b7 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "2.0.0-next.1", + "version": "2.1.0-next.2", "description": "The Backstage backend plugin that helps you create new things", "backstage": { "role": "backend-plugin", @@ -61,7 +61,6 @@ "test": "backstage-cli package test" }, "dependencies": { - "@backstage/backend-common": "^0.25.0", "@backstage/backend-defaults": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", diff --git a/plugins/scaffolder-backend/report-alpha.api.md b/plugins/scaffolder-backend/report-alpha.api.md index 958d68fc25..966d71acaa 100644 --- a/plugins/scaffolder-backend/report-alpha.api.md +++ b/plugins/scaffolder-backend/report-alpha.api.md @@ -10,6 +10,8 @@ import { PermissionCondition } from '@backstage/plugin-permission-common'; import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { ResourcePermission } from '@backstage/plugin-permission-common'; +import { SerializedTask } from '@backstage/plugin-scaffolder-node'; +import { TaskFilter } from '@backstage/plugin-scaffolder-node'; import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; @@ -19,6 +21,12 @@ export const createScaffolderActionConditionalDecision: ( conditions: PermissionCriteria<PermissionCondition<'scaffolder-action'>>, ) => ConditionalPolicyDecision; +// @alpha (undocumented) +export const createScaffolderTaskConditionalDecision: ( + permission: ResourcePermission<'scaffolder-task'>, + conditions: PermissionCriteria<PermissionCondition<'scaffolder-task'>>, +) => ConditionalPolicyDecision; + // @alpha export const createScaffolderTemplateConditionalDecision: ( permission: ResourcePermission<'scaffolder-template'>, @@ -76,6 +84,18 @@ export const scaffolderActionConditions: Conditions<{ >; }>; +// @alpha +export const scaffolderTaskConditions: Conditions<{ + isTaskOwner: PermissionRule< + SerializedTask, + TaskFilter, + 'scaffolder-task', + { + createdBy: string[]; + } + >; +}>; + // @alpha export const scaffolderTemplateConditions: Conditions<{ hasTag: PermissionRule< diff --git a/plugins/scaffolder-backend/report.api.md b/plugins/scaffolder-backend/report.api.md index 1f2a17a384..ada936c6e9 100644 --- a/plugins/scaffolder-backend/report.api.md +++ b/plugins/scaffolder-backend/report.api.md @@ -14,9 +14,9 @@ import { Duration } from 'luxon'; import { EventsService } from '@backstage/plugin-events-node'; import { HumanDuration } from '@backstage/types'; import { JsonObject } from '@backstage/types'; -import { JsonValue } from '@backstage/types'; import { Knex } from 'knex'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { PermissionEvaluator } from '@backstage/plugin-permission-common'; import { PermissionRule } from '@backstage/plugin-permission-node'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; @@ -28,6 +28,7 @@ import { SerializedTaskEvent } from '@backstage/plugin-scaffolder-node'; import { TaskBroker } from '@backstage/plugin-scaffolder-node'; import { TaskCompletionState } from '@backstage/plugin-scaffolder-node'; import { TaskContext } from '@backstage/plugin-scaffolder-node'; +import { TaskFilters } from '@backstage/plugin-scaffolder-node'; import { TaskRecovery } from '@backstage/plugin-scaffolder-common'; import { TaskSecrets } from '@backstage/plugin-scaffolder-node'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; @@ -38,6 +39,7 @@ import { TemplateEntityStepV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateFilter } from '@backstage/plugin-scaffolder-node'; import { TemplateGlobal } from '@backstage/plugin-scaffolder-node'; import { TemplateParametersV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha'; @@ -351,6 +353,7 @@ export class DatabaseTaskStore implements TaskStore { order: 'asc' | 'desc'; field: string; }[]; + permissionFilters?: PermissionCriteria<TaskFilters>; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number; @@ -442,19 +445,9 @@ export class TaskManager implements TaskContext { // (undocumented) get spec(): TaskSpecV1beta3; // (undocumented) - updateCheckpoint?( - options: - | { - key: string; - status: 'success'; - value: JsonValue; - } - | { - key: string; - status: 'failed'; - reason: string; - }, - ): Promise<void>; + get taskId(): string; + // (undocumented) + updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>; } // @public @deprecated @@ -498,6 +491,7 @@ export interface TaskStore { limit?: number; offset?: number; }; + permissionFilters?: PermissionCriteria<TaskFilters>; order?: { order: 'asc' | 'desc'; field: string; diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts index eb0418b5ed..03a85ad971 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.test.ts @@ -14,14 +14,19 @@ * limitations under the License. */ -import { DatabaseManager } from '@backstage/backend-common'; +import { DatabaseManager } from '@backstage/backend-defaults/database'; import { ConfigReader } from '@backstage/config'; import { DatabaseTaskStore, RawDbTaskEventRow } from './DatabaseTaskStore'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { ConflictError } from '@backstage/errors'; -import { createMockDirectory } from '@backstage/backend-test-utils'; +import { + mockServices, + createMockDirectory, +} from '@backstage/backend-test-utils'; import fs from 'fs-extra'; import { EventsService } from '@backstage/plugin-events-node'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; +import { TaskFilters } from '@backstage/plugin-scaffolder-node'; const createStore = async (events?: EventsService) => { const manager = DatabaseManager.fromConfig( @@ -33,7 +38,10 @@ const createStore = async (events?: EventsService) => { }, }, }), - ).forPlugin('scaffolder'); + ).forPlugin('scaffolder', { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }); const store = await DatabaseTaskStore.create({ database: manager, events, @@ -231,6 +239,56 @@ describe('DatabaseTaskStore', () => { expect(tasks[0].id).toBeDefined(); }); + it('should filter tasks based on permissionFilters', async () => { + const { store } = await createStore(); + + await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'user:default/one', + }); + + await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'user:default/two', + }); + + await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'user:default/three', + }); + + await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'user:default/one', + }); + + await store.createTask({ + spec: {} as TaskSpec, + createdBy: 'user:default/four', + }); + + const permissionFilters: PermissionCriteria<TaskFilters> = { + not: { + key: 'created_by', + values: ['user:default/three', 'user:default/four'], + }, + }; + + const { tasks, totalTasks } = await store.list({ + permissionFilters: permissionFilters, + }); + + expect(totalTasks).toBe(3); + + const createdByList = tasks.map(task => task.createdBy); + expect(createdByList).toEqual( + expect.arrayContaining(['user:default/one', 'user:default/two']), + ); + expect(createdByList).not.toEqual( + expect.arrayContaining(['user:default/three', 'user:default/four']), + ); + }); + it('should sent an event to start cancelling the task', async () => { const { store } = await createStore(eventsService); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts index f81a79a5a0..f07d827f45 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/DatabaseTaskStore.ts @@ -35,6 +35,7 @@ import { SerializedTask, SerializedTaskEvent, TaskEventType, + TaskFilter, TaskSecrets, TaskStatus, } from '@backstage/plugin-scaffolder-node'; @@ -48,6 +49,14 @@ import { } from '@backstage/plugin-scaffolder-node/alpha'; import { flattenParams } from '../../service/helpers'; import { EventsService } from '@backstage/plugin-events-node'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; +import { + isAndCriteria, + isNotCriteria, + isOrCriteria, +} from '@backstage/plugin-permission-node'; +import { TaskFilters } from '@backstage/plugin-scaffolder-node'; +import { compact } from 'lodash'; const migrationsDir = resolvePackagePath( '@backstage/plugin-scaffolder-backend', @@ -195,6 +204,48 @@ export class DatabaseTaskStore implements TaskStore { } } + private isTaskFilter(filter: any): filter is TaskFilter { + return filter.hasOwnProperty('key'); + } + + private parseFilter( + filter: PermissionCriteria<TaskFilters>, + query: Knex.QueryBuilder, + db: Knex, + negate: boolean = false, + ): Knex.QueryBuilder { + if (isNotCriteria(filter)) { + return this.parseFilter(filter.not, query, db, !negate); + } + + if (this.isTaskFilter(filter)) { + const values: string[] = compact(filter.values) ?? []; + if (negate) { + query.whereNotIn(filter.key, values); + } else { + query.whereIn(filter.key, values); + } + + return query; + } + + return query[negate ? 'andWhereNot' : 'andWhere'](subQuery => { + if (isOrCriteria(filter)) { + for (const subFilter of filter.anyOf ?? []) { + subQuery.orWhere(subQueryInner => + this.parseFilter(subFilter, subQueryInner, db, false), + ); + } + } else if (isAndCriteria(filter)) { + for (const subFilter of filter.allOf ?? []) { + subQuery.andWhere(subQueryInner => + this.parseFilter(subFilter, subQueryInner, db, false), + ); + } + } + }); + } + async list(options: { createdBy?: string; status?: TaskStatus; @@ -207,16 +258,31 @@ export class DatabaseTaskStore implements TaskStore { offset?: number; }; order?: { order: 'asc' | 'desc'; field: string }[]; + permissionFilters?: PermissionCriteria<TaskFilters>; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }> { - const { createdBy, status, pagination, order, filters } = options ?? {}; + const { createdBy, status, pagination, order, filters, permissionFilters } = + options ?? {}; const queryBuilder = this.db<RawDbTaskRow & { count: number }>('tasks'); - if (createdBy || filters?.createdBy) { - const arr: string[] = flattenParams<string>( - createdBy, - filters?.createdBy, - ); - queryBuilder.whereIn('created_by', [...new Set(arr)]); + const createdByValues = flattenParams<string>( + createdBy, + filters?.createdBy, + ); + + const combinedPermissionFilters: + | PermissionCriteria<TaskFilters> + | undefined = + createdByValues.length > 0 + ? { + allOf: [ + { key: 'created_by', values: createdByValues }, + ...(permissionFilters ? [permissionFilters] : []), + ], + } + : permissionFilters; + + if (combinedPermissionFilters) { + this.parseFilter(combinedPermissionFilters, queryBuilder, this.db); } if (status || filters?.status) { @@ -271,24 +337,29 @@ export class DatabaseTaskStore implements TaskStore { throw new NotFoundError(`No task with id '${taskId}' found`); } try { - const spec = JSON.parse(result.spec); - const secrets = result.secrets ? JSON.parse(result.secrets) : undefined; - const state = this.getState(result); - return { - id: result.id, - spec, - status: result.status, - lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at), - createdAt: parseSqlDateToIsoString(result.created_at), - createdBy: result.created_by ?? undefined, - secrets, - state, - }; + return this.parseTaskRow(result); } catch (error) { throw new Error(`Failed to parse spec of task '${taskId}', ${error}`); } } + private parseTaskRow(result: RawDbTaskRow): SerializedTask { + const spec = JSON.parse(result.spec); + const secrets = result.secrets ? JSON.parse(result.secrets) : undefined; + const state = this.getState(result); + + return { + id: result.id, + spec, + status: result.status, + lastHeartbeatAt: parseSqlDateToIsoString(result.last_heartbeat_at), + createdAt: parseSqlDateToIsoString(result.created_at), + createdBy: result.created_by ?? undefined, + secrets, + state, + }; + } + async createTask( options: TaskStoreCreateTaskOptions, ): Promise<TaskStoreCreateTaskResult> { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts index a4876f2edb..79a847c46d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.test.ts @@ -832,6 +832,42 @@ describe('NunjucksWorkflowRunner', () => { }); }); + it('should run a step repeatedly - flat values with secrets', async () => { + const secrets = { + s1: 'secret-value1', + s2: 'secret-value2', + s3: 'secret-value3', + }; + const task = createMockTaskWithSpec( + { + steps: [ + { + id: 'test', + name: 'name', + each: [ + '${{ secrets.s1 }}', + '${{ secrets.s2 }}', + '${{ secrets.s3 }}', + ], + action: 'jest-mock-action', + input: { secret: '${{each.value}}' }, + }, + ], + }, + secrets, + ); + await runner.execute(task); + + Object.values(secrets).forEach((secret, idx) => { + expectTaskLog( + `info: Running step each: {"key":"${idx}","value":"***"}`, + ); + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ input: { secret } }), + ); + }); + }); + it('should run a step repeatedly - object list', async () => { const task = createMockTaskWithSpec({ steps: [ @@ -862,6 +898,46 @@ describe('NunjucksWorkflowRunner', () => { ); }); + it('should run a step repeatedly - object list with secrets', async () => { + const secrets = { + s1: 'secret-value1', + s2: 'secret-value2', + }; + const names = ['Service1', 'Service2']; + const task = createMockTaskWithSpec( + { + steps: [ + { + id: 'test', + name: 'name', + each: [ + { name: names[0], token: '${{ secrets.s1 }}' }, + { name: names[1], token: '${{ secrets.s2 }}' }, + ], + action: 'jest-mock-action', + input: { + name: '${{each.value.name}}', + token: '${{each.value.token}}', + }, + }, + ], + }, + secrets, + ); + await runner.execute(task); + + Object.values(secrets).forEach((secret, idx) => { + expectTaskLog( + `info: Running step each: {"key":"${idx}","value":"[object Object]"}`, + ); + expect(fakeActionHandler).toHaveBeenCalledWith( + expect.objectContaining({ + input: { name: names[idx], token: secret }, + }), + ); + }); + }); + it('should run a step repeatedly - object', async () => { const settings = { color: 'blue', diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts index a66ae0e789..1542db8438 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/NunjucksWorkflowRunner.ts @@ -60,6 +60,10 @@ import { scaffolderActionRules } from '../../service/rules'; import { createCounterMetric, createHistogramMetric } from '../../util/metrics'; import { BackstageLoggerTransport, WinstonLogger } from './logger'; import { convertFiltersToRecord } from '../../util/templating'; +import { + CheckpointState, + CheckpointContext, +} from '@backstage/plugin-scaffolder-node/alpha'; type NunjucksWorkflowRunnerOptions = { workingDirectory: string; @@ -91,16 +95,6 @@ type TemplateContext = { }; }; -type CheckpointState = - | { - status: 'failed'; - reason: string; - } - | { - status: 'success'; - value: JsonValue; - }; - const isValidTaskSpec = (taskSpec: TaskSpec): taskSpec is TaskSpecV1beta3 => { return taskSpec.apiVersion === 'scaffolder.backstage.io/v1beta3'; }; @@ -299,7 +293,12 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { } const resolvedEach = - step.each && this.render(step.each, context, renderTemplate); + step.each && + this.render( + step.each, + { ...context, secrets: task.secrets ?? {} }, + renderTemplate, + ); if (step.each && !resolvedEach) { throw new InputError( @@ -379,10 +378,9 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { secrets: task.secrets ?? {}, logger: taskLogger, workspacePath, - async checkpoint<T extends JsonValue | void>(opts: { - key?: string; - fn: () => Promise<T> | T; - }) { + async checkpoint<T extends JsonValue | void>( + opts: CheckpointContext<T>, + ) { const { key: checkpointKey, fn } = opts; const key = `v1.task.checkpoint.${step.id}.${checkpointKey}`; @@ -391,9 +389,7 @@ export class NunjucksWorkflowRunner implements WorkflowRunner { if (prevTaskState) { const prevState = ( - prevTaskState.state?.checkpoints as { - [key: string]: CheckpointState; - } + prevTaskState.state?.checkpoints as CheckpointState )?.[key]; if (prevState && prevState.status === 'success') { diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts index dc89100b95..2b13130908 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DatabaseManager } from '@backstage/backend-common'; +import { DatabaseManager } from '@backstage/backend-defaults/database'; import { ConfigReader } from '@backstage/config'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { @@ -36,7 +36,10 @@ async function createStore(): Promise<DatabaseTaskStore> { }, }, }), - ).forPlugin('scaffolder'); + ).forPlugin('scaffolder', { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }); return await DatabaseTaskStore.create({ database: manager, diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts index 86f9f7fcf7..5b4d568144 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/StorageTaskBroker.ts @@ -29,33 +29,24 @@ import { TaskBrokerDispatchOptions, TaskCompletionState, TaskContext, + TaskFilters, TaskSecrets, TaskStatus, } from '@backstage/plugin-scaffolder-node'; -import { WorkspaceProvider } from '@backstage/plugin-scaffolder-node/alpha'; import { - JsonObject, - JsonValue, - Observable, - createDeferred, -} from '@backstage/types'; + CheckpointState, + WorkspaceProvider, + UpdateTaskCheckpointOptions, +} from '@backstage/plugin-scaffolder-node/alpha'; +import { JsonObject, Observable, createDeferred } from '@backstage/types'; import ObservableImpl from 'zen-observable'; import { DefaultWorkspaceService, WorkspaceService } from './WorkspaceService'; import { readDuration } from './helper'; import { InternalTaskSecrets, TaskStore } from './types'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; type TaskState = { - checkpoints: { - [key: string]: - | { - status: 'failed'; - reason: string; - } - | { - status: 'success'; - value: JsonValue; - }; - }; + checkpoints: CheckpointState; }; /** * TaskManager @@ -105,6 +96,10 @@ export class TaskManager implements TaskContext { private readonly auth?: AuthService, ) {} + get taskId() { + return this.task.taskId; + } + get spec() { return this.task.spec; } @@ -152,20 +147,9 @@ export class TaskManager implements TaskContext { return this.storage.getTaskState?.({ taskId: this.task.taskId }); } - async updateCheckpoint?( - options: - | { - key: string; - status: 'success'; - value: JsonValue; - } - | { - key: string; - status: 'failed'; - reason: string; - }, - ): Promise<void> { + async updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void> { const { key, ...value } = options; + if (this.task.state) { (this.task.state as TaskState).checkpoints[key] = value; } else { @@ -291,6 +275,7 @@ export class StorageTaskBroker implements TaskBroker { offset?: number; }; order?: { order: 'asc' | 'desc'; field: string }[]; + permissionFilters?: PermissionCriteria<TaskFilters>; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }> { if (!this.storage.list) { throw new Error( diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index dd9233a5ee..6fedc4df40 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -15,7 +15,7 @@ */ import os from 'os'; -import { DatabaseManager } from '@backstage/backend-common'; +import { DatabaseManager } from '@backstage/backend-defaults/database'; import { ConfigReader } from '@backstage/config'; import { DatabaseTaskStore } from './DatabaseTaskStore'; import { StorageTaskBroker } from './StorageTaskBroker'; @@ -49,7 +49,10 @@ async function createStore(): Promise<DatabaseTaskStore> { }, }, }), - ).forPlugin('scaffolder'); + ).forPlugin('scaffolder', { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }); return await DatabaseTaskStore.create({ database: manager, }); diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index b0c3166ff6..24871ca452 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -189,6 +189,7 @@ export class TaskWorker { meta: { actionType: 'execution', taskId: task.taskId, + createdBy: task.createdBy, taskParameters: task.spec.parameters, templateRef: task.spec.templateInfo?.entityRef, }, diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts index a3d9e3b741..62ffb171ff 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/types.ts @@ -23,7 +23,9 @@ import { SerializedTaskEvent, SerializedTask, TaskStatus, + TaskFilters, } from '@backstage/plugin-scaffolder-node'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; /** * TaskStoreEmitOptions @@ -131,6 +133,7 @@ export interface TaskStore { limit?: number; offset?: number; }; + permissionFilters?: PermissionCriteria<TaskFilters>; order?: { order: 'asc' | 'desc'; field: string }[]; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; diff --git a/plugins/scaffolder-backend/src/service/alpha.ts b/plugins/scaffolder-backend/src/service/alpha.ts index 7d22c9d262..ac14f0b1ba 100644 --- a/plugins/scaffolder-backend/src/service/alpha.ts +++ b/plugins/scaffolder-backend/src/service/alpha.ts @@ -17,9 +17,14 @@ import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, RESOURCE_TYPE_SCAFFOLDER_ACTION, + RESOURCE_TYPE_SCAFFOLDER_TASK, } from '@backstage/plugin-scaffolder-common/alpha'; import { createConditionExports } from '@backstage/plugin-permission-node'; -import { scaffolderTemplateRules, scaffolderActionRules } from './rules'; +import { + scaffolderTemplateRules, + scaffolderActionRules, + scaffolderTaskRules, +} from './rules'; const templateConditionExports = createConditionExports({ pluginId: 'scaffolder', @@ -33,6 +38,12 @@ const actionsConditionExports = createConditionExports({ rules: scaffolderActionRules, }); +const taskConditionExports = createConditionExports({ + pluginId: 'scaffolder', + resourceType: RESOURCE_TYPE_SCAFFOLDER_TASK, + rules: scaffolderTaskRules, +}); + /** * `createScaffolderTemplateConditionalDecision` can be used when authoring policies to * create conditional decisions. It requires a permission of type @@ -90,3 +101,17 @@ export const createScaffolderActionConditionalDecision = * @alpha */ export const scaffolderActionConditions = actionsConditionExports.conditions; + +/** + * @alpha + */ +export const createScaffolderTaskConditionalDecision = + taskConditionExports.createConditionalDecision; + +/** + * These conditions are used when creating conditional decisions for scaffolder + * tasks that are returned by authorization policies. + * + * @alpha + */ +export const scaffolderTaskConditions = taskConditionExports.conditions; diff --git a/plugins/scaffolder-backend/src/service/permissions.ts b/plugins/scaffolder-backend/src/service/permissions.ts index 9b12e115b9..b73cce7a74 100644 --- a/plugins/scaffolder-backend/src/service/permissions.ts +++ b/plugins/scaffolder-backend/src/service/permissions.ts @@ -21,9 +21,20 @@ import { } from '@backstage/plugin-scaffolder-common'; import { RESOURCE_TYPE_SCAFFOLDER_ACTION, + RESOURCE_TYPE_SCAFFOLDER_TASK, RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, } from '@backstage/plugin-scaffolder-common/alpha'; import { PermissionRuleParams } from '@backstage/plugin-permission-common'; +import { SerializedTask } from '@backstage/plugin-scaffolder-node'; + +/** + * + * @public + */ +export type ScaffolderPermissionRuleInput = + | TemplatePermissionRuleInput + | ActionPermissionRuleInput + | TaskPermissionRuleInput; /** * @public @@ -37,7 +48,7 @@ export type TemplatePermissionRuleInput< TParams >; export function isTemplatePermissionRuleInput( - permissionRule: TemplatePermissionRuleInput | ActionPermissionRuleInput, + permissionRule: ScaffolderPermissionRuleInput, ): permissionRule is TemplatePermissionRuleInput { return permissionRule.resourceType === RESOURCE_TYPE_SCAFFOLDER_TEMPLATE; } @@ -55,7 +66,27 @@ export type ActionPermissionRuleInput< TParams >; export function isActionPermissionRuleInput( - permissionRule: TemplatePermissionRuleInput | ActionPermissionRuleInput, + permissionRule: ScaffolderPermissionRuleInput, ): permissionRule is ActionPermissionRuleInput { return permissionRule.resourceType === RESOURCE_TYPE_SCAFFOLDER_ACTION; } + +/** + * @public + */ +export type TaskPermissionRuleInput< + TParams extends PermissionRuleParams = PermissionRuleParams, +> = PermissionRule< + SerializedTask, + { + key: string; + values?: string[]; + }, + typeof RESOURCE_TYPE_SCAFFOLDER_TASK, + TParams +>; +export function isTaskPermissionRuleInput( + permissionRule: ScaffolderPermissionRuleInput, +): permissionRule is TaskPermissionRuleInput { + return permissionRule.resourceType === RESOURCE_TYPE_SCAFFOLDER_TASK; +} diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index cdda1dc6cc..bebfadb9b1 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -14,10 +14,8 @@ * limitations under the License. */ -import { DatabaseManager } from '@backstage/backend-common'; +import { DatabaseManager } from '@backstage/backend-defaults/database'; import { ConfigReader } from '@backstage/config'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import express from 'express'; import request from 'supertest'; import ObservableImpl from 'zen-observable'; @@ -26,10 +24,6 @@ import ObservableImpl from 'zen-observable'; * Due to a circular dependency between this plugin and the * plugin-scaffolder-backend-module-cookiecutter plugin, it results in an error: * TypeError: _pluginscaffolderbackend.createTemplateAction is not a function - * - * TODO: These tests need refactoring. Seems like the identityApi tests don't do anything different anymore. - * And there's very little value re-reunning all the tests again with just additional template filters and values. - * Let's break them out into better tests. Didn't want to do it in the same PR i'm working on right now. */ import { parseEntityRef, @@ -41,12 +35,12 @@ import { TaskBroker, TemplateFilter, TemplateGlobal, + SerializedTaskEvent, } from '@backstage/plugin-scaffolder-node'; +import { TaskSpec } from '@backstage/plugin-scaffolder-common'; +import { JsonValue } from '@backstage/types'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; -import { - AuthorizeResult, - PermissionEvaluator, -} from '@backstage/plugin-permission-common'; +import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { mockCredentials, mockErrorHandler, @@ -54,14 +48,15 @@ import { } from '@backstage/backend-test-utils'; import { AutocompleteHandler, + CreatedTemplateFilter, + CreatedTemplateGlobal, createTemplateFilter, createTemplateGlobalFunction, createTemplateGlobalValue, } from '@backstage/plugin-scaffolder-node/alpha'; import { catalogServiceMock } from '@backstage/plugin-catalog-node/testUtils'; -import { EventsService } from '@backstage/plugin-events-node'; import { DatabaseService } from '@backstage/backend-plugin-api'; -import { loggerToWinstonLogger } from '../util/loggerToWinstonLogger'; + import { ScmIntegrations } from '@backstage/integration'; import { extractFilterMetadata, @@ -72,21 +67,6 @@ import { createDefaultFilters } from '../lib/templating/filters/createDefaultFil import { createRouter } from './router'; import { DatabaseTaskStore } from '../scaffolder/tasks/DatabaseTaskStore'; -const mockAccess = jest.fn(); - -jest.mock('fs-extra', () => ({ - access: (...args: any[]) => mockAccess(...args), - promises: { - access: (...args: any[]) => mockAccess(...args), - }, - constants: { - F_OK: 0, - W_OK: 1, - }, - mkdir: jest.fn(), - remove: jest.fn(), -})); - function createDatabase(): DatabaseService { return DatabaseManager.fromConfig( new ConfigReader({ @@ -97,1639 +77,1389 @@ function createDatabase(): DatabaseService { }, }, }), - ).forPlugin('scaffolder'); + ).forPlugin('scaffolder', { + logger: mockServices.logger.mock(), + lifecycle: mockServices.lifecycle.mock(), + }); } const config = new ConfigReader({}); -describe.each([ - { - desc: 'no template filters/globals', +// todo: this needs to return a new object every time as there seems to +// be some mutation in the tests. +const generateMockTemplate = () => ({ + apiVersion: 'scaffolder.backstage.io/v1beta3', + kind: 'Template', + metadata: { + description: 'Create a new CRA website project', + name: 'create-react-app-template', + tags: ['experimental', 'react', 'cra'], + title: 'Create React App Template', + annotations: { + 'backstage.io/managed-by-location': 'url:https://dev.azure.com', + }, }, - { - desc: 'legacy template filters', - additionalTemplateFilters: { - foo: (s: any) => s, - bar: (bar: any) => !!bar, - baz: (what: string, ever: string) => what + ever, - } as Record<string, TemplateFilter>, - }, - { - desc: 'created template filters', - additionalTemplateFilters: [ - createTemplateFilter({ - id: 'foo', - schema: z => - z - .function() - .args(z.any().describe('a value')) - .returns(z.any().describe('same value')), - filter: s => s, - }), - createTemplateFilter({ - id: 'bar', - filter: bar => !!bar, - }), - createTemplateFilter({ - id: 'baz', - description: 'append the argument to the incoming value', - schema: z => - z - .function() - .args(z.string(), z.string().describe('value to append to input')) - .returns(z.string().describe('input+suffix')), - filter: (what, ever) => what + ever, - }), - createTemplateFilter({ - id: 'blah', - schema: z => - z - .function() - .args( - z.number(), - z.number().describe('factor by which to multiply input'), - z.number().describe('addend by which to increase input * factor'), - ), - filter: (base, factor, addend) => base * factor + addend, - }), - ], - }, - { - desc: 'legacy template globals', - additionalTemplateGlobals: { - nul: null, - nop: x => x, - } as Record<string, TemplateGlobal>, - }, - { - desc: 'created template globals', - additionalTemplateGlobals: [ - createTemplateGlobalValue({ - id: 'nul', - description: 'null value', - value: null, - }), - createTemplateGlobalFunction({ - id: 'nop', - description: 'nop function', - schema: z => - z - .function() - .args(z.any().describe('input')) - .returns(z.any().describe('output')), - fn: x => x, - }), - ], - }, -])( - 'createRouter, $desc', - ({ additionalTemplateFilters, additionalTemplateGlobals }) => { - let app: express.Express; - let loggerSpy: jest.SpyInstance; - let taskBroker: TaskBroker; - const catalogMock = catalogServiceMock.mock(); - const permissionApi = { - authorize: jest.fn(), - authorizeConditional: jest.fn(), - } as unknown as PermissionEvaluator; - const auth = mockServices.auth(); - const httpAuth = mockServices.httpAuth(); - const events = { - publish: jest.fn(), - } as unknown as EventsService; - - const credentials = mockCredentials.user(); - - const getMockTemplate = (): TemplateEntityV1beta3 => ({ - apiVersion: 'scaffolder.backstage.io/v1beta3', - kind: 'Template', - metadata: { - description: 'Create a new CRA website project', - name: 'create-react-app-template', - tags: ['experimental', 'react', 'cra'], - title: 'Create React App Template', - annotations: { - 'backstage.io/managed-by-location': 'url:https://dev.azure.com', + spec: { + owner: 'web@example.com', + type: 'website', + steps: [ + { + id: 'step-one', + name: 'First log', + action: 'debug:log', + input: { + message: 'hello', }, }, - spec: { - owner: 'web@example.com', - type: 'website', + { + id: 'step-two', + name: 'Second log', + action: 'debug:log', + input: { + message: 'world', + }, + 'backstage:permissions': { + tags: ['steps-tag'], + }, + }, + ], + parameters: [ + { + type: 'object', + required: ['requiredParameter1'], + properties: { + requiredParameter1: { + type: 'string', + description: 'Required parameter 1', + }, + }, + }, + { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', + }, + }, + }, + ], + }, +}); + +const mockUser: UserEntity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'User', + metadata: { + name: 'guest', + annotations: { + 'google.com/email': 'bobby@tables.com', + }, + }, + spec: { + profile: { + displayName: 'Robert Tables of the North', + }, + }, +}; + +const createTestRouter = async ( + overrides: { + additionalTemplateFilters?: + | Record<string, TemplateFilter> + | CreatedTemplateFilter<any, any>[]; + additionalTemplateGlobals?: + | Record<string, TemplateGlobal> + | CreatedTemplateGlobal[]; + autocompleteHandlers?: Record<string, AutocompleteHandler>; + } = {}, +) => { + const logger = mockServices.logger.mock({ + child: () => logger, + }); + + const databaseTaskStore = await DatabaseTaskStore.create({ + database: createDatabase(), + }); + + const taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); + + jest.spyOn(taskBroker, 'dispatch'); + jest.spyOn(taskBroker, 'claim'); + jest.spyOn(taskBroker, 'cancel'); + jest.spyOn(taskBroker, 'retry'); + jest.spyOn(taskBroker, 'list'); + jest.spyOn(taskBroker, 'get'); + jest.spyOn(taskBroker, 'vacuumTasks'); + jest.spyOn(taskBroker, 'event$'); + + const catalog = catalogServiceMock.mock(); + const permissions = mockServices.permissions(); + const auth = mockServices.auth(); + const httpAuth = mockServices.httpAuth(); + const events = mockServices.events(); + + catalog.getEntityByRef.mockImplementation(async ref => { + const { kind } = parseEntityRef(ref); + + if (kind.toLocaleLowerCase() === 'template') { + return generateMockTemplate(); + } + + if (kind.toLocaleLowerCase() === 'user') { + return mockUser; + } + + throw new Error(`no mock found for kind: ${kind}`); + }); + + const router = await createRouter({ + logger, + config: new ConfigReader({}), + database: createDatabase(), + catalog, + taskBroker, + permissions, + auth, + httpAuth, + events, + additionalTemplateFilters: overrides.additionalTemplateFilters, + additionalTemplateGlobals: overrides.additionalTemplateGlobals, + autocompleteHandlers: overrides.autocompleteHandlers, + actions: [ + createTemplateAction({ + id: 'test', + description: 'test', + schema: { + input: z => + z.object({ + test: z.string(), + }), + }, + handler: async () => {}, + }), + ], + }); + + router.use(mockErrorHandler()); + return { router, logger, taskBroker, permissions, catalog }; +}; + +describe('scaffolder router', () => { + const credentials = mockCredentials.user(); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('GET /v2/actions', () => { + it('lists available actions', async () => { + const { router } = await createTestRouter(); + const response = await request(router).get('/v2/actions').send(); + expect(response.status).toEqual(200); + expect(response.body[0].id).toBeDefined(); + expect(response.body.length).toBe(1); + }); + }); + + describe('GET /v2/templating-extensions', () => { + it('lists template filters and globals', async () => { + const { router } = await createTestRouter(); + const response = await request(router) + .get('/v2/templating-extensions') + .send(); + expect(response.status).toEqual(200); + const integrations = ScmIntegrations.fromConfig(config); + + expect(response.body).toMatchObject({ + filters: { + ...extractFilterMetadata(createDefaultFilters({ integrations })), + }, + globals: { + functions: {}, + values: {}, + }, + }); + }); + + it('should include legacy template filters in templating extensions', async () => { + const { router } = await createTestRouter({ + additionalTemplateFilters: { + foo: (s: JsonValue) => s, + bar: (bar: JsonValue) => !!bar, + baz: (what: JsonValue, ever: JsonValue) => + String(what) + String(ever), + } as Record<string, TemplateFilter>, + }); + + const response = await request(router) + .get('/v2/templating-extensions') + .send(); + expect(response.status).toEqual(200); + + const integrations = ScmIntegrations.fromConfig(config); + + expect(response.body).toMatchObject({ + filters: { + ...extractFilterMetadata(createDefaultFilters({ integrations })), + ...extractFilterMetadata({ + foo: (s: JsonValue) => s, + bar: (bar: JsonValue) => !!bar, + baz: (what: JsonValue, ever: JsonValue) => + String(what) + String(ever), + }), + }, + globals: { + functions: {}, + values: {}, + }, + }); + }); + + it('should include created template filters in templating extensions', async () => { + const { router } = await createTestRouter({ + additionalTemplateFilters: [ + createTemplateFilter({ + id: 'foo', + schema: z => + z + .function() + .args(z.any().describe('a value')) + .returns(z.any().describe('same value')), + filter: s => s, + }), + createTemplateFilter({ + id: 'bar', + filter: bar => !!bar, + }), + createTemplateFilter({ + id: 'baz', + description: 'append the argument to the incoming value', + schema: z => + z + .function() + .args( + z.string(), + z.string().describe('value to append to input'), + ) + .returns(z.string().describe('input+suffix')), + filter: (what, ever) => what + ever, + }), + createTemplateFilter({ + id: 'blah', + schema: z => + z + .function() + .args( + z.number(), + z.number().describe('factor by which to multiply input'), + z + .number() + .describe('addend by which to increase input * factor'), + ), + filter: (base, factor, addend) => base * factor + addend, + }), + ], + }); + + const response = await request(router) + .get('/v2/templating-extensions') + .send(); + + expect(response.status).toEqual(200); + + const integrations = ScmIntegrations.fromConfig(config); + + expect(response.body.filters).toMatchObject({ + ...extractFilterMetadata(createDefaultFilters({ integrations })), + foo: expect.any(Object), + bar: expect.any(Object), + baz: expect.any(Object), + blah: expect.any(Object), + }); + }); + + it('should include legacy template globals in templating extensions', async () => { + const { router } = await createTestRouter({ + additionalTemplateGlobals: { + nul: null, + nop: (x: JsonValue) => x, + } as Record<string, TemplateGlobal>, + }); + + const response = await request(router) + .get('/v2/templating-extensions') + .send(); + + expect(response.status).toEqual(200); + + expect(response.body.globals).toMatchObject({ + functions: extractGlobalFunctionMetadata({ + nul: null, + nop: (x: JsonValue) => x, + }), + values: extractGlobalValueMetadata({ + nul: null, + nop: (x: JsonValue) => x, + }), + }); + }); + + it('should include created template globals in templating extensions', async () => { + const { router } = await createTestRouter({ + additionalTemplateGlobals: [ + createTemplateGlobalValue({ + id: 'nul', + description: 'null value', + value: null, + }), + createTemplateGlobalFunction({ + id: 'nop', + description: 'nop function', + schema: z => + z + .function() + .args(z.any().describe('input')) + .returns(z.any().describe('output')), + fn: (x: JsonValue) => x, + }), + ], + }); + const response = await request(router) + .get('/v2/templating-extensions') + .send(); + expect(response.status).toEqual(200); + + expect(response.body.globals).toMatchObject({ + functions: expect.objectContaining({ + nop: expect.any(Object), + }), + values: expect.objectContaining({ + nul: expect.any(Object), + }), + }); + }); + }); + + describe('GET /v2/templates/:namespace/:kind/:name/parameter-schema', () => { + it('returns the parameter schema', async () => { + const { router, permissions } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + result: AuthorizeResult.ALLOW, + }, + ]); + const response = await request(router) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); + + expect(response.status).toEqual(200); + + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', steps: [ { - id: 'step-one', - name: 'First log', - action: 'debug:log', - input: { - message: 'hello', - }, - }, - { - id: 'step-two', - name: 'Second log', - action: 'debug:log', - input: { - message: 'world', - }, - 'backstage:permissions': { - tags: ['steps-tag'], - }, - }, - ], - parameters: [ - { - type: 'object', - required: ['requiredParameter1'], - properties: { - requiredParameter1: { - type: 'string', - description: 'Required parameter 1', + title: 'Please enter the following information', + schema: { + required: ['requiredParameter1'], + type: 'object', + properties: { + requiredParameter1: { + description: 'Required parameter 1', + type: 'string', + }, }, }, }, { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', + title: 'Please enter the following information', + schema: { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', + }, }, }, }, ], - }, + }); }); - const mockUser: UserEntity = { - apiVersion: 'backstage.io/v1alpha1', - kind: 'User', - metadata: { - name: 'guest', - annotations: { - 'google.com/email': 'bobby@tables.com', - }, - }, - spec: { - profile: { - displayName: 'Robert Tables of the North', - }, - }, - }; - - describe('not providing an identity api', () => { - beforeEach(async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - const databaseTaskStore = await DatabaseTaskStore.create({ - database: createDatabase(), - }); - taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); - - jest.spyOn(taskBroker, 'dispatch'); - jest.spyOn(taskBroker, 'get'); - jest.spyOn(taskBroker, 'list'); - jest.spyOn(taskBroker, 'event$'); - loggerSpy = jest.spyOn(logger, 'info'); - - const router = await createRouter({ - logger: logger, - config: new ConfigReader({}), - database: createDatabase(), - catalog: catalogMock, - taskBroker, - permissions: permissionApi, - auth, - httpAuth, - events, - additionalTemplateFilters, - additionalTemplateGlobals, - actions: [ - createTemplateAction({ - id: 'test', - description: 'test', - schema: { - input: z => - z.object({ - test: z.string(), - }), - }, - handler: async () => {}, - }), - ], - }); - app = express().use(router); - - catalogMock.getEntityByRef.mockImplementation(async ref => { - const { kind } = parseEntityRef(ref); - - if (kind.toLocaleLowerCase() === 'template') { - return getMockTemplate(); - } - - if (kind.toLocaleLowerCase() === 'user') { - return mockUser; - } - - throw new Error(`no mock found for kind: ${kind}`); - }); - - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ + it('filters parameters that the user is not authorized to see', async () => { + const { router, permissions } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + result: AuthorizeResult.DENY, + }, { result: AuthorizeResult.ALLOW, }, ]); - }); - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('GET /v2/actions', () => { - it('lists available actions', async () => { - const response = await request(app).get('/v2/actions').send(); - expect(response.status).toEqual(200); - expect(response.body[0].id).toBeDefined(); - expect(response.body.length).toBe(1); - }); - }); - - describe('GET /v2/templating-extensions', () => { - it('lists template filters and globals', async () => { - const response = await request(app) - .get('/v2/templating-extensions') - .send(); - expect(response.status).toEqual(200); - const integrations = ScmIntegrations.fromConfig(config); - - expect(response.body).toMatchObject({ - filters: { - ...extractFilterMetadata(createDefaultFilters({ integrations })), - ...extractFilterMetadata(additionalTemplateFilters), - }, - globals: { - functions: extractGlobalFunctionMetadata( - additionalTemplateGlobals, - ), - values: extractGlobalValueMetadata(additionalTemplateGlobals), - }, - }); - }); - }); - - describe('POST /v2/tasks', () => { - it('rejects template values which do not match the template schema definition', async () => { - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - storePath: 'https://github.com/backstage/backstage', - }, - }); - - expect(response.status).toEqual(400); - }); - - it('return the template id', async () => { - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - broker.mockResolvedValue({ - taskId: 'a-random-id', - }); - - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(response.status).toEqual(201); - expect(response.body.id).toBe('a-random-id'); - }); - - it('should call the broker with a correct spec', async () => { - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - const mockToken = mockCredentials.user.token(); - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - __initiatorCredentials: JSON.stringify({ - ...credentials, - token: mockToken, - }), - backstageToken: mockToken, - }, - spec: { - apiVersion: mockTemplate.apiVersion, - steps: mockTemplate.spec.steps.map((step, index) => ({ - ...step, - id: step.id ?? `step-${index + 1}`, - name: step.name ?? step.action, - })), - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('should emit auditlog containing user identifier when backstage auth is passed', async () => { - const mockToken = mockCredentials.user.token(); - - await request(app) - .post('/v2/tasks') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(loggerSpy).toHaveBeenCalledTimes(1); - expect(loggerSpy).toHaveBeenCalledWith( - 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', - ); - }); - }); - - describe('GET /v2/tasks', () => { - it('return all tasks', async () => { - ( - taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get(`/v2/tasks`); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: {}, - pagination: {}, - }); - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - }); - - it('return filtered tasks', async () => { - ( - taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get( - `/v2/tasks?createdBy=user:default/foo&createdBy=user:default/bar&status=completed&status=open&limit=1&offset=0&order=desc:created_at`, - ); - - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: { - createdBy: ['user:default/foo', 'user:default/bar'], - status: ['completed', 'open'], - }, - pagination: { - limit: 1, - offset: 0, - }, - order: [{ order: 'desc', field: 'created_at' }], - }); - }); - }); - - describe('GET /v2/tasks/:taskId', () => { - it('does not divulge secrets', async () => { - (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - secrets: { - __initiatorCredentials: JSON.stringify(credentials), - }, - createdBy: '', - }); - - const response = await request(app).get(`/v2/tasks/a-random-id`); - expect(response.status).toEqual(200); - expect(response.body.status).toBe('completed'); - expect(response.body.secrets).toBeUndefined(); - }); - }); - - describe('GET /v2/tasks/:taskId/eventstream', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ], - }); - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - // emit after this function returned - }); - - let statusCode: any = undefined; - let headers: any = {}; - const responseDataFn = jest.fn(); - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', chunk => { - responseDataFn(chunk.toString()); - - // the server expects the client to abort the request - if (chunk.includes('completion')) { - req.abort(); - } - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - expect(responseDataFn).toHaveBeenCalledTimes(2); - expect(responseDataFn).toHaveBeenCalledWith(`event: log -data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} - -`); - expect(responseDataFn).toHaveBeenCalledWith(`event: completion -data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} - -`); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - }); - - let statusCode: any = undefined; - let headers: any = {}; - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .query({ after: 10 }) - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', () => { - // close immediately - req.abort(); - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/tasks/:taskId/events', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - - const response = await request(app).get( - '/v2/tasks/a-random-id/events', - ); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: 0, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); - }); - - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(() => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ events: [] }); - }); - }); - - const response = await request(app) - .get('/v2/tasks/a-random-id/events') - .query({ after: 10 }); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('POST /v2/dry-run', () => { - it('should get user entity', async () => { - const mockToken = mockCredentials.user.token(); - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/dry-run') - .set('Authorization', `Bearer ${mockToken}`) - .send({ - template: mockTemplate, - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - directoryContents: [], - }); - - expect(catalogMock.getEntityByRef).toHaveBeenCalledTimes(1); - - expect(catalogMock.getEntityByRef).toHaveBeenCalledWith( - 'user:default/mock', - expect.anything(), - ); - }); + const response = await request(router) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', + steps: [], }); }); - describe('providing an identity api', () => { - beforeEach(async () => { - const logger = loggerToWinstonLogger(mockServices.logger.mock()); - const databaseTaskStore = await DatabaseTaskStore.create({ - database: createDatabase(), - }); - taskBroker = new StorageTaskBroker(databaseTaskStore, logger, config); - - jest.spyOn(taskBroker, 'dispatch'); - jest.spyOn(taskBroker, 'get'); - jest.spyOn(taskBroker, 'list'); - jest.spyOn(taskBroker, 'event$'); - loggerSpy = jest.spyOn(logger, 'info'); - - const router = await createRouter({ - logger: logger, - config: new ConfigReader({}), - database: createDatabase(), - catalog: catalogMock, - taskBroker, - permissions: permissionApi, - auth, - httpAuth, - actions: [ - createTemplateAction({ - id: 'test', - description: 'test', - schema: { - input: z => - z.object({ - test: z.string(), - }), - }, - handler: async () => {}, - }), - ], - }); - app = express().use(router); - - catalogMock.getEntityByRef.mockImplementation(async ref => { - const { kind } = parseEntityRef(ref); - - if (kind.toLocaleLowerCase() === 'template') { - return getMockTemplate(); - } - - if (kind.toLocaleLowerCase() === 'user') { - return mockUser; - } - throw new Error(`no mock found for kind: ${kind}`); - }); - - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, + it('filters parameters that the user is not authorized to see in case of conditional decision', async () => { + const { permissions, router } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementation(async () => [ + { + conditions: { + resourceType: 'scaffolder-template', + rule: 'HAS_TAG', + params: { tag: 'parameters-tag' }, }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - jest.spyOn(permissionApi, 'authorize').mockImplementation(async () => [ + pluginId: 'scaffolder', + resourceType: 'scaffolder-template', + result: AuthorizeResult.CONDITIONAL, + }, { result: AuthorizeResult.ALLOW, }, ]); - }); - afterEach(() => { - jest.resetAllMocks(); - }); + const response = await request(router) + .get( + '/v2/templates/default/Template/create-react-app-template/parameter-schema', + ) + .send(); - describe('GET /v2/actions', () => { - it('lists available actions', async () => { - const response = await request(app).get('/v2/actions').send(); - expect(response.status).toEqual(200); - expect(response.body[0].id).toBeDefined(); - expect(response.body.length).toBe(1); - }); - }); - - describe('GET /v2/templates/:namespace/:kind/:name/parameter-schema', () => { - it('returns the parameter schema', async () => { - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [ - { - title: 'Please enter the following information', - schema: { - required: ['requiredParameter1'], - type: 'object', - properties: { - requiredParameter1: { - description: 'Required parameter 1', - type: 'string', - }, - }, + expect(response.status).toEqual(200); + expect(response.body).toEqual({ + title: 'Create React App Template', + description: 'Create a new CRA website project', + steps: [ + { + title: 'Please enter the following information', + schema: { + type: 'object', + required: ['requiredParameter2'], + 'backstage:permissions': { + tags: ['parameters-tag'], + }, + properties: { + requiredParameter2: { + type: 'string', + description: 'Required parameter 2', }, }, - { - title: 'Please enter the following information', - schema: { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', - }, - }, - }, - }, - ], - }); - }); - - it('filters parameters that the user is not authorized to see', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementationOnce(async () => [ - { - result: AuthorizeResult.DENY, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [], - }); - }); - - it('filters parameters that the user is not authorized to see in case of conditional decision', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementationOnce(async () => [ - { - conditions: { - resourceType: 'scaffolder-template', - rule: 'HAS_TAG', - params: { tag: 'parameters-tag' }, - }, - pluginId: 'scaffolder', - resourceType: 'scaffolder-template', - result: AuthorizeResult.CONDITIONAL, - }, - { - result: AuthorizeResult.ALLOW, - }, - ]); - const response = await request(app) - .get( - '/v2/templates/default/Template/create-react-app-template/parameter-schema', - ) - .send(); - expect(response.status).toEqual(200); - expect(response.body).toEqual({ - title: 'Create React App Template', - description: 'Create a new CRA website project', - steps: [ - { - title: 'Please enter the following information', - schema: { - type: 'object', - required: ['requiredParameter2'], - 'backstage:permissions': { - tags: ['parameters-tag'], - }, - properties: { - requiredParameter2: { - type: 'string', - description: 'Required parameter 2', - }, - }, - }, - }, - ], - }); - }); - }); - - describe('POST /v2/tasks', () => { - it('rejects template values which do not match the template schema definition', async () => { - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - storePath: 'https://github.com/backstage/backstage', - }, - }); - - expect(response.status).toEqual(400); - }); - - it('filters steps that the user is not authorized to see', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - result: AuthorizeResult.DENY, - }, - ]); - - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - __initiatorCredentials: JSON.stringify({ - ...credentials, - token: mockCredentials.user.token(), - }), - backstageToken: mockCredentials.user.token(), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: [], - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('filters steps that the user is not authorized to see in case of conditional decision', async () => { - jest - .spyOn(permissionApi, 'authorizeConditional') - .mockImplementation(async () => [ - { - result: AuthorizeResult.ALLOW, - }, - { - conditions: { - resourceType: 'scaffolder-template', - rule: 'HAS_TAG', - params: { tag: 'steps-tag' }, - }, - pluginId: 'scaffolder', - resourceType: 'scaffolder-template', - result: AuthorizeResult.CONDITIONAL, - }, - ]); - - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - const mockTemplate = getMockTemplate(); - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - __initiatorCredentials: JSON.stringify({ - ...credentials, - token: mockCredentials.user.token(), - }), - backstageToken: mockCredentials.user.token(), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: [ - { - id: 'step-two', - name: 'Second log', - action: 'debug:log', - input: { - message: 'world', - }, - 'backstage:permissions': { - tags: ['steps-tag'], - }, - }, - ], - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('return the template id', async () => { - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - broker.mockResolvedValue({ - taskId: 'a-random-id', - }); - - const response = await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(response.body.id).toBe('a-random-id'); - expect(response.status).toEqual(201); - }); - - it('should call the broker with a correct spec', async () => { - const broker = - taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; - const mockTemplate = getMockTemplate(); - - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - expect(broker).toHaveBeenCalledWith( - expect.objectContaining({ - createdBy: 'user:default/mock', - secrets: { - __initiatorCredentials: JSON.stringify({ - ...credentials, - token: mockCredentials.user.token(), - }), - backstageToken: mockCredentials.user.token(), - }, - - spec: { - apiVersion: mockTemplate.apiVersion, - steps: mockTemplate.spec.steps.map((step, index) => ({ - ...step, - id: step.id ?? `step-${index + 1}`, - name: step.name ?? step.action, - })), - output: mockTemplate.spec.output ?? {}, - parameters: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - user: { - entity: mockUser, - ref: 'user:default/mock', - }, - templateInfo: { - entityRef: stringifyEntityRef({ - kind: 'Template', - namespace: 'Default', - name: mockTemplate.metadata?.name, - }), - baseUrl: 'https://dev.azure.com', - entity: { - metadata: mockTemplate.metadata, - }, - }, - }, - }), - ); - }); - - it('should emit auditlog containing user identifier when backstage auth is passed', async () => { - await request(app) - .post('/v2/tasks') - .send({ - templateRef: stringifyEntityRef({ - kind: 'template', - name: 'create-react-app-template', - }), - values: { - requiredParameter1: 'required-value-1', - requiredParameter2: 'required-value-2', - }, - }); - - expect(loggerSpy).toHaveBeenCalledTimes(1); - expect(loggerSpy).toHaveBeenCalledWith( - 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', - ); - }); - }); - - describe('GET /v2/tasks', () => { - it('return all tasks', async () => { - ( - taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get(`/v2/tasks`); - expect(taskBroker.list).toHaveBeenCalledWith({ - pagination: {}, - filters: {}, - }); - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: '', - }, - ], - totalTasks: 1, - }); - }); - - it('return filtered tasks', async () => { - ( - taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] - ).mockResolvedValue({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); - - const response = await request(app).get( - `/v2/tasks?createdBy=user:default/foo`, - ); - expect(taskBroker.list).toHaveBeenCalledWith({ - filters: { - createdBy: ['user:default/foo'], }, - pagination: {}, - }); + }, + ], + }); + }); + }); - expect(response.status).toEqual(200); - expect(response.body).toStrictEqual({ - tasks: [ - { - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - createdBy: 'user:default/foo', - }, - ], - totalTasks: 1, - }); + describe('POST /v2/tasks', () => { + it('rejects template values which do not match the template schema definition', async () => { + const { router } = await createTestRouter(); + const response = await request(router) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + storePath: 'https://github.com/backstage/backstage', + }, }); + + expect(response.status).toEqual(400); + }); + + it('return the template id', async () => { + const { router, taskBroker } = await createTestRouter(); + const broker = taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; + + broker.mockResolvedValue({ + taskId: 'a-random-id', }); - describe('GET /v2/tasks/:taskId', () => { - it('does not divulge secrets', async () => { - (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ - id: 'a-random-id', - spec: {} as any, - status: 'completed', - createdAt: '', - secrets: { - __initiatorCredentials: JSON.stringify(credentials), - }, - createdBy: '', - }); - - const response = await request(app).get(`/v2/tasks/a-random-id`); - expect(response.status).toEqual(200); - expect(response.body.status).toBe('completed'); - expect(response.body.secrets).toBeUndefined(); - }); - }); - - describe('GET /v2/tasks/:taskId/eventstream', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - ], - }); - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - // emit after this function returned - }); - - let statusCode: any = undefined; - let headers: any = {}; - const responseDataFn = jest.fn(); - - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', chunk => { - responseDataFn(chunk.toString()); - - // the server expects the client to abort the request - if (chunk.includes('completion')) { - req.abort(); - } - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - expect(responseDataFn).toHaveBeenCalledTimes(2); - expect(responseDataFn).toHaveBeenCalledWith(`event: log -data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} - -`); - expect(responseDataFn).toHaveBeenCalledWith(`event: completion -data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} - -`); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); + const response = await request(router) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, }); - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - setImmediate(() => { - observer.next({ - events: [ - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - }); + expect(response.status).toEqual(201); + expect(response.body.id).toBe('a-random-id'); + }); - let statusCode: any = undefined; - let headers: any = {}; + it('should call the broker with a correct spec', async () => { + const { router, taskBroker } = await createTestRouter(); + const broker = taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; + const mockToken = mockCredentials.user.token(); + const mockTemplate = generateMockTemplate(); - const req = request(app) - .get('/v2/tasks/a-random-id/eventstream') - .query({ after: 10 }) - .set('accept', 'text/event-stream') - .parse((res, _) => { - ({ statusCode, headers } = res as any); - - res.on('data', () => { - // close immediately - req.abort(); - }); - }); - - // wait for the request to finish - await req.catch(() => { - // ignore 'aborted' error - }); - - expect(statusCode).toBe(200); - expect(headers['content-type']).toBe('text/event-stream'); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/tasks/:taskId/events', () => { - it('should return log messages', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(({ taskId }) => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ - events: [ - { - id: 0, - taskId, - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId, - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ], - }); - }); - }); - - const response = await request(app).get( - '/v2/tasks/a-random-id/events', - ); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: 0, - taskId: 'a-random-id', - type: 'log', - createdAt: '', - body: { message: 'My log message' }, - }, - { - id: 1, - taskId: 'a-random-id', - type: 'completion', - createdAt: '', - body: { message: 'Finished!' }, - }, - ]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - }); - expect(subscriber!.closed).toBe(true); + await request(router) + .post('/v2/tasks') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, }); - it('should return log messages with after query', async () => { - let subscriber: ZenObservable.SubscriptionObserver<any>; - ( - taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] - ).mockImplementation(() => { - return new ObservableImpl(observer => { - subscriber = observer; - observer.next({ events: [] }); - }); - }); - - const response = await request(app) - .get('/v2/tasks/a-random-id/events') - .query({ after: 10 }); - - expect(response.status).toEqual(200); - expect(response.body).toEqual([]); - - expect(taskBroker.event$).toHaveBeenCalledTimes(1); - expect(taskBroker.event$).toHaveBeenCalledWith({ - taskId: 'a-random-id', - after: 10, - }); - expect(subscriber!.closed).toBe(true); - }); - }); - - describe('GET /v2/autocomplete/:provider/:resource', () => { - let handleAutocompleteRequest: AutocompleteHandler; - - beforeEach(async () => { - handleAutocompleteRequest = jest.fn().mockResolvedValue({ - results: [{ title: 'blob' }], - }); - - const router = await createRouter({ - logger: loggerToWinstonLogger(mockServices.logger.mock()), - config: new ConfigReader({}), - database: createDatabase(), - catalog: catalogMock, - taskBroker, - permissions: permissionApi, - auth, - httpAuth, - autocompleteHandlers: { - 'test-provider': handleAutocompleteRequest, - }, - }); - - app = express().use(router).use(mockErrorHandler()); - }); - - it('should throw an error when the provider is not registered', async () => { - const response = await request(app) - .post('/v2/autocomplete/unknown-provider/resource') - .send({ - token: 'token', - context: {}, - }); - - expect(response.status).toEqual(400); - expect(response.body).toEqual( - expect.objectContaining({ - error: { - message: 'Unsupported provider: unknown-provider', - name: 'InputError', - }, - }), - ); - }); - - it('should call the autocomplete handler', async () => { - const context = { mock: 'context' }; - const mockToken = 'mocktoken'; - - const response = await request(app) - .post('/v2/autocomplete/test-provider/resource') - .send({ + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + __initiatorCredentials: JSON.stringify({ + ...credentials, token: mockToken, - context, + }), + backstageToken: mockToken, + }, + spec: { + apiVersion: mockTemplate.apiVersion, + steps: mockTemplate.spec.steps.map((step, index) => ({ + ...step, + id: step.id ?? `step-${index + 1}`, + name: step.name ?? step.action, + })), + output: {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('should emit auditlog containing user identifier when backstage auth is passed', async () => { + const { logger, router } = await createTestRouter(); + const mockToken = mockCredentials.user.token(); + + const { status, body } = await request(router) + .post('/v2/tasks') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + + console.log(status, body); + expect(logger.info).toHaveBeenCalledTimes(1); + expect(logger.info).toHaveBeenCalledWith( + 'Scaffolding task for template:default/create-react-app-template created by user:default/mock', + ); + }); + + it('filters steps that the user is not authorized to see', async () => { + const { router, permissions, taskBroker } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + result: AuthorizeResult.DENY, + }, + ]); + + const broker = taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; + const mockTemplate = generateMockTemplate(); + + await request(router) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + __initiatorCredentials: JSON.stringify({ + ...credentials, + token: mockCredentials.user.token(), + }), + backstageToken: mockCredentials.user.token(), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: [], + output: {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + + it('filters steps that the user is not authorized to see in case of conditional decision', async () => { + const { permissions, router, taskBroker } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementation(async () => [ + { + result: AuthorizeResult.ALLOW, + }, + { + conditions: { + resourceType: 'scaffolder-template', + rule: 'HAS_TAG', + params: { tag: 'steps-tag' }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-template', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + + const broker = taskBroker.dispatch as jest.Mocked<TaskBroker>['dispatch']; + const mockTemplate = generateMockTemplate(); + await request(router) + .post('/v2/tasks') + .send({ + templateRef: stringifyEntityRef({ + kind: 'template', + name: 'create-react-app-template', + }), + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + }); + expect(broker).toHaveBeenCalledWith( + expect.objectContaining({ + createdBy: 'user:default/mock', + secrets: { + __initiatorCredentials: JSON.stringify({ + ...credentials, + token: mockCredentials.user.token(), + }), + backstageToken: mockCredentials.user.token(), + }, + + spec: { + apiVersion: mockTemplate.apiVersion, + steps: [ + { + id: 'step-two', + name: 'Second log', + action: 'debug:log', + input: { + message: 'world', + }, + 'backstage:permissions': { + tags: ['steps-tag'], + }, + }, + ], + output: {}, + parameters: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + user: { + entity: mockUser, + ref: 'user:default/mock', + }, + templateInfo: { + entityRef: stringifyEntityRef({ + kind: 'Template', + namespace: 'Default', + name: mockTemplate.metadata?.name, + }), + baseUrl: 'https://dev.azure.com', + entity: { + metadata: mockTemplate.metadata, + }, + }, + }, + }), + ); + }); + }); + + describe('GET /v2/tasks', () => { + it('return all tasks', async () => { + const { router, taskBroker } = await createTestRouter(); + ( + taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + + const response = await request(router).get(`/v2/tasks`); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: {}, + pagination: {}, + }); + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + createdBy: '', + }, + ], + totalTasks: 1, + }); + }); + + it('return filtered tasks', async () => { + const { router, taskBroker } = await createTestRouter(); + ( + taskBroker.list as jest.Mocked<Required<TaskBroker>>['list'] + ).mockResolvedValue({ + tasks: [ + { + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + + const response = await request(router).get( + `/v2/tasks?createdBy=user:default/foo&createdBy=user:default/bar&status=completed&status=open&limit=1&offset=0&order=desc:created_at`, + ); + + expect(response.status).toEqual(200); + expect(response.body).toStrictEqual({ + tasks: [ + { + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + createdBy: 'user:default/foo', + }, + ], + totalTasks: 1, + }); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: { + createdBy: ['user:default/foo', 'user:default/bar'], + status: ['completed', 'open'], + }, + pagination: { + limit: 1, + offset: 0, + }, + order: [{ order: 'desc', field: 'created_at' }], + }); + }); + + it('disallows users from seeing tasks they do not own', async () => { + const { router, taskBroker, permissions } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + conditions: { + resourceType: 'scaffolder-task', + rule: 'IS_TASK_OWNER', + params: { createdBy: ['user'] }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-task', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + const response = await request(router).get( + `/v2/tasks?createdBy=not-user`, + ); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: { createdBy: ['not-user'], status: undefined }, + order: undefined, + pagination: { limit: undefined, offset: undefined }, + permissionFilters: { key: 'created_by', values: ['user'] }, + }); + expect(response.status).toBe(200); + expect(response.body.totalTasks).toBe(0); + expect(response.body.tasks).toEqual([]); + }); + }); + + describe('GET /v2/tasks/:taskId', () => { + it('does not divulge secrets', async () => { + const { router, taskBroker } = await createTestRouter(); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + + const response = await request(router).get(`/v2/tasks/a-random-id`); + expect(response.status).toEqual(200); + expect(response.body.status).toBe('completed'); + expect(response.body.secrets).toBeUndefined(); + }); + it('disallows users from seeing tasks they do not own', async () => { + const { router, permissions, taskBroker } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + conditions: { + resourceType: 'scaffolder-task', + rule: 'IS_TASK_OWNER', + params: { createdBy: ['user'] }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-task', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: 'not-user', + }); + + const response = await request(router).get(`/v2/tasks/a-random-id`); + expect(taskBroker.get).toHaveBeenCalledWith('a-random-id'); + expect(response.error).not.toBeFalsy(); + }); + }); + + describe('GET /v2/tasks/:taskId/eventstream', () => { + it('should return log messages', async () => { + const { router, taskBroker } = await createTestRouter(); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + let subscriber: ZenObservable.SubscriptionObserver<{ + events: SerializedTaskEvent[]; + }>; + ( + taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + ], }); + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + // emit after this function returned + }); - expect(response.status).toEqual(200); + let statusCode: number | undefined = undefined; + let headers: Record<string, string> = {}; + const responseDataFn = jest.fn(); - expect(response.body).toEqual({ results: [{ title: 'blob' }] }); - expect(handleAutocompleteRequest).toHaveBeenCalledWith({ - token: mockToken, - context, - resource: 'resource', + const req = request(router) + .get('/v2/tasks/a-random-id/eventstream') + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as unknown as { + statusCode: number; + headers: Record<string, string>; + }); + + res.on('data', chunk => { + responseDataFn(chunk.toString()); + + // the server expects the client to abort the request + if (chunk.includes('completion')) { + req.abort(); + } + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + expect(responseDataFn).toHaveBeenCalledTimes(2); + expect(responseDataFn).toHaveBeenCalledWith(`event: log +data: {"id":0,"taskId":"a-random-id","type":"log","createdAt":"","body":{"message":"My log message"}} + +`); + expect(responseDataFn).toHaveBeenCalledWith(`event: completion +data: {"id":1,"taskId":"a-random-id","type":"completion","createdAt":"","body":{"message":"Finished!"}} + +`); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + const { router, taskBroker } = await createTestRouter(); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + let subscriber: ZenObservable.SubscriptionObserver<{ + events: SerializedTaskEvent[]; + }>; + ( + taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + setImmediate(() => { + observer.next({ + events: [ + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); }); }); }); + + let statusCode: number | undefined = undefined; + let headers: Record<string, string> = {}; + + const req = request(router) + .get('/v2/tasks/a-random-id/eventstream') + .query({ after: 10 }) + .set('accept', 'text/event-stream') + .parse((res, _) => { + ({ statusCode, headers } = res as unknown as { + statusCode: number; + headers: Record<string, string>; + }); + + res.on('data', () => { + // close immediately + req.abort(); + }); + }); + + // wait for the request to finish + await req.catch(() => { + // ignore 'aborted' error + }); + + expect(statusCode).toBe(200); + expect(headers['content-type']).toBe('text/event-stream'); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + + expect(subscriber!.closed).toBe(true); }); - }, -); + }); + + describe('GET /v2/tasks/:taskId/events', () => { + it('should return log messages', async () => { + const { router, taskBroker } = await createTestRouter(); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + let subscriber: ZenObservable.SubscriptionObserver<{ + events: SerializedTaskEvent[]; + }>; + ( + taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] + ).mockImplementation(({ taskId }) => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ + events: [ + { + id: 0, + taskId, + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId, + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ], + }); + }); + }); + + const response = await request(router).get( + '/v2/tasks/a-random-id/events', + ); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([ + { + id: 0, + taskId: 'a-random-id', + type: 'log', + createdAt: '', + body: { message: 'My log message' }, + }, + { + id: 1, + taskId: 'a-random-id', + type: 'completion', + createdAt: '', + body: { message: 'Finished!' }, + }, + ]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + }); + expect(subscriber!.closed).toBe(true); + }); + + it('should return log messages with after query', async () => { + const { router, taskBroker } = await createTestRouter(); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: '', + }); + let subscriber: ZenObservable.SubscriptionObserver<{ + events: SerializedTaskEvent[]; + }>; + ( + taskBroker.event$ as jest.Mocked<TaskBroker>['event$'] + ).mockImplementation(() => { + return new ObservableImpl(observer => { + subscriber = observer; + observer.next({ events: [] }); + }); + }); + + const response = await request(router) + .get('/v2/tasks/a-random-id/events') + .query({ after: 10 }); + + expect(response.status).toEqual(200); + expect(response.body).toEqual([]); + + expect(taskBroker.event$).toHaveBeenCalledTimes(1); + expect(taskBroker.event$).toHaveBeenCalledWith({ + taskId: 'a-random-id', + after: 10, + }); + expect(subscriber!.closed).toBe(true); + }); + it('disallows users from seeing events for tasks they do not own', async () => { + const { permissions, router, taskBroker } = await createTestRouter(); + + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + conditions: { + resourceType: 'scaffolder-task', + rule: 'IS_TASK_OWNER', + params: { createdBy: ['user'] }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-task', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + (taskBroker.get as jest.Mocked<TaskBroker>['get']).mockResolvedValue({ + id: 'a-random-id', + spec: {} as any, + status: 'completed', + createdAt: '', + secrets: { + __initiatorCredentials: JSON.stringify(credentials), + }, + createdBy: 'not-user', + }); + + const response = await request(router).get( + `/v2/tasks/a-random-id/events`, + ); + expect(taskBroker.get).toHaveBeenCalledWith('a-random-id'); + expect(response.error).not.toBeFalsy(); + }); + }); + + describe('POST /v2/dry-run', () => { + it('should get user entity', async () => { + const { router, catalog } = await createTestRouter(); + const mockToken = mockCredentials.user.token(); + const mockTemplate = generateMockTemplate(); + + await request(router) + .post('/v2/dry-run') + .set('Authorization', `Bearer ${mockToken}`) + .send({ + template: mockTemplate, + values: { + requiredParameter1: 'required-value-1', + requiredParameter2: 'required-value-2', + }, + directoryContents: [], + }); + + expect(catalog.getEntityByRef).toHaveBeenCalledTimes(1); + + expect(catalog.getEntityByRef).toHaveBeenCalledWith( + 'user:default/mock', + expect.anything(), + ); + }); + it('disallows users from seeing tasks they do not own', async () => { + const { permissions, router, taskBroker } = await createTestRouter(); + jest + .spyOn(permissions, 'authorizeConditional') + .mockImplementationOnce(async () => [ + { + conditions: { + resourceType: 'scaffolder-task', + rule: 'IS_TASK_OWNER', + params: { createdBy: ['user'] }, + }, + pluginId: 'scaffolder', + resourceType: 'scaffolder-task', + result: AuthorizeResult.CONDITIONAL, + }, + ]); + const response = await request(router).get( + `/v2/tasks?createdBy=not-user`, + ); + expect(taskBroker.list).toHaveBeenCalledWith({ + filters: { createdBy: ['not-user'], status: undefined }, + order: undefined, + pagination: { limit: undefined, offset: undefined }, + permissionFilters: { key: 'created_by', values: ['user'] }, + }); + expect(response.status).toBe(200); + expect(response.body.totalTasks).toBe(0); + expect(response.body.tasks).toEqual([]); + }); + }); + + describe('GET /v2/autocomplete/:provider/:resource', () => { + it('should throw an error when the provider is not registered', async () => { + const handleAutocompleteRequest = jest.fn().mockResolvedValue({ + results: [{ title: 'blob' }], + }); + const { router } = await createTestRouter({ + autocompleteHandlers: { + 'test-provider': handleAutocompleteRequest, + }, + }); + + const response = await request(router) + .post('/v2/autocomplete/unknown-provider/resource') + .send({ + token: 'token', + context: {}, + }); + + expect(response.status).toEqual(400); + expect(response.body).toEqual( + expect.objectContaining({ + error: { + message: 'Unsupported provider: unknown-provider', + name: 'InputError', + }, + }), + ); + }); + + it('should call the autocomplete handler', async () => { + const handleAutocompleteRequest = jest.fn().mockResolvedValue({ + results: [{ title: 'blob' }], + }); + + const { router } = await createTestRouter({ + autocompleteHandlers: { + 'test-provider': handleAutocompleteRequest, + }, + }); + + const context = { mock: 'context' }; + const mockToken = 'mocktoken'; + + const response = await request(router) + .post('/v2/autocomplete/test-provider/resource') + .send({ + token: mockToken, + context, + }); + + expect(response.status).toEqual(200); + + expect(response.body).toEqual({ results: [{ title: 'blob' }] }); + expect(handleAutocompleteRequest).toHaveBeenCalledWith({ + token: mockToken, + context, + resource: 'resource', + }); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 0ea6293f36..6e476d9a19 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -42,6 +42,8 @@ import { EventsService } from '@backstage/plugin-events-node'; import { createConditionAuthorizer, createPermissionIntegrationRouter, + createConditionTransformer, + ConditionTransformer, } from '@backstage/plugin-permission-node'; import { TaskSpec, @@ -51,7 +53,9 @@ import { import { RESOURCE_TYPE_SCAFFOLDER_ACTION, RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, + RESOURCE_TYPE_SCAFFOLDER_TASK, scaffolderActionPermissions, + scaffolderTaskPermissions, scaffolderPermissions, scaffolderTemplatePermissions, taskCancelPermission, @@ -89,7 +93,11 @@ import { import { createDryRunner } from '../scaffolder/dryrun'; import { StorageTaskBroker } from '../scaffolder/tasks/StorageTaskBroker'; import { InternalTaskSecrets } from '../scaffolder/tasks/types'; -import { checkPermission } from '../util/checkPermissions'; +import { + checkPermission, + checkTaskPermission, + getAuthorizeConditions, +} from '../util/checkPermissions'; import { findTemplate, getEntityBaseUrl, @@ -97,7 +105,7 @@ import { parseNumberParam, parseStringsParam, } from './helpers'; -import { scaffolderActionRules, scaffolderTemplateRules } from './rules'; + import { convertFiltersToRecord, convertGlobalsToRecord, @@ -107,6 +115,9 @@ import { } from '../util/templating'; import { createDefaultFilters } from '../lib/templating/filters/createDefaultFilters'; import { + ScaffolderPermissionRuleInput, + TaskPermissionRuleInput, + isTaskPermissionRuleInput, ActionPermissionRuleInput, isActionPermissionRuleInput, isTemplatePermissionRuleInput, @@ -114,6 +125,14 @@ import { } from './permissions'; import { CatalogService } from '@backstage/plugin-catalog-node'; +import { + scaffolderActionRules, + scaffolderTemplateRules, + scaffolderTaskRules, +} from './rules'; + +import { TaskFilters } from '@backstage/plugin-scaffolder-node'; + /** * RouterOptions */ @@ -139,9 +158,7 @@ export interface RouterOptions { | CreatedTemplateGlobal[]; additionalWorkspaceProviders?: Record<string, WorkspaceProvider>; permissions?: PermissionsService; - permissionRules?: Array< - TemplatePermissionRuleInput | ActionPermissionRuleInput - >; + permissionRules?: Array<ScaffolderPermissionRuleInput>; auth: AuthService; httpAuth: HttpAuthService; events?: EventsService; @@ -312,15 +329,24 @@ export async function createRouter( const actionRules: ActionPermissionRuleInput[] = Object.values( scaffolderActionRules, ); + const taskRules: TaskPermissionRuleInput[] = + Object.values(scaffolderTaskRules); if (permissionRules) { templateRules.push( ...permissionRules.filter(isTemplatePermissionRuleInput), ); actionRules.push(...permissionRules.filter(isActionPermissionRuleInput)); + taskRules.push(...permissionRules.filter(isTaskPermissionRuleInput)); } - const isAuthorized = createConditionAuthorizer(Object.values(templateRules)); + const isTemplateAuthorized = createConditionAuthorizer( + Object.values(templateRules), + ); + const isTaskAuthorized = createConditionAuthorizer(Object.values(taskRules)); + + const taskTransformConditions: ConditionTransformer<TaskFilters> = + createConditionTransformer(Object.values(taskRules)); const permissionIntegrationRouter = createPermissionIntegrationRouter({ resources: [ @@ -334,6 +360,11 @@ export async function createRouter( permissions: scaffolderActionPermissions, rules: actionRules, }, + { + resourceType: RESOURCE_TYPE_SCAFFOLDER_TASK, + permissions: scaffolderTaskPermissions, + rules: taskRules, + }, ], permissions: scaffolderPermissions, }); @@ -532,11 +563,6 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); - await checkPermission({ - credentials, - permissions: [taskReadPermission], - permissionService: permissions, - }); if (!taskBroker.list) { throw new Error( @@ -564,6 +590,13 @@ export async function createRouter( const limit = parseNumberParam(req.query.limit, 'limit'); const offset = parseNumberParam(req.query.offset, 'offset'); + const taskPermissionFilters = await getAuthorizeConditions({ + credentials: credentials, + permission: taskReadPermission, + permissionService: permissions, + transformConditions: taskTransformConditions, + }); + const tasks = await taskBroker.list({ filters: { createdBy, @@ -574,6 +607,7 @@ export async function createRouter( limit: limit ? limit[0] : undefined, offset: offset ? offset[0] : undefined, }, + permissionFilters: taskPermissionFilters, }); await auditorEvent?.success(); @@ -598,13 +632,17 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); - await checkPermission({ + + const task = await taskBroker.get(taskId); + + await checkTaskPermission({ credentials, permissions: [taskReadPermission], permissionService: permissions, + task: task, + isTaskAuthorized, }); - const task = await taskBroker.get(taskId); if (!task) { throw new NotFoundError(`Task with id ${taskId} does not exist`); } @@ -634,11 +672,14 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); + const task = await taskBroker.get(taskId); // Requires both read and cancel permissions - await checkPermission({ + await checkTaskPermission({ credentials, permissions: [taskCancelPermission, taskReadPermission], permissionService: permissions, + task: task, + isTaskAuthorized, }); await taskBroker.cancel?.(taskId); @@ -666,13 +707,23 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); - // Requires both read and cancel permissions + const task = await taskBroker.get(taskId); + + // Requires both read and create permissions await checkPermission({ credentials, - permissions: [taskCreatePermission, taskReadPermission], + permissions: [taskCreatePermission], permissionService: permissions, }); + await checkTaskPermission({ + credentials, + permissions: [taskReadPermission], + permissionService: permissions, + task: task, + isTaskAuthorized, + }); + await auditorEvent?.success(); const { token } = await auth.getPluginRequestToken({ @@ -711,10 +762,14 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); - await checkPermission({ + const task = await taskBroker.get(taskId); + + await checkTaskPermission({ credentials, permissions: [taskReadPermission], permissionService: permissions, + task: task, + isTaskAuthorized, }); const after = @@ -783,10 +838,14 @@ export async function createRouter( try { const credentials = await httpAuth.credentials(req); - await checkPermission({ + const task = await taskBroker.get(taskId); + + await checkTaskPermission({ credentials, permissions: [taskReadPermission], permissionService: permissions, + task: task, + isTaskAuthorized, }); const after = Number(req.query.after) || undefined; @@ -1023,18 +1082,18 @@ export async function createRouter( // Authorize parameters if (Array.isArray(template.spec.parameters)) { template.spec.parameters = template.spec.parameters.filter(step => - isAuthorized(parameterDecision, step), + isTemplateAuthorized(parameterDecision, step), ); } else if ( template.spec.parameters && - !isAuthorized(parameterDecision, template.spec.parameters) + !isTemplateAuthorized(parameterDecision, template.spec.parameters) ) { template.spec.parameters = undefined; } // Authorize steps template.spec.steps = template.spec.steps.filter(step => - isAuthorized(stepDecision, step), + isTemplateAuthorized(stepDecision, step), ); return template; diff --git a/plugins/scaffolder-backend/src/service/rules.test.ts b/plugins/scaffolder-backend/src/service/rules.test.ts index 8e24cacb32..85e393247a 100644 --- a/plugins/scaffolder-backend/src/service/rules.test.ts +++ b/plugins/scaffolder-backend/src/service/rules.test.ts @@ -22,10 +22,13 @@ import { hasProperty, hasStringProperty, hasTag, + isTaskOwner, } from './rules'; import { createConditionAuthorizer } from '@backstage/plugin-permission-node'; import { RESOURCE_TYPE_SCAFFOLDER_ACTION } from '@backstage/plugin-scaffolder-common/alpha'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; +import { SerializedTask } from '@backstage/plugin-scaffolder-node'; +import { TaskSpec } from '@backstage/plugin-scaffolder-common'; describe('hasTag', () => { describe('apply', () => { @@ -523,3 +526,77 @@ describe('hasStringProperty', () => { ); }); }); + +describe('isTaskOwner', () => { + describe('apply', () => { + const task: SerializedTask = { + id: 'a-random-id', + spec: {} as TaskSpec, + status: 'completed', + createdAt: '', + createdBy: 'user:default/user-1', + }; + it('returns false when createdBy is an empty array', () => { + expect( + isTaskOwner.apply(task, { + createdBy: [], + }), + ).toEqual(false); + }); + it('returns false when createdBy is not matched (single user in createdBy)', () => { + expect( + isTaskOwner.apply(task, { + createdBy: ['not-matched'], + }), + ).toEqual(false); + }); + it('returns true when createdBy matches (single user in createdBy)', () => { + expect( + isTaskOwner.apply(task, { + createdBy: ['user:default/user-1'], + }), + ).toEqual(true); + }); + it('returns false when createdBy is not matched (multiple users in createdBy)', () => { + expect( + isTaskOwner.apply(task, { + createdBy: [ + 'user:default/user-2', + 'user:default/user-3', + 'user:default/user-4', + ], + }), + ).toEqual(false); + }); + it('returns true when createdBy matches (multiple users in createdBy)', () => { + expect( + isTaskOwner.apply(task, { + createdBy: [ + 'user:default/user-1', + 'user:default/user-2', + 'user:default/user-3', + ], + }), + ).toEqual(true); + }); + }); + describe('toQuery', () => { + it('returns the correct query filter with values (single user in createdBy)', () => { + expect( + isTaskOwner.toQuery({ + createdBy: ['user:default/user-1'], + }), + ).toEqual({ key: 'created_by', values: ['user:default/user-1'] }); + }); + }); + it('returns the correct query filter with values (multiple users in createdBy)', () => { + expect( + isTaskOwner.toQuery({ + createdBy: ['user:default/user-1', 'user:default/user-2'], + }), + ).toEqual({ + key: 'created_by', + values: ['user:default/user-1', 'user:default/user-2'], + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/service/rules.ts b/plugins/scaffolder-backend/src/service/rules.ts index b197810757..5753668089 100644 --- a/plugins/scaffolder-backend/src/service/rules.ts +++ b/plugins/scaffolder-backend/src/service/rules.ts @@ -15,9 +15,11 @@ */ import { makeCreatePermissionRule } from '@backstage/plugin-permission-node'; + import { RESOURCE_TYPE_SCAFFOLDER_TEMPLATE, RESOURCE_TYPE_SCAFFOLDER_ACTION, + RESOURCE_TYPE_SCAFFOLDER_TASK, } from '@backstage/plugin-scaffolder-common/alpha'; import { @@ -25,6 +27,8 @@ import { TemplateParametersV1beta3, } from '@backstage/plugin-scaffolder-common'; +import { SerializedTask, TaskFilter } from '@backstage/plugin-scaffolder-node'; + import { z } from 'zod'; import { JsonObject, JsonPrimitive } from '@backstage/types'; import { get } from 'lodash'; @@ -129,6 +133,37 @@ function buildHasProperty<Schema extends z.ZodType<JsonPrimitive>>({ }); } +export const createTaskPermissionRule = makeCreatePermissionRule< + SerializedTask, + TaskFilter, + typeof RESOURCE_TYPE_SCAFFOLDER_TASK +>(); + +export const isTaskOwner = createTaskPermissionRule({ + name: 'IS_TASK_OWNER', + description: 'Allows tasks created by certain users to be accessible', + resourceType: RESOURCE_TYPE_SCAFFOLDER_TASK, + paramsSchema: z.object({ + createdBy: z + .array(z.string()) + .describe( + 'List of creater entity refs; only tasks created by these users will be viewable', + ), + }), + apply: (resource, { createdBy }) => { + if (!resource.createdBy) { + return false; + } + return createdBy.includes(resource.createdBy); + }, + toQuery: ({ createdBy }) => { + return { + key: 'created_by', + values: createdBy, + }; + }, +}); + export const scaffolderTemplateRules = { hasTag }; export const scaffolderActionRules = { hasActionId, @@ -136,3 +171,4 @@ export const scaffolderActionRules = { hasNumberProperty, hasStringProperty, }; +export const scaffolderTaskRules = { isTaskOwner }; diff --git a/plugins/scaffolder-backend/src/util/checkPermissions.ts b/plugins/scaffolder-backend/src/util/checkPermissions.ts index b6c0395fbb..08a4ae1b13 100644 --- a/plugins/scaffolder-backend/src/util/checkPermissions.ts +++ b/plugins/scaffolder-backend/src/util/checkPermissions.ts @@ -21,7 +21,13 @@ import { NotAllowedError } from '@backstage/errors'; import { AuthorizeResult, BasicPermission, + PermissionCriteria, + PolicyDecision, + ResourcePermission, } from '@backstage/plugin-permission-common'; +import { ConditionTransformer } from '@backstage/plugin-permission-node'; +import { SerializedTask } from '@backstage/plugin-scaffolder-node'; +import { TaskFilters } from '@backstage/plugin-scaffolder-node'; export type checkPermissionOptions = { credentials: BackstageCredentials; @@ -29,6 +35,24 @@ export type checkPermissionOptions = { permissionService?: PermissionsService; }; +export type checkTaskPermissionOptions = { + credentials: BackstageCredentials; + permissions: ResourcePermission[]; + permissionService?: PermissionsService; + task: SerializedTask; + isTaskAuthorized: ( + decision: PolicyDecision, + resource: SerializedTask | undefined, + ) => boolean; +}; + +export type authorizeConditionsOptions = { + credentials: BackstageCredentials; + permission: ResourcePermission; + permissionService?: PermissionsService; + transformConditions: ConditionTransformer<TaskFilters>; +}; + /** * Does a basic check on permissions. Throws 403 error if any permission responds with AuthorizeResult.DENY * @public @@ -51,3 +75,55 @@ export async function checkPermission(options: checkPermissionOptions) { } } } + +/** + * Does a conditional permission check for scaffolder task reading and cancellation. + * Throws 403 error if permission responds with AuthorizeResult.DENY, or does not resolve to true during the conditional rule check + * @public + */ +export async function checkTaskPermission(options: checkTaskPermissionOptions) { + const { + permissions, + permissionService, + credentials, + task, + isTaskAuthorized, + } = options; + if (permissionService) { + const permissionRequest = permissions.map(permission => ({ + permission, + })); + const authorizationResponses = await permissionService.authorizeConditional( + permissionRequest, + { credentials }, + ); + for (const response of authorizationResponses) { + if ( + response.result === AuthorizeResult.DENY || + !isTaskAuthorized(response, task) + ) { + throw new NotAllowedError(); + } + } + } +} + +/** Fetches and transforms authorization conditions into filters, or returns `undefined` if the decision is not conditional. + * @public + */ +export const getAuthorizeConditions = async ( + options: authorizeConditionsOptions, +): Promise<PermissionCriteria<TaskFilters> | undefined> => { + const { permission, permissionService, credentials, transformConditions } = + options; + if (permissionService) { + const [taskDecision] = await permissionService.authorizeConditional( + [{ permission: permission }], + { credentials }, + ); + if (taskDecision.result === AuthorizeResult.CONDITIONAL) { + return transformConditions(taskDecision.conditions); + } + } + return undefined; +}; diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 5f1ed60113..360890bd90 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-scaffolder-common +## 1.6.0-next.1 + +### Minor Changes + +- c1ce316: BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + + BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + + BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. + +## 1.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + ## 1.5.11 ### Patch Changes diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 92e044898f..acb4df8f9c 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-common", - "version": "1.5.11", + "version": "1.6.0-next.1", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", "backstage": { "role": "common-library", diff --git a/plugins/scaffolder-common/report-alpha.api.md b/plugins/scaffolder-common/report-alpha.api.md index 97a3de63c5..0bed459cc8 100644 --- a/plugins/scaffolder-common/report-alpha.api.md +++ b/plugins/scaffolder-common/report-alpha.api.md @@ -12,6 +12,9 @@ export const actionExecutePermission: ResourcePermission<'scaffolder-action'>; // @alpha export const RESOURCE_TYPE_SCAFFOLDER_ACTION = 'scaffolder-action'; +// @alpha +export const RESOURCE_TYPE_SCAFFOLDER_TASK = 'scaffolder-task'; + // @alpha export const RESOURCE_TYPE_SCAFFOLDER_TEMPLATE = 'scaffolder-template'; @@ -23,22 +26,26 @@ export const scaffolderPermissions: ( | BasicPermission | ResourcePermission<'scaffolder-action'> | ResourcePermission<'scaffolder-template'> + | ResourcePermission<'scaffolder-task'> )[]; // @alpha -export const scaffolderTaskPermissions: BasicPermission[]; +export const scaffolderTaskPermissions: ( + | BasicPermission + | ResourcePermission<'scaffolder-task'> +)[]; // @alpha export const scaffolderTemplatePermissions: ResourcePermission<'scaffolder-template'>[]; // @alpha -export const taskCancelPermission: BasicPermission; +export const taskCancelPermission: ResourcePermission<'scaffolder-task'>; // @alpha export const taskCreatePermission: BasicPermission; // @alpha -export const taskReadPermission: BasicPermission; +export const taskReadPermission: ResourcePermission<'scaffolder-task'>; // @alpha export const templateManagementPermission: BasicPermission; diff --git a/plugins/scaffolder-common/src/permissions.ts b/plugins/scaffolder-common/src/permissions.ts index e758291e75..36c72072e9 100644 --- a/plugins/scaffolder-common/src/permissions.ts +++ b/plugins/scaffolder-common/src/permissions.ts @@ -30,6 +30,13 @@ export const RESOURCE_TYPE_SCAFFOLDER_TEMPLATE = 'scaffolder-template'; */ export const RESOURCE_TYPE_SCAFFOLDER_ACTION = 'scaffolder-action'; +/** + * Permission resource type which corresponds to scaffolder tasks + * + * @alpha + */ +export const RESOURCE_TYPE_SCAFFOLDER_TASK = 'scaffolder-task'; + /** * This permission is used to authorize actions that involve executing * an action from a template. @@ -89,6 +96,7 @@ export const taskReadPermission = createPermission({ attributes: { action: 'read', }, + resourceType: RESOURCE_TYPE_SCAFFOLDER_TASK, }); /** @@ -111,6 +119,7 @@ export const taskCreatePermission = createPermission({ export const taskCancelPermission = createPermission({ name: 'scaffolder.task.cancel', attributes: {}, + resourceType: RESOURCE_TYPE_SCAFFOLDER_TASK, }); /** diff --git a/plugins/scaffolder-node-test-utils/CHANGELOG.md b/plugins/scaffolder-node-test-utils/CHANGELOG.md index 6eddc2ddb1..7d69e03fe0 100644 --- a/plugins/scaffolder-node-test-utils/CHANGELOG.md +++ b/plugins/scaffolder-node-test-utils/CHANGELOG.md @@ -1,5 +1,68 @@ # @backstage/plugin-scaffolder-node-test-utils +## 0.3.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.10.0-next.2 + +## 0.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/backend-test-utils@1.7.0-next.1 + - @backstage/plugin-scaffolder-node@0.9.1-next.1 + +## 0.3.1-next.0 + +### Patch Changes + +- dbde180: An internal refactor which adds additional types to experimental checkpoints +- Updated dependencies + - @backstage/backend-test-utils@1.7.0-next.0 + - @backstage/plugin-scaffolder-node@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/types@1.2.1 + +## 0.3.0 + +### Minor Changes + +- 3cea7ee: **BREAKING CHANGES** + + Because of the removal of the `logStream` property to the `ActionsContext` this has been removed from the `createMockActionContext` method. + + You can remove this as it's no longer supported in the scaffolder actions. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0 + - @backstage/backend-test-utils@1.6.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/types@1.2.1 + +## 0.3.0-next.2 + +### Minor Changes + +- 3cea7ee: **BREAKING CHANGES** + + Because of the removal of the `logStream` property to the `ActionsContext` this has been removed from the `createMockActionContext` method. + + You can remove this as it's no longer supported in the scaffolder actions. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.9.0-next.2 + - @backstage/backend-test-utils@1.6.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/types@1.2.1 + ## 0.2.3-next.1 ### Patch Changes diff --git a/plugins/scaffolder-node-test-utils/package.json b/plugins/scaffolder-node-test-utils/package.json index 8f334cd23f..a4bb67a793 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.3-next.1", + "version": "0.3.1-next.2", "backstage": { "role": "node-library", "pluginId": "scaffolder", diff --git a/plugins/scaffolder-node-test-utils/src/actions/mockActionContext.ts b/plugins/scaffolder-node-test-utils/src/actions/mockActionContext.ts index a6703a12d1..e2f07f2320 100644 --- a/plugins/scaffolder-node-test-utils/src/actions/mockActionContext.ts +++ b/plugins/scaffolder-node-test-utils/src/actions/mockActionContext.ts @@ -22,6 +22,7 @@ import { } from '@backstage/backend-test-utils'; import { JsonObject, JsonValue } from '@backstage/types'; import { ActionContext } from '@backstage/plugin-scaffolder-node'; +import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha'; import { loggerToWinstonLogger } from './loggerToWinstonLogger'; /** @@ -44,10 +45,9 @@ export function createMockActionContext< output: jest.fn(), createTemporaryDirectory: jest.fn(), input: {} as TActionInput, - async checkpoint<T extends JsonValue | void>(opts: { - key: string; - fn: () => Promise<T> | T; - }): Promise<T> { + async checkpoint<T extends JsonValue | void>( + opts: CheckpointContext<T>, + ): Promise<T> { return opts.fn(); }, getInitiatorCredentials: () => Promise.resolve(credentials), diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 068a613888..3e60eb243e 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,187 @@ # @backstage/plugin-scaffolder-node +## 0.10.0-next.2 + +### Minor Changes + +- c1ce316: BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + + BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + + BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + +## 0.9.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + +## 0.9.1-next.0 + +### Patch Changes + +- dbde180: An internal refactor which adds additional types to experimental checkpoints +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 0.9.0 + +### Minor Changes + +- 5863b04: **BREAKING CHANGES** + + The legacy methods to define `createTemplateActions` have been replaced with the new native `zod` approaches for defining input and output schemas. + + You can migrate actions that look like the following with the below examples: + + ```ts + // really old legacy json schema + createTemplateAction<{ repoUrl: string }, { repoOutput: string }>({ + id: 'test', + schema: { + input: { + type: 'object' + required: ['repoUrl'] + properties: { + repoUrl: { + type: 'string', + description: 'repository url description' + } + } + } + } + }); + + // old zod method + createTemplateAction({ + id: 'test' + schema: { + input: { + repoUrl: z.string({ description: 'repository url description' }) + } + } + }) + + // new method: + createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: z => z.string({ description: 'repository url description' }) + } + } + }) + + // or for more complex zod types like unions + createTemplateAction({ + id: 'test', + schema: { + input: z => z.object({ + repoUrl: z.string({ description: 'repository url description' }) + }) + } + }) + ``` + + This breaking change also means that `logStream` has been removed entirely from `ActionsContext`, and that the `logger` is now just a `LoggerService` implementation instead. There is no replacement for the `logStream`, if you wish to still keep using a `logStream` we recommend that you create your own stream that writes to `ctx.logger` instead. + +### Patch Changes + +- e89d7b6: Use `LoggerService` instead of `Logger`. This is a non-breaking change, as the `LoggerService` is a subset of the `Logger` interface. +- 9c8ff0c: Update pull request creation filter to include .gitignore files in the created pull request +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 0.9.0-next.2 + +### Minor Changes + +- 5863b04: **BREAKING CHANGES** + + The legacy methods to define `createTemplateActions` have been replaced with the new native `zod` approaches for defining input and output schemas. + + You can migrate actions that look like the following with the below examples: + + ```ts + // really old legacy json schema + createTemplateAction<{ repoUrl: string }, { repoOutput: string }>({ + id: 'test', + schema: { + input: { + type: 'object' + required: ['repoUrl'] + properties: { + repoUrl: { + type: 'string', + description: 'repository url description' + } + } + } + } + }); + + // old zod method + createTemplateAction({ + id: 'test' + schema: { + input: { + repoUrl: z.string({ description: 'repository url description' }) + } + } + }) + + // new method: + createTemplateAction({ + id: 'test', + schema: { + input: { + repoUrl: z => z.string({ description: 'repository url description' }) + } + } + }) + + // or for more complex zod types like unions + createTemplateAction({ + id: 'test', + schema: { + input: z => z.object({ + repoUrl: z.string({ description: 'repository url description' }) + }) + } + }) + ``` + + This breaking change also means that `logStream` has been removed entirely from `ActionsContext`, and that the `logger` is now just a `LoggerService` implementation instead. There is no replacement for the `logStream`, if you wish to still keep using a `logStream` we recommend that you create your own stream that writes to `ctx.logger` instead. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-scaffolder-common@1.5.11 + ## 0.8.3-next.1 ### Patch Changes diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 6e44babf81..191ca53b62 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.3-next.1", + "version": "0.10.0-next.2", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", "backstage": { "role": "node-library", @@ -58,6 +58,7 @@ "@backstage/catalog-model": "workspace:^", "@backstage/errors": "workspace:^", "@backstage/integration": "workspace:^", + "@backstage/plugin-permission-common": "workspace:^", "@backstage/plugin-scaffolder-common": "workspace:^", "@backstage/types": "workspace:^", "@isomorphic-git/pgp-plugin": "^0.0.7", diff --git a/plugins/scaffolder-node/report-alpha.api.md b/plugins/scaffolder-node/report-alpha.api.md index a96f312d2d..09541fbc56 100644 --- a/plugins/scaffolder-node/report-alpha.api.md +++ b/plugins/scaffolder-node/report-alpha.api.md @@ -27,6 +27,31 @@ export type AutocompleteHandler = ({ }[]; }>; +// @alpha +export type CheckpointContext<T extends JsonValue | void = JsonValue> = { + key: string; + fn: () => Promise<T> | T; +}; + +// @alpha +export type CheckpointState = { + [key: string]: CheckpointStateValue; +}; + +// @alpha +export type CheckpointStateValue<T extends JsonValue = JsonValue> = + | { + status: 'failed'; + reason: string; + } + | { + status: 'success'; + value: T; + }; + +// @alpha +export type CheckpointStatus = 'failed' | 'success'; + // @alpha (undocumented) export type CreatedTemplateFilter< TFunctionArgs extends [z.ZodTypeAny, ...z.ZodTypeAny[]], @@ -187,6 +212,11 @@ export type TemplateGlobalFunctionExample = { notes?: string; }; +// @alpha +export type UpdateTaskCheckpointOptions = { + key: string; +} & CheckpointStateValue; + // @alpha export interface WorkspaceProvider { // (undocumented) diff --git a/plugins/scaffolder-node/report.api.md b/plugins/scaffolder-node/report.api.md index a23a9137b1..d0fd036f29 100644 --- a/plugins/scaffolder-node/report.api.md +++ b/plugins/scaffolder-node/report.api.md @@ -4,17 +4,20 @@ ```ts import { BackstageCredentials } from '@backstage/backend-plugin-api'; +import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha'; import { Expand } from '@backstage/types'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Observable } from '@backstage/types'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { Schema } from 'jsonschema'; import { ScmIntegrationRegistry } from '@backstage/integration'; import { ScmIntegrations } from '@backstage/integration'; import { SpawnOptionsWithoutStdio } from 'child_process'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; +import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha'; import { UrlReaderService } from '@backstage/backend-plugin-api'; import { UserEntity } from '@backstage/catalog-model'; import { Writable } from 'stream'; @@ -30,10 +33,9 @@ export type ActionContext< secrets?: TaskSecrets; workspacePath: string; input: TActionInput; - checkpoint<T extends JsonValue | void>(opts: { - key: string; - fn: () => Promise<T> | T; - }): Promise<T>; + checkpoint<T extends JsonValue | void>( + opts: CheckpointContext<T>, + ): Promise<T>; output( name: keyof TActionOutput, value: TActionOutput[keyof TActionOutput], @@ -373,6 +375,7 @@ export interface TaskBroker { order: 'asc' | 'desc'; field: string; }[]; + permissionFilters?: PermissionCriteria<TaskFilters>; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number; @@ -446,24 +449,31 @@ export interface TaskContext { // (undocumented) taskId?: string; // (undocumented) - updateCheckpoint?( - options: - | { - key: string; - status: 'success'; - value: JsonValue; - } - | { - key: string; - status: 'failed'; - reason: string; - }, - ): Promise<void>; + updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>; } // @public export type TaskEventType = 'completion' | 'log' | 'cancelled' | 'recovered'; +// @public +export type TaskFilter = { + key: string; + values?: string[]; +}; + +// @public +export type TaskFilters = + | { + anyOf: TaskFilter[]; + } + | { + allOf: TaskFilter[]; + } + | { + not: TaskFilter; + } + | TaskFilter; + // @public export type TaskSecrets = Record<string, string> & { backstageToken?: string; diff --git a/plugins/scaffolder-node/src/actions/types.ts b/plugins/scaffolder-node/src/actions/types.ts index f13d256332..0162d96d95 100644 --- a/plugins/scaffolder-node/src/actions/types.ts +++ b/plugins/scaffolder-node/src/actions/types.ts @@ -23,6 +23,8 @@ import { BackstageCredentials, LoggerService, } from '@backstage/backend-plugin-api'; +import { CheckpointContext } from '@backstage/plugin-scaffolder-node/alpha'; + /** * ActionContext is passed into scaffolder actions. * @public @@ -36,10 +38,9 @@ export type ActionContext< secrets?: TaskSecrets; workspacePath: string; input: TActionInput; - checkpoint<T extends JsonValue | void>(opts: { - key: string; - fn: () => Promise<T> | T; - }): Promise<T>; + checkpoint<T extends JsonValue | void>( + opts: CheckpointContext<T>, + ): Promise<T>; output( name: keyof TActionOutput, value: TActionOutput[keyof TActionOutput], diff --git a/plugins/scaffolder-node/src/alpha/checkpoints/index.ts b/plugins/scaffolder-node/src/alpha/checkpoints/index.ts new file mode 100644 index 0000000000..16c936b235 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/checkpoints/index.ts @@ -0,0 +1,16 @@ +/* + * 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 * from './types'; diff --git a/plugins/scaffolder-node/src/alpha/checkpoints/types.ts b/plugins/scaffolder-node/src/alpha/checkpoints/types.ts new file mode 100644 index 0000000000..5bd1eb5cd3 --- /dev/null +++ b/plugins/scaffolder-node/src/alpha/checkpoints/types.ts @@ -0,0 +1,57 @@ +/* + * 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 { JsonValue } from '@backstage/types'; + +/** + * The status of a checkpoint, indicating whether it succeeded or failed. + * + * @alpha + */ +export type CheckpointStatus = 'failed' | 'success'; + +/** + * Represents the union of all possible checkpoint state values. + * + * @alpha + */ +export type CheckpointStateValue<T extends JsonValue = JsonValue> = + | { status: 'failed'; reason: string } + | { status: 'success'; value: T }; + +/** + * A map of checkpoint keys to their states. + * + * @alpha + */ +export type CheckpointState = { + [key: string]: CheckpointStateValue; +}; + +/** + * Context for checkpoint function invocation. + * + * @alpha + */ +export type CheckpointContext<T extends JsonValue | void = JsonValue> = { + /** + * Unique key for the checkpoint + */ + key: string; + /** + * Function to execute for the checkpoint + */ + fn: () => Promise<T> | T; +}; diff --git a/plugins/scaffolder-node/src/alpha/index.ts b/plugins/scaffolder-node/src/alpha/index.ts index 9df968a52d..30f95f116a 100644 --- a/plugins/scaffolder-node/src/alpha/index.ts +++ b/plugins/scaffolder-node/src/alpha/index.ts @@ -28,6 +28,7 @@ export * from '../tasks/alpha'; export * from './filters'; export * from './globals'; export * from './types'; +export * from './checkpoints'; /** * Extension point for managing scaffolder actions. diff --git a/plugins/scaffolder-node/src/tasks/alpha.ts b/plugins/scaffolder-node/src/tasks/alpha.ts index fb6b09026c..4354a798cf 100644 --- a/plugins/scaffolder-node/src/tasks/alpha.ts +++ b/plugins/scaffolder-node/src/tasks/alpha.ts @@ -1,3 +1,5 @@ +import { CheckpointStateValue } from '../alpha'; + /* * Copyright 2024 The Backstage Authors * @@ -14,3 +16,12 @@ * limitations under the License. */ export * from './serializer'; + +/** + * Options for updating a checkpoint in a task. + * + * @alpha + */ +export type UpdateTaskCheckpointOptions = { + key: string; +} & CheckpointStateValue; diff --git a/plugins/scaffolder-node/src/tasks/index.ts b/plugins/scaffolder-node/src/tasks/index.ts index 930de95237..7e93e84cc0 100644 --- a/plugins/scaffolder-node/src/tasks/index.ts +++ b/plugins/scaffolder-node/src/tasks/index.ts @@ -18,6 +18,8 @@ export type { TaskSecrets, SerializedTask, SerializedTaskEvent, + TaskFilter, + TaskFilters, TaskBroker, TaskBrokerDispatchOptions, TaskBrokerDispatchResult, diff --git a/plugins/scaffolder-node/src/tasks/types.ts b/plugins/scaffolder-node/src/tasks/types.ts index d8cfd24530..b55a2729b7 100644 --- a/plugins/scaffolder-node/src/tasks/types.ts +++ b/plugins/scaffolder-node/src/tasks/types.ts @@ -15,8 +15,10 @@ */ import { BackstageCredentials } from '@backstage/backend-plugin-api'; +import { PermissionCriteria } from '@backstage/plugin-permission-common'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; -import { JsonObject, JsonValue, Observable } from '@backstage/types'; +import { JsonObject, Observable } from '@backstage/types'; +import { UpdateTaskCheckpointOptions } from '@backstage/plugin-scaffolder-node/alpha'; /** * TaskSecrets @@ -104,6 +106,25 @@ export type TaskBrokerDispatchOptions = { createdBy?: string; }; +/** + * TaskFilter + * @public + */ +export type TaskFilter = { + key: string; + values?: string[]; +}; + +/** + * TaskFilters + * @public + */ +export type TaskFilters = + | { anyOf: TaskFilter[] } + | { allOf: TaskFilter[] } + | { not: TaskFilter } + | TaskFilter; + /** * Task * @@ -129,19 +150,7 @@ export interface TaskContext { | undefined >; - updateCheckpoint?( - options: - | { - key: string; - status: 'success'; - value: JsonValue; - } - | { - key: string; - status: 'failed'; - reason: string; - }, - ): Promise<void>; + updateCheckpoint?(options: UpdateTaskCheckpointOptions): Promise<void>; serializeWorkspace?(options: { path: string }): Promise<void>; @@ -194,6 +203,7 @@ export interface TaskBroker { offset?: number; }; order?: { order: 'asc' | 'desc'; field: string }[]; + permissionFilters?: PermissionCriteria<TaskFilters>; }): Promise<{ tasks: SerializedTask[]; totalTasks?: number }>; /** diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index b9561731f3..064b0de5fa 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,94 @@ # @backstage/plugin-scaffolder-react +## 1.18.0-next.2 + +### Minor Changes + +- c1ce316: BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + + BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + + BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. + +### Patch Changes + +- 94c11a5: Scroll to the top of the page when navigating between steps in template forms. +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + +## 1.17.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.17.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 1.17.0 + +### Minor Changes + +- 6c972fe: Added information about the `entityRef` and `taskId` to the analytics events whenever is possible. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 1.16.1-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-permission-react@0.4.34 + - @backstage/plugin-scaffolder-common@1.5.11 + ## 1.16.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index d0e2c6344b..39da67460a 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.1-next.1", + "version": "1.18.0-next.2", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", "backstage": { "role": "web-library", diff --git a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx index 9546792983..2df4ff3b71 100644 --- a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx @@ -30,7 +30,6 @@ import Functions from '@material-ui/icons/Functions'; import MoreVert from '@material-ui/icons/MoreVert'; import { SyntheticEvent, useState } from 'react'; import { usePermission } from '@backstage/plugin-permission-react'; -import { taskReadPermission } from '@backstage/plugin-scaffolder-common/alpha'; import { templateManagementPermission } from '@backstage/plugin-scaffolder-common/alpha'; import { scaffolderReactTranslationRef } from '../../../translation'; @@ -69,10 +68,6 @@ export function ScaffolderPageContextMenu( const classes = useStyles(); const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>(); - const { allowed: canReadTasks } = usePermission({ - permission: taskReadPermission, - }); - const { allowed: canManageTemplates } = usePermission({ permission: templateManagementPermission, }); @@ -164,7 +159,7 @@ export function ScaffolderPageContextMenu( /> </MenuItem> )} - {onTasksClicked && canReadTasks && ( + {onTasksClicked && ( <MenuItem onClick={onTasksClicked}> <ListItemIcon> <List fontSize="small" /> diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx index af9117ccf5..b0746e0672 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.test.tsx @@ -672,6 +672,40 @@ describe('Stepper', () => { ); }); + it('should scroll the first main element to top when activeStep changes', async () => { + const manifest: TemplateParameterSchema = { + steps: [ + { title: 'Step 1', schema: { properties: {} } }, + { title: 'Step 2', schema: { properties: {} } }, + ], + title: 'Scroll Test', + }; + + // Render a main element in the document for the Stepper to find + const main = document.createElement('main'); + document.body.appendChild(main); + const scrollToMock = jest.fn(); + main.scrollTo = scrollToMock; + + // Render Stepper as usual (do not pass container) + const { getByRole, unmount } = await renderInTestApp( + <SecretsContextProvider> + <Stepper manifest={manifest} extensions={[]} onCreate={jest.fn()} /> + </SecretsContextProvider>, + ); + + // Click next to change the activeStep + await act(async () => { + fireEvent.click(getByRole('button', { name: 'Next' })); + }); + + expect(scrollToMock).toHaveBeenCalledWith({ top: 0, behavior: 'auto' }); + + // Clean up + document.body.removeChild(main); + unmount(); + }); + describe('Scaffolder Layouts', () => { it('should render the step in the scaffolder layout', async () => { const ScaffolderLayout: LayoutTemplate = ({ properties }) => ( diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 6715eb0e1e..1f36eb58d3 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -37,6 +37,7 @@ import { merge } from 'lodash'; import { ComponentType, useCallback, + useEffect, useMemo, useState, type ReactNode, @@ -218,6 +219,13 @@ export const Stepper = (stepperProps: StepperProps) => { [validation, analytics], ); + useEffect(() => { + const main = document.querySelector('main'); + if (main && typeof main.scrollTo === 'function') { + main.scrollTo({ top: 0, behavior: 'auto' }); + } + }, [activeStep]); + const mergedUiSchema = merge({}, propUiSchema, currentStep?.uiSchema); const [isCreating, setIsCreating] = useState(false); diff --git a/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx b/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx index a70b822d13..74ade33344 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateGroup/TemplateGroup.tsx @@ -22,7 +22,7 @@ import { } from '@backstage/core-components'; import { stringifyEntityRef } from '@backstage/catalog-model'; import { TemplateCardProps, TemplateCard } from '../TemplateCard'; -import { IconComponent } from '@backstage/core-plugin-api'; +import { AnalyticsContext, IconComponent } from '@backstage/core-plugin-api'; /** * The props for the {@link TemplateGroup} component. @@ -69,12 +69,18 @@ export const TemplateGroup = (props: TemplateGroupProps) => { {titleComponent} <ItemCardGrid> {templates.map(({ template, additionalLinks }) => ( - <Card + <AnalyticsContext + attributes={{ + entityRef: stringifyEntityRef(template), + }} key={stringifyEntityRef(template)} - additionalLinks={additionalLinks} - template={template} - onSelected={onSelected} - /> + > + <Card + additionalLinks={additionalLinks} + template={template} + onSelected={onSelected} + /> + </AnalyticsContext> ))} </ItemCardGrid> </Content> diff --git a/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx b/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx index a0070765fe..58b74dff9d 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateGroups/TemplateGroups.tsx @@ -22,7 +22,7 @@ import { } from '@backstage/plugin-scaffolder-common'; import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react'; import Typography from '@material-ui/core/Typography'; -import { ComponentType, useCallback } from 'react'; +import { ComponentType, useCallback, useEffect } from 'react'; import { TemplateGroup } from '../TemplateGroup/TemplateGroup'; @@ -58,12 +58,17 @@ export const TemplateGroups = (props: TemplateGroupsProps) => { [onTemplateSelected], ); + useEffect(() => { + if (error) { + errorApi.post(error); + } + }, [error, errorApi]); + if (loading) { return <Progress />; } if (error) { - errorApi.post(error); return null; } diff --git a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx index 2aa4ee9b2d..527009037d 100644 --- a/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx +++ b/plugins/scaffolder-react/src/next/components/Workflow/Workflow.tsx @@ -97,13 +97,14 @@ export const Workflow = (workflowProps: WorkflowProps): JSX.Element | null => { async (formState: Record<string, JsonValue>) => { await onCreate(formState); - const name = - typeof formState.name === 'string' ? formState.name : undefined; - analytics.captureEvent('create', name ?? templateName ?? 'unknown', { + analytics.captureEvent('create', 'Task has been created', { value: minutesSaved, + attributes: { + templateSteps: sortedManifest?.steps?.length ?? 0, + }, }); }, - [onCreate, analytics, templateName, minutesSaved], + [onCreate, analytics, minutesSaved, sortedManifest], ); useEffect(() => { diff --git a/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts b/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts index be6f581f87..cac696a745 100644 --- a/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts +++ b/plugins/scaffolder-react/src/next/hooks/useFilteredSchemaProperties.ts @@ -16,6 +16,7 @@ import cloneDeep from 'lodash/cloneDeep'; import { useApi, featureFlagsApiRef } from '@backstage/core-plugin-api'; import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; +import { useMemo } from 'react'; /** * Returns manifest of software templates with steps without a featureFlag tag. @@ -28,49 +29,51 @@ export const useFilteredSchemaProperties = ( const featureFlagKey = 'backstage:featureFlag'; const featureFlagApi = useApi(featureFlagsApiRef); - if (!manifest) { - return undefined; - } - - const filteredSteps = manifest?.steps - .filter(step => { - const featureFlag = step.schema[featureFlagKey]; - return ( - typeof featureFlag !== 'string' || featureFlagApi.isActive(featureFlag) - ); - }) - .map(step => { - const filteredStep = cloneDeep(step); - const removedPropertyKeys: Array<string> = []; - if (filteredStep.schema.properties) { - filteredStep.schema.properties = Object.fromEntries( - Object.entries(filteredStep.schema.properties).filter( - ([key, value]) => { - if (value[featureFlagKey]) { - if (featureFlagApi.isActive(value[featureFlagKey])) { - return true; - } - - removedPropertyKeys.push(key); - return false; - } - return true; - }, - ), + return useMemo(() => { + if (!manifest) { + return undefined; + } + const filteredSteps = manifest?.steps + .filter(step => { + const featureFlag = step.schema[featureFlagKey]; + return ( + typeof featureFlag !== 'string' || + featureFlagApi.isActive(featureFlag) ); + }) + .map(step => { + const filteredStep = cloneDeep(step); + const removedPropertyKeys: Array<string> = []; + if (filteredStep.schema.properties) { + filteredStep.schema.properties = Object.fromEntries( + Object.entries(filteredStep.schema.properties).filter( + ([key, value]) => { + if (value[featureFlagKey]) { + if (featureFlagApi.isActive(value[featureFlagKey])) { + return true; + } - // remove the feature flag property key from required if they are not active - filteredStep.schema.required = Array.isArray( - filteredStep.schema.required, - ) - ? filteredStep.schema.required?.filter( - r => !removedPropertyKeys.includes(r as string), - ) - : filteredStep.schema.required; - } + removedPropertyKeys.push(key); + return false; + } + return true; + }, + ), + ); - return filteredStep; - }); + // remove the feature flag property key from required if they are not active + filteredStep.schema.required = Array.isArray( + filteredStep.schema.required, + ) + ? filteredStep.schema.required?.filter( + r => !removedPropertyKeys.includes(r as string), + ) + : filteredStep.schema.required; + } - return { ...manifest, steps: filteredSteps }; + return filteredStep; + }); + + return { ...manifest, steps: filteredSteps }; + }, [manifest, featureFlagApi]); }; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 82b8029637..a6e0c7d497 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,125 @@ # @backstage/plugin-scaffolder +## 1.33.0-next.2 + +### Minor Changes + +- c1ce316: BREAKING `/alpha`: Converted `scaffolder.task.read` and `scaffolder.task.cancel` into Resource Permissions. + + BREAKING `/alpha`: Added a new scaffolder rule `isTaskOwner` for `scaffolder.task.read` and `scaffolder.task.cancel` to allow for conditional permission policies such as restricting access to tasks and task events based on task creators. + + BREAKING `/alpha`: Retrying a task now requires both `scaffolder.task.read` and `scaffolder.task.create` permissions, replacing the previous requirement of `scaffolder.task.read` and `scaffolder.task.cancel`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-react@1.18.0-next.2 + - @backstage/core-components@0.17.4-next.2 + - @backstage/plugin-scaffolder-common@1.6.0-next.1 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + +## 1.32.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-permission-react@0.4.36-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-scaffolder-common@1.5.12-next.0 + - @backstage/plugin-scaffolder-react@1.17.1-next.1 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.32.1-next.0 + +### Patch Changes + +- 289e4a1: Filter MultiEntityPicker options based on rendered option value +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-scaffolder-react@1.17.1-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 1.32.0 + +### Minor Changes + +- 6c972fe: Added information about the `entityRef` and `taskId` to the analytics events whenever is possible. +- 3c59ece: **New Frontend System Only:** + The `Scaffolder` plugin is now responsible for providing an entity icon link extension to launch templates from the catalog entity page. + +### Patch Changes + +- d781b33: render details for composite property schemas +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- b00c160: Remove React import form notification and scaffolder plugin +- 95a1d72: Added appropriate message when global templating function metadata is absent. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-scaffolder-react@1.17.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/integration-react@1.2.8 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.35 + - @backstage/plugin-scaffolder-common@1.5.11 + +## 1.32.0-next.2 + +### Minor Changes + +- 3c59ece: **New Frontend System Only:** + The `Scaffolder` plugin is now responsible for providing an entity icon link extension to launch templates from the catalog entity page. + +### Patch Changes + +- b00c160: Remove React import form notification and scaffolder plugin +- 95a1d72: Added appropriate message when global templating function metadata is absent. +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-scaffolder-react@1.16.1-next.2 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/types@1.2.1 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-react@0.4.34 + - @backstage/plugin-scaffolder-common@1.5.11 + ## 1.31.1-next.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index afcb93ff68..41155cd046 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "1.31.1-next.1", + "version": "1.33.0-next.2", "description": "The Backstage plugin that helps you create new things", "backstage": { "role": "frontend-plugin", diff --git a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorToolbarTemplatesMenu.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorToolbarTemplatesMenu.test.tsx index 879f225b4e..0f3237547a 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorToolbarTemplatesMenu.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateEditorPage/TemplateEditorToolbarTemplatesMenu.test.tsx @@ -99,7 +99,6 @@ describe('TemplateEditorToolbarTemplatesMenu', () => { name: 'Publish', action: 'publish:github', input: { - allowedHosts: ['github.com'], description: 'This is ${{ parameters.component_id }}', repoUrl: '${{ parameters.repoUrl }}', }, @@ -205,7 +204,6 @@ describe('TemplateEditorToolbarTemplatesMenu', () => { name: 'Publish', action: 'publish:github', input: { - allowedHosts: ['github.com'], description: 'This is ${{ parameters.component_id }}', repoUrl: '${{ parameters.repoUrl }}', }, diff --git a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx index d3a02931de..6836259dd8 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPage.test.tsx @@ -21,7 +21,7 @@ import { renderInTestApp, TestApiRegistry, } from '@backstage/test-utils'; -import { act, fireEvent } from '@testing-library/react'; +import { fireEvent, waitFor } from '@testing-library/react'; import { ScaffolderApi, scaffolderApiRef, @@ -127,14 +127,10 @@ describe('TemplateWizardPage', () => { }); // Go to the final page - await act(async () => { - fireEvent.click(await findByRole('button', { name: 'Review' })); - }); + fireEvent.click(await findByRole('button', { name: 'Review' })); // Create the software - await act(async () => { - fireEvent.click(await findByRole('button', { name: 'Create' })); - }); + fireEvent.click(await findByRole('button', { name: 'Create' })); // The "Next Step" button should have fired an event expect(analyticsApi.captureEvent).toHaveBeenCalledWith( @@ -148,15 +144,20 @@ describe('TemplateWizardPage', () => { ); // And the "Create" button should have fired an event - expect(analyticsApi.captureEvent).toHaveBeenCalledWith( - expect.objectContaining({ - action: 'create', - subject: 'expected-name', - context: expect.objectContaining({ - entityRef: 'template:default/test', + await waitFor(() => + expect(analyticsApi.captureEvent).toHaveBeenCalledWith( + expect.objectContaining({ + action: 'create', + subject: 'Task has been created', + attributes: { + templateSteps: 1, + }, + context: expect.objectContaining({ + entityRef: 'template:default/test', + }), + value: 120, }), - value: 120, - }), + ), ); }); diff --git a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx index 9a912f265f..c15795de2c 100644 --- a/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx +++ b/plugins/scaffolder/src/alpha/components/TemplateWizardPage/TemplateWizardPageContextMenu.tsx @@ -26,6 +26,7 @@ import MoreVert from '@material-ui/icons/MoreVert'; import { SyntheticEvent, useState } from 'react'; import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../../translation'; +import { Link } from '@backstage/core-components'; const useStyles = makeStyles(theme => ({ button: { @@ -82,7 +83,7 @@ export function TemplateWizardPageContextMenu( transformOrigin={{ vertical: 'top', horizontal: 'right' }} > <MenuList> - <MenuItem onClick={() => window.open(editUrl, '_blank')}> + <MenuItem component={Link} to={editUrl}> <ListItemIcon> <Edit fontSize="small" /> </ListItemIcon> diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index 6d48cec125..d6b5b7116e 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -91,14 +91,22 @@ const ListTaskPageContent = (props: MyTaskPageProps) => { if (error) { return ( - <> - <ErrorPanel error={error} /> - <EmptyState - missing="info" - title={t('listTaskPage.content.emptyState.title')} - description={t('listTaskPage.content.emptyState.description')} - /> - </> + <CatalogFilterLayout> + <CatalogFilterLayout.Filters> + <OwnerListPicker + filter={ownerFilter} + onSelectOwner={id => setOwnerFilter(id)} + /> + </CatalogFilterLayout.Filters> + <CatalogFilterLayout.Content> + <ErrorPanel error={error} /> + <EmptyState + missing="info" + title={t('listTaskPage.content.emptyState.title')} + description={t('listTaskPage.content.emptyState.description')} + /> + </CatalogFilterLayout.Content> + </CatalogFilterLayout> ); } diff --git a/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx b/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx index 704d39a8fd..2df1fc4ddb 100644 --- a/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/ContextMenu.tsx @@ -21,7 +21,6 @@ import MenuItem from '@material-ui/core/MenuItem'; import MenuList from '@material-ui/core/MenuList'; import Popover from '@material-ui/core/Popover'; import { makeStyles, Theme, useTheme } from '@material-ui/core/styles'; -import { useAsync } from '@react-hookz/web'; import Cancel from '@material-ui/icons/Cancel'; import Repeat from '@material-ui/icons/Repeat'; import Replay from '@material-ui/icons/Replay'; @@ -29,11 +28,8 @@ import Toc from '@material-ui/icons/Toc'; import ControlPointIcon from '@material-ui/icons/ControlPoint'; import MoreVert from '@material-ui/icons/MoreVert'; import { SyntheticEvent, useState } from 'react'; -import { useAnalytics, useApi } from '@backstage/core-plugin-api'; -import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; import { usePermission } from '@backstage/plugin-permission-react'; import { - taskCancelPermission, taskReadPermission, taskCreatePermission, } from '@backstage/plugin-scaffolder-common/alpha'; @@ -51,6 +47,8 @@ type ContextMenuProps = { onToggleLogs?: (state: boolean) => void; onToggleButtonBar?: (state: boolean) => void; taskId?: string; + isCancelButtonDisabled: boolean; + onCancel: () => void; }; const useStyles = makeStyles<Theme, { fontColor: string }>(() => ({ @@ -75,24 +73,12 @@ export const ContextMenu = (props: ContextMenuProps) => { const { getPageTheme } = useTheme(); const pageTheme = getPageTheme({ themeId: 'website' }); const classes = useStyles({ fontColor: pageTheme.fontColor }); - const scaffolderApi = useApi(scaffolderApiRef); - const analytics = useAnalytics(); const [anchorEl, setAnchorEl] = useState<HTMLButtonElement>(); const { t } = useTranslationRef(scaffolderTranslationRef); - const [{ status: cancelStatus }, { execute: cancel }] = useAsync(async () => { - if (taskId) { - analytics.captureEvent('cancelled', 'Template has been cancelled'); - await scaffolderApi.cancelTask(taskId); - } - }); - - const { allowed: canCancelTask } = usePermission({ - permission: taskCancelPermission, - }); - const { allowed: canReadTask } = usePermission({ permission: taskReadPermission, + resourceRef: taskId, }); const { allowed: canCreateTask } = usePermission({ @@ -171,12 +157,8 @@ export const ContextMenu = (props: ContextMenuProps) => { </MenuItem> )} <MenuItem - onClick={cancel} - disabled={ - !cancelEnabled || - cancelStatus !== 'not-executed' || - !canCancelTask - } + onClick={props.onCancel} + disabled={props.isCancelButtonDisabled} data-testid="cancel-task" > <ListItemIcon> diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx index a576b1a9bc..5e44d5cf43 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.test.tsx @@ -161,20 +161,43 @@ describe('OngoingTask', () => { await expect(rendered.findByText('Hide Logs')).resolves.toBeInTheDocument(); }); - it('should have cancel and start over buttons be disabled without the proper permissions', async () => { + it('should have cancel button be disabled when user has read permission but lacks cancel permission', async () => { const permissionApi = mockApis.permission({ - authorize: AuthorizeResult.DENY, + authorize: request => { + if (request.permission.name === 'scaffolder.task.cancel') { + return AuthorizeResult.DENY; + } + return AuthorizeResult.ALLOW; + }, }); const rendered = await render(permissionApi); const { getByTestId } = rendered; expect(getByTestId('cancel-button')).toHaveClass('Mui-disabled'); - expect(getByTestId('start-over-button')).toHaveClass('Mui-disabled'); await act(async () => { fireEvent.click(getByTestId('menu-button')); }); expect(getByTestId('cancel-task')).toHaveClass('Mui-disabled'); - expect(getByTestId('start-over-task')).toHaveClass('Mui-disabled'); + }); + + it('should have start over button be disabled when user has read permission but lacks create permission', async () => { + const permissionApi = mockApis.permission({ + authorize: request => { + if (request.permission.name === 'scaffolder.task.create') { + return AuthorizeResult.DENY; + } + return AuthorizeResult.ALLOW; + }, + }); + const rendered = await render(permissionApi); + + const { getByTestId } = rendered; + expect(getByTestId('start-over-button')).toHaveClass('Mui-disabled'); + + await act(async () => { + fireEvent.click(getByTestId('menu-button')); + }); + expect(getByTestId('start-over-button')).toHaveClass('Mui-disabled'); }); }); diff --git a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx index b718a257b6..bc0d5fe1aa 100644 --- a/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx +++ b/plugins/scaffolder/src/components/OngoingTask/OngoingTask.tsx @@ -32,7 +32,12 @@ import { useTaskEventStream, } from '@backstage/plugin-scaffolder-react'; import { selectedTemplateRouteRef } from '../../routes'; -import { useAnalytics, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { + AnalyticsContext, + useAnalytics, + useApi, + useRouteRef, +} from '@backstage/core-plugin-api'; import qs from 'qs'; import { ContextMenu } from './ContextMenu'; import { @@ -51,6 +56,7 @@ import { useTranslationRef } from '@backstage/core-plugin-api/alpha'; import { scaffolderTranslationRef } from '../../translation'; import { entityPresentationApiRef } from '@backstage/plugin-catalog-react'; import { default as reactUseAsync } from 'react-use/esm/useAsync'; +import { stringifyEntityRef } from '@backstage/catalog-model'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -82,6 +88,36 @@ export const OngoingTask = (props: { }>; }) => { // todo(blam): check that task Id actually exists, and that it's valid. otherwise redirect to something more useful. + const { taskId } = useParams(); + const taskStream = useTaskEventStream(taskId!); + const { namespace, name } = + taskStream.task?.spec.templateInfo?.entity?.metadata ?? {}; + + return ( + <AnalyticsContext + attributes={{ + entityRef: + name && + stringifyEntityRef({ + kind: 'template', + namespace, + name, + }), + taskId, + }} + > + <Page themeId="website"> + <OngoingTaskContent {...props} /> + </Page> + </AnalyticsContext> + ); +}; + +function OngoingTaskContent(props: { + TemplateOutputsComponent?: ComponentType<{ + output?: ScaffolderTaskOutput; + }>; +}) { const { taskId } = useParams(); const templateRouteRef = useRouteRef(selectedTemplateRouteRef); const navigate = useNavigate(); @@ -103,13 +139,14 @@ export const OngoingTask = (props: { const [logsVisible, setLogVisibleState] = useState(false); const [buttonBarVisible, setButtonBarVisibleState] = useState(true); - // Used dummy string value for `resourceRef` since `allowed` field will always return `false` if `resourceRef` is `undefined` const { allowed: canCancelTask } = usePermission({ permission: taskCancelPermission, + resourceRef: taskId, }); const { allowed: canReadTask } = usePermission({ permission: taskReadPermission, + resourceRef: taskId, }); const { allowed: canCreateTask } = usePermission({ @@ -183,7 +220,7 @@ export const OngoingTask = (props: { templateRouteRef, ]); - const [{ status: _ }, { execute: triggerRetry }] = useAsync(async () => { + const [, { execute: triggerRetry }] = useAsync(async () => { if (taskId) { analytics.captureEvent('retried', 'Template has been retried'); await scaffolderApi.retry?.(taskId); @@ -202,9 +239,11 @@ export const OngoingTask = (props: { const Outputs = props.TemplateOutputsComponent ?? DefaultTemplateOutputs; const cancelEnabled = !(taskStream.cancelled || taskStream.completed); + const isCancelButtonDisabled = + !cancelEnabled || cancelStatus !== 'not-executed' || !canCancelTask; return ( - <Page themeId="website"> + <> <Header pageTitleOverride={ presentation @@ -232,6 +271,8 @@ export const OngoingTask = (props: { onToggleLogs={setLogVisibleState} onToggleButtonBar={setButtonBarVisibleState} taskId={taskId} + onCancel={triggerCancel} + isCancelButtonDisabled={isCancelButtonDisabled} /> </Header> <Content className={classes.contentWrapper}> @@ -316,6 +357,6 @@ export const OngoingTask = (props: { </Paper> ) : null} </Content> - </Page> + </> ); -}; +} diff --git a/plugins/scaffolder/src/components/Router/Router.tsx b/plugins/scaffolder/src/components/Router/Router.tsx index 91567c1ddd..d8dee73849 100644 --- a/plugins/scaffolder/src/components/Router/Router.tsx +++ b/plugins/scaffolder/src/components/Router/Router.tsx @@ -60,10 +60,7 @@ import { CustomFieldsPage, } from '../../alpha/components/TemplateEditorPage'; import { RequirePermission } from '@backstage/plugin-permission-react'; -import { - taskReadPermission, - templateManagementPermission, -} from '@backstage/plugin-scaffolder-common/alpha'; +import { templateManagementPermission } from '@backstage/plugin-scaffolder-common/alpha'; import { useApp } from '@backstage/core-plugin-api'; import { FormField, OpaqueFormField } from '@internal/scaffolder'; import { useAsync, useMountEffect } from '@react-hookz/web'; @@ -182,11 +179,9 @@ export const InternalRouter = ( <Route path={scaffolderTaskRouteRef.path} element={ - <RequirePermission permission={taskReadPermission}> - <TaskPageComponent - TemplateOutputsComponent={TemplateOutputsComponent} - /> - </RequirePermission> + <TaskPageComponent + TemplateOutputsComponent={TemplateOutputsComponent} + /> } /> <Route @@ -230,11 +225,7 @@ export const InternalRouter = ( /> <Route path={scaffolderListTaskRouteRef.path} - element={ - <RequirePermission permission={taskReadPermission}> - <ListTasksPage contextMenu={props.contextMenu} /> - </RequirePermission> - } + element={<ListTasksPage contextMenu={props.contextMenu} />} /> <Route path={editorRouteRef.path} diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx index acae3418b9..a02d793a34 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.test.tsx @@ -947,4 +947,81 @@ describe('<MultiEntityPicker />', () => { expect(getByText(description.fromUiSchema)).toBeInTheDocument(); }); }); + + describe('entity presentation', () => { + beforeEach(() => { + uiSchema = { + 'ui:options': { + defaultKind: 'Group', + }, + }; + props = { + onChange, + schema, + required, + uiSchema, + rawErrors, + formData, + } as unknown as FieldProps<any>; + }); + + it('renders and filters selection displayName', async () => { + catalogApi.getEntities.mockResolvedValue({ + items: entities.map(item => ({ + ...item, + spec: { + profile: { displayName: item.metadata.name.replace('-', ' ') }, + }, + })), + }); + + const { getByRole, getByText } = await renderInTestApp( + <Wrapper> + <MultiEntityPicker {...props} /> + </Wrapper>, + ); + + const input = getByRole('textbox'); + + fireEvent.change(input, { target: { value: 'team a' } }); + + expect(getByText('team a')).toBeInTheDocument(); + + fireEvent.change(input, { target: { value: 'squad b' } }); + + expect(getByText('squad b')).toBeInTheDocument(); + + fireEvent.blur(input); + }); + + it('renders and filters selection title', async () => { + catalogApi.getEntities.mockResolvedValue({ + items: entities.map(item => ({ + ...item, + metadata: { + ...item.metadata, + title: item.metadata.name.replace('-', ' ').toUpperCase(), + }, + })), + }); + + const { getByRole, getByText } = await renderInTestApp( + <Wrapper> + <MultiEntityPicker {...props} /> + </Wrapper>, + ); + + const input = getByRole('textbox'); + + fireEvent.change(input, { target: { value: 'team a' } }); + + expect(getByText('TEAM A')).toBeInTheDocument(); + + fireEvent.change(input, { target: { value: 'squad b' } }); + + expect(getByText('SQUAD B')).toBeInTheDocument(); + + fireEvent.blur(input); + }); + }); }); diff --git a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx index e7dcf0f996..57c4b87d6d 100644 --- a/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx +++ b/plugins/scaffolder/src/components/fields/MultiEntityPicker/MultiEntityPicker.tsx @@ -32,6 +32,7 @@ import { import TextField from '@material-ui/core/TextField'; import Autocomplete, { AutocompleteChangeReason, + createFilterOptions, } from '@material-ui/lab/Autocomplete'; import { useCallback, useEffect, useState } from 'react'; import useAsync from 'react-use/esm/useAsync'; @@ -203,6 +204,11 @@ export const MultiEntityPicker = (props: MultiEntityPickerProps) => { }} /> )} + filterOptions={createFilterOptions<Entity>({ + stringify: option => + entities?.entityRefToPresentation.get(stringifyEntityRef(option)) + ?.primaryTitle!, + })} ListboxComponent={VirtualizedListbox} /> </ScaffolderField> diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index 71c11e8986..2c392bc597 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,36 @@ # @backstage/plugin-search-backend-module-catalog +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 0.3.5 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + ## 0.3.5-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 12f3a1001c..f06a14f431 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-catalog", - "version": "0.3.5-next.1", + "version": "0.3.6-next.0", "description": "A module for the search backend that exports catalog modules", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index b51ab568bd..c196160885 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.7.4-next.0 + +### Patch Changes + +- 3507fcd: Just some more circular dep cleanup +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 1.7.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + ## 1.7.3-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index a989e08cd9..127e6a0008 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", - "version": "1.7.3-next.1", + "version": "1.7.4-next.0", "description": "A module for the search backend that implements search using ElasticSearch", "backstage": { "role": "backend-plugin-module", diff --git a/plugins/search-backend-module-elasticsearch/report.api.md b/plugins/search-backend-module-elasticsearch/report.api.md index 9250cfda31..19c44bd566 100644 --- a/plugins/search-backend-module-elasticsearch/report.api.md +++ b/plugins/search-backend-module-elasticsearch/report.api.md @@ -11,7 +11,6 @@ import { BulkHelper } from '@elastic/elasticsearch/lib/Helpers'; import { BulkStats } from '@elastic/elasticsearch/lib/Helpers'; import { Config } from '@backstage/config'; import type { ConnectionOptions } from 'tls'; -import { ElasticSearchQueryTranslator as ElasticSearchQueryTranslator_2 } from '@backstage/plugin-search-backend-module-elasticsearch'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { IndexableResultSet } from '@backstage/plugin-search-common'; @@ -332,7 +331,7 @@ export type ElasticSearchQueryTranslator = ( // @public (undocumented) export interface ElasticSearchQueryTranslatorExtensionPoint { // (undocumented) - setTranslator(translator: ElasticSearchQueryTranslator_2): void; + setTranslator(translator: ElasticSearchQueryTranslator): void; } // @public diff --git a/plugins/search-backend-module-elasticsearch/src/module.ts b/plugins/search-backend-module-elasticsearch/src/module.ts index c5c2a6fc1d..d4ce249093 100644 --- a/plugins/search-backend-module-elasticsearch/src/module.ts +++ b/plugins/search-backend-module-elasticsearch/src/module.ts @@ -22,7 +22,7 @@ import { searchEngineRegistryExtensionPoint } from '@backstage/plugin-search-bac import { ElasticSearchQueryTranslator, ElasticSearchSearchEngine, -} from '@backstage/plugin-search-backend-module-elasticsearch'; +} from './engines/ElasticSearchSearchEngine'; /** @public */ export interface ElasticSearchQueryTranslatorExtensionPoint { diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index 2b34facfe7..6b40d45b41 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-search-backend-module-explore +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index b6c1f5aae3..90f4cdfa02 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.3-next.1", + "version": "0.3.4-next.0", "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 8334585cf9..f426315db8 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.46-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 0.5.45 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + ## 0.5.45-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 25533d1ad2..2222ba4c44 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.45-next.1", + "version": "0.5.46-next.0", "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 f0a9051be9..f8e330fcbc 100644 --- a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-search-backend-module-stack-overflow-collator +## 0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 0.3.10 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + ## 0.3.10-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 01f1389f35..24377e5ede 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.10-next.1", + "version": "0.3.11-next.0", "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 1f72c806e9..fd87d8e8aa 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.4.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-techdocs-node@1.13.5-next.1 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-techdocs-node@1.13.5-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-common@1.2.18 + +## 0.4.3 + +### Patch Changes + +- b47ec38: Exports the default document collator for use in document transformation during search indexing. +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-node@1.13.4 + ## 0.4.3-next.1 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index 3cec11b059..56265b5574 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.3-next.1", + "version": "0.4.4-next.1", "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 fe9bcce037..bbd1884d56 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-search-backend-node +## 1.3.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 1.3.12 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-common@1.2.18 + ## 1.3.12-next.1 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index b54fc5b403..90a08594bd 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.12-next.1", + "version": "1.3.13-next.0", "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 5117375cb3..f74053ab02 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,69 @@ # @backstage/plugin-search-backend +## 2.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/plugin-permission-node@0.10.2-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-node@1.3.13-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/backend-openapi-utils@0.5.5-next.0 + +## 2.0.4-next.0 + +### Patch Changes + +- 69fb975: Error messages should not contain backend SQL query strings in the API response, this change will ensure that messages are logged and empty response is returned to the user +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-common@1.2.18 + +## 2.0.3 + +### Patch Changes + +- c83cd8b: Fixed some circular or otherwise unclear imports +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/backend-openapi-utils@0.5.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1 + - @backstage/plugin-search-backend-node@1.3.12 + - @backstage/plugin-search-common@1.2.18 + +## 2.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-openapi-utils@0.5.4-next.1 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-permission-node@0.10.1-next.1 + - @backstage/plugin-search-backend-node@1.3.12-next.1 + - @backstage/plugin-search-common@1.2.18 + ## 2.0.3-next.1 ### Patch Changes diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 5226e3529f..4af04fbc48 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.3-next.1", + "version": "2.0.4-next.1", "description": "The Backstage backend plugin that provides your backstage app with search", "backstage": { "role": "backend-plugin", diff --git a/plugins/search-backend/src/schema/openapi/generated/router.ts b/plugins/search-backend/src/schema/openapi/generated/router.ts index cf972d9f6c..a2510346ca 100644 --- a/plugins/search-backend/src/schema/openapi/generated/router.ts +++ b/plugins/search-backend/src/schema/openapi/generated/router.ts @@ -18,7 +18,7 @@ // * THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. * // ****************************************************************** import { createValidatedOpenApiRouterFromGeneratedEndpointMap } from '@backstage/backend-openapi-utils'; -import { EndpointMap } from './'; +import { EndpointMap } from './apis'; export const spec = { openapi: '3.0.3', diff --git a/plugins/search-backend/src/service/router.test.ts b/plugins/search-backend/src/service/router.test.ts index 5aea71b7c1..623b78016a 100644 --- a/plugins/search-backend/src/service/router.test.ts +++ b/plugins/search-backend/src/service/router.test.ts @@ -106,7 +106,7 @@ describe('createRouter', () => { expect.objectContaining({ error: { name: 'Error', - message: `There was a problem performing the search query: ${error.message}`, + message: `There was a problem performing the search query`, }, }), ); diff --git a/plugins/search-backend/src/service/router.ts b/plugins/search-backend/src/service/router.ts index 0ba5eede81..eedd84105a 100644 --- a/plugins/search-backend/src/service/router.ts +++ b/plugins/search-backend/src/service/router.ts @@ -197,14 +197,17 @@ export async function createRouter( res.json(filterResultSet(toSearchResults(resultSet))); } catch (error) { + // Log the error message here, but don't expose it to the user in the response + logger.error( + `There was a problem performing the search query: ${error.message}`, + ); if (error.name === 'MissingIndexError') { // re-throw and let the default error handler middleware captures it and serializes it with the right response code on the standard form throw error; } - throw new Error( - `There was a problem performing the search query: ${error.message}`, - ); + // If the error is not a MissingIndexError, we want to throw a generic error without the error message as it may leak internal information + throw new Error(`There was a problem performing the search query`); } }); diff --git a/plugins/search-common/CHANGELOG.md b/plugins/search-common/CHANGELOG.md index e6f08e2183..e80fc1a2e4 100644 --- a/plugins/search-common/CHANGELOG.md +++ b/plugins/search-common/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-search-common +## 1.2.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-permission-common@0.9.1-next.0 + ## 1.2.18 ### Patch Changes diff --git a/plugins/search-common/package.json b/plugins/search-common/package.json index 1c68dd7762..a7d25e2de3 100644 --- a/plugins/search-common/package.json +++ b/plugins/search-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-common", - "version": "1.2.18", + "version": "1.2.19-next.0", "description": "Common functionalities for Search, to be shared between various search-enabled plugins", "backstage": { "role": "common-library", diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index da0a5e1c0f..5e5020b501 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,56 @@ # @backstage/plugin-search-react +## 1.9.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.9.2-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + +## 1.9.1 + +### Patch Changes + +- 869fa46: SearchBar clear button support i18n +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + +## 1.9.1-next.1 + +### Patch Changes + +- 869fa46: SearchBar clear button support i18n +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + ## 1.9.1-next.0 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index f157471d76..74b663c860 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.1-next.0", + "version": "1.9.2-next.1", "backstage": { "role": "web-library", "pluginId": "search", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 9b9f4a532d..d331fcb630 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,67 @@ # @backstage/plugin-search +## 1.4.28-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.4.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-search-react@1.9.2-next.0 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + +## 1.4.27 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/plugin-search-react@1.9.1 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + +## 1.4.27-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-search-react@1.9.1-next.1 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-search-common@1.2.18 + ## 1.4.27-next.1 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index ca31eb1244..c92665f251 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "1.4.27-next.1", + "version": "1.4.28-next.1", "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 3f09417863..debecec616 100644 --- a/plugins/signals-backend/CHANGELOG.md +++ b/plugins/signals-backend/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/plugin-signals-backend +## 0.3.6-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-signals-node@0.1.22-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.3.5 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + - @backstage/plugin-signals-node@0.1.21 + ## 0.3.5-next.1 ### Patch Changes diff --git a/plugins/signals-backend/package.json b/plugins/signals-backend/package.json index cb9146c9c4..27e1ab15fd 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.5-next.1", + "version": "0.3.6-next.0", "backstage": { "role": "backend-plugin", "pluginId": "signals", diff --git a/plugins/signals-node/CHANGELOG.md b/plugins/signals-node/CHANGELOG.md index 8e753081d8..85c6d20a20 100644 --- a/plugins/signals-node/CHANGELOG.md +++ b/plugins/signals-node/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-signals-node +## 0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-events-node@0.4.13-next.0 + +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/config@1.3.2 + - @backstage/types@1.2.1 + - @backstage/plugin-events-node@0.4.12 + ## 0.1.21-next.1 ### Patch Changes diff --git a/plugins/signals-node/package.json b/plugins/signals-node/package.json index 4cb5c08f79..4190c9d848 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.21-next.1", + "version": "0.1.22-next.0", "description": "Node.js library for the signals plugin", "backstage": { "role": "node-library", diff --git a/plugins/signals-react/CHANGELOG.md b/plugins/signals-react/CHANGELOG.md index 43aee22b12..bc4a0a2ccc 100644 --- a/plugins/signals-react/CHANGELOG.md +++ b/plugins/signals-react/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-signals-react +## 0.0.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.0.14 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.10.8 + - @backstage/types@1.2.1 + ## 0.0.13 ### Patch Changes diff --git a/plugins/signals-react/package.json b/plugins/signals-react/package.json index d1fa5f9392..e47d288e56 100644 --- a/plugins/signals-react/package.json +++ b/plugins/signals-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals-react", - "version": "0.0.13", + "version": "0.0.15-next.0", "description": "Web library for the signals plugin", "backstage": { "role": "web-library", diff --git a/plugins/signals/CHANGELOG.md b/plugins/signals/CHANGELOG.md index db16d0ade5..64fdd3a94c 100644 --- a/plugins/signals/CHANGELOG.md +++ b/plugins/signals/CHANGELOG.md @@ -1,5 +1,63 @@ # @backstage/plugin-signals +## 0.0.21-next.2 + +### Patch Changes + +- d52d67f: Added a New Frontend System App Root Element for the `<SignalsDisplay />` component +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + +## 0.0.21-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-signals-react@0.0.15-next.0 + +## 0.0.21-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.14 + +## 0.0.20 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.14 + +## 0.0.20-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/core-plugin-api@1.10.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.13 + ## 0.0.20-next.0 ### Patch Changes diff --git a/plugins/signals/package.json b/plugins/signals/package.json index 5a5c17a82f..f3cd860c80 100644 --- a/plugins/signals/package.json +++ b/plugins/signals/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-signals", - "version": "0.0.20-next.0", + "version": "0.0.21-next.2", "backstage": { "role": "frontend-plugin", "pluginId": "signals", @@ -51,6 +51,7 @@ "test": "backstage-cli package test" }, "dependencies": { + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/frontend-plugin-api": "workspace:^", diff --git a/plugins/signals/report-alpha.api.md b/plugins/signals/report-alpha.api.md index 45ee0da3b8..3fc89308c1 100644 --- a/plugins/signals/report-alpha.api.md +++ b/plugins/signals/report-alpha.api.md @@ -7,6 +7,7 @@ import { AnyApiFactory } from '@backstage/frontend-plugin-api'; import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { ExtensionDefinition } from '@backstage/frontend-plugin-api'; import { FrontendPlugin } from '@backstage/frontend-plugin-api'; +import { JSX as JSX_2 } from 'react'; // @alpha (undocumented) const _default: FrontendPlugin< @@ -28,6 +29,21 @@ const _default: FrontendPlugin< factory: AnyApiFactory; }; }>; + 'app-root-element:signals/signals-display': ExtensionDefinition<{ + kind: 'app-root-element'; + name: 'signals-display'; + config: {}; + configInput: {}; + output: ConfigurableExtensionDataRef< + JSX_2.Element, + 'core.reactElement', + {} + >; + inputs: {}; + params: { + element: JSX.Element | (() => JSX.Element); + }; + }>; } >; export default _default; diff --git a/plugins/signals/src/alpha.tsx b/plugins/signals/src/alpha.tsx index 90b78b05fd..38955473e1 100644 --- a/plugins/signals/src/alpha.tsx +++ b/plugins/signals/src/alpha.tsx @@ -16,6 +16,7 @@ import { ApiBlueprint, + AppRootElementBlueprint, createApiFactory, createFrontendPlugin, discoveryApiRef, @@ -23,6 +24,8 @@ import { } from '@backstage/frontend-plugin-api'; import { signalApiRef } from '@backstage/plugin-signals-react'; import { SignalClient } from './api/SignalClient'; +import { SignalsDisplay } from './plugin'; +import { compatWrapper } from '@backstage/core-compat-api'; const api = ApiBlueprint.make({ params: { @@ -42,9 +45,16 @@ const api = ApiBlueprint.make({ }, }); +const signalsDisplayAppRootElement = AppRootElementBlueprint.make({ + name: 'signals-display', + params: { + element: compatWrapper(<SignalsDisplay />), + }, +}); + /** @alpha */ export default createFrontendPlugin({ pluginId: 'signals', info: { packageJson: () => import('../package.json') }, - extensions: [api], + extensions: [api, signalsDisplayAppRootElement], }); diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index be8b0d5920..22069284f4 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,78 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.51-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/test-utils@1.7.10-next.2 + - @backstage/plugin-catalog@1.31.1-next.2 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + - @backstage/plugin-techdocs@1.13.2-next.1 + +## 1.0.51-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/test-utils@1.7.10-next.1 + - @backstage/plugin-techdocs@1.13.2-next.1 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/plugin-catalog@1.31.1-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + +## 1.0.51-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs@1.13.2-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/plugin-catalog@1.31.1-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/test-utils@1.7.10-next.0 + - @backstage/plugin-search-react@1.9.2-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/core-app-api@1.17.1 + - @backstage/core-plugin-api@1.10.8 + +## 1.0.49 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs@1.13.0 + - @backstage/plugin-catalog@1.31.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/plugin-search-react@1.9.1 + - @backstage/integration-react@1.2.8 + - @backstage/core-app-api@1.17.1 + - @backstage/test-utils@1.7.9 + +## 1.0.49-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog@1.31.0-next.2 + - @backstage/plugin-techdocs@1.13.0-next.2 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-search-react@1.9.1-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/core-app-api@1.17.0 + - @backstage/core-plugin-api@1.10.7 + - @backstage/test-utils@1.7.8 + ## 1.0.49-next.1 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index 668f2bea88..92d55d5212 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.49-next.1", + "version": "1.0.51-next.2", "backstage": { "role": "web-library", "pluginId": "techdocs-addons", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index a24b2477ca..130a13ed7b 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,79 @@ # @backstage/plugin-techdocs-backend +## 2.0.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/plugin-permission-common@0.9.1-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/integration@1.17.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.4-next.1 + - @backstage/plugin-techdocs-node@1.13.5-next.1 + - @backstage/plugin-catalog-common@1.1.5-next.0 + - @backstage/plugin-catalog-node@1.17.2-next.0 + +## 2.0.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.4-next.0 + - @backstage/plugin-techdocs-node@1.13.5-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-techdocs-common@0.1.1 + +## 2.0.3 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-client@1.10.1 + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-catalog-node@1.17.1 + - @backstage/plugin-search-backend-module-techdocs@0.4.3 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-techdocs-node@1.13.4 + +## 2.0.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/plugin-catalog-common@1.1.4 + - @backstage/plugin-catalog-node@1.17.1-next.1 + - @backstage/plugin-permission-common@0.9.0 + - @backstage/plugin-search-backend-module-techdocs@0.4.3-next.1 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + - @backstage/plugin-techdocs-node@1.13.4-next.1 + ## 2.0.3-next.1 ### Patch Changes diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index afb3694630..62e150d519 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.3-next.1", + "version": "2.0.4-next.1", "description": "The Backstage backend plugin that renders technical documentation for your components", "backstage": { "role": "backend-plugin", diff --git a/plugins/techdocs-common/CHANGELOG.md b/plugins/techdocs-common/CHANGELOG.md index bf2a3300ea..7e2568ab78 100644 --- a/plugins/techdocs-common/CHANGELOG.md +++ b/plugins/techdocs-common/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-techdocs-common +## 0.1.1 + +### Patch Changes + +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + ## 0.1.1-next.0 ### Patch Changes diff --git a/plugins/techdocs-common/package.json b/plugins/techdocs-common/package.json index ef0a6c613e..1f43a48f13 100644 --- a/plugins/techdocs-common/package.json +++ b/plugins/techdocs-common/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-common", - "version": "0.1.1-next.0", + "version": "0.1.1", "description": "Common functionality for TechDocs", "backstage": { "role": "common-library", diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 978d09e195..272080260d 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.26-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + +## 1.1.25 + +### Patch Changes + +- 9dde3ba: Improved Keyboard accessibility in techdocs. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/integration-react@1.2.8 + - @backstage/integration@1.17.0 + +## 1.1.25-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/core-plugin-api@1.10.7 + - @backstage/integration@1.17.0 + ## 1.1.25-next.1 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index b0bbd9c36f..e48efba4f2 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.25-next.1", + "version": "1.1.26-next.1", "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 85a11b495c..a0ed155482 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,51 @@ # @backstage/plugin-techdocs-node +## 1.13.5-next.2 + +### Patch Changes + +- 029526c: Updated the error message thrown by parseReferenceAnnotation to reflect the annotation value passed as an argument rather than in correctly assuming location. + +## 1.13.5-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/integration-aws-node@0.1.17-next.0 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-search-common@1.2.19-next.0 + +## 1.13.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.13.4 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/integration-aws-node@0.1.16 + - @backstage/plugin-search-common@1.2.18 + ## 1.13.4-next.1 ### Patch Changes diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 25aaa92014..d406a12457 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.4-next.1", + "version": "1.13.5-next.2", "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-node/src/helpers.test.ts b/plugins/techdocs-node/src/helpers.test.ts index 33f4018f47..aa1aa69d30 100644 --- a/plugins/techdocs-node/src/helpers.test.ts +++ b/plugins/techdocs-node/src/helpers.test.ts @@ -113,9 +113,12 @@ describe('parseReferenceAnnotation', () => { }); it('should throw error without annotation', () => { + const logMsgRegex = new RegExp( + `No ${TECHDOCS_ANNOTATION} annotation provided`, + ); expect(() => { parseReferenceAnnotation(TECHDOCS_ANNOTATION, entityBase); - }).toThrow(/No location annotation/); + }).toThrow(logMsgRegex); }); it('should throw error with bad annotation', () => { diff --git a/plugins/techdocs-node/src/helpers.ts b/plugins/techdocs-node/src/helpers.ts index 67bb862c62..f1655aa9a6 100644 --- a/plugins/techdocs-node/src/helpers.ts +++ b/plugins/techdocs-node/src/helpers.ts @@ -52,7 +52,7 @@ export const parseReferenceAnnotation = ( const annotation = entity.metadata.annotations?.[annotationName]; if (!annotation) { throw new InputError( - `No location annotation provided in entity: ${entity.metadata.name}`, + `No ${annotationName} annotation provided in entity: ${entity.metadata.name}`, ); } diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 5357f4c90d..213bafc1a3 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,59 @@ # @backstage/plugin-techdocs-react +## 1.3.1-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/frontend-plugin-api@0.10.4-next.1 + +## 1.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.3.0 + +### Minor Changes + +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/version-bridge@1.0.11 + +## 1.3.0-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/version-bridge@1.0.11 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + ## 1.3.0-next.0 ### Minor Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 618af87698..9cc593306b 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-react", - "version": "1.3.0-next.0", + "version": "1.3.1-next.1", "description": "Shared frontend utilities for TechDocs and Addons", "backstage": { "role": "web-library", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 9b70f65408..dc1df80c42 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,108 @@ # @backstage/plugin-techdocs +## 1.13.2-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/config@1.3.3-next.0 + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/catalog-client@1.10.2-next.0 + - @backstage/integration@1.17.1-next.1 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/integration-react@1.2.9-next.1 + - @backstage/plugin-techdocs-react@1.3.1-next.1 + - @backstage/plugin-search-common@1.2.19-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/plugin-search-react@1.9.2-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-auth-react@0.1.17-next.1 + +## 1.13.2-next.0 + +### Patch Changes + +- 1debf7f: Fixed an issue causing TechDocs to not properly handle initial redirect. +- Updated dependencies + - @backstage/integration@1.17.1-next.0 + - @backstage/integration-react@1.2.9-next.0 + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/plugin-search-react@1.9.2-next.0 + - @backstage/plugin-techdocs-react@1.3.1-next.0 + - @backstage/catalog-client@1.10.1 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/plugin-auth-react@0.1.17-next.0 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1 + +## 1.13.0 + +### Minor Changes + +- 3c59ece: **New Frontend System Only:** + The `TechDocs` plugin is now responsible for providing an entity icon link extension to read documentation from the catalog entity page. +- ec7b35d: Introduced `backstage.io/techdocs-entity-path` annotation which allows deep linking into another entities TechDocs in conjunction with `backstage.io/techdocs-entity`. + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- 9dde3ba: Improved Keyboard accessibility in techdocs. +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/catalog-client@1.10.1 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/plugin-techdocs-react@1.3.0 + - @backstage/plugin-techdocs-common@0.1.1 + - @backstage/plugin-search-react@1.9.1 + - @backstage/integration-react@1.2.8 + - @backstage/plugin-auth-react@0.1.16 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/theme@0.6.6 + - @backstage/plugin-search-common@1.2.18 + +## 1.13.0-next.2 + +### Minor Changes + +- 3c59ece: **New Frontend System Only:** + The `TechDocs` plugin is now responsible for providing an entity icon link extension to read documentation from the catalog entity page. + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/plugin-search-react@1.9.1-next.1 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/integration-react@1.2.7 + - @backstage/plugin-auth-react@0.1.16-next.0 + - @backstage/plugin-techdocs-react@1.3.0-next.1 + - @backstage/catalog-client@1.10.1-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/config@1.3.2 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/integration@1.17.0 + - @backstage/theme@0.6.6 + - @backstage/plugin-search-common@1.2.18 + - @backstage/plugin-techdocs-common@0.1.1-next.0 + ## 1.13.0-next.1 ### Patch Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index c79dadca05..bf5a01c643 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "1.13.0-next.1", + "version": "1.13.2-next.1", "description": "The Backstage plugin that renders technical documentation for your components", "backstage": { "role": "frontend-plugin", diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.test.tsx new file mode 100644 index 0000000000..80b19f0308 --- /dev/null +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.test.tsx @@ -0,0 +1,72 @@ +/* + * 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 { render } from '@testing-library/react'; + +// We need to mock react-router-dom hooks used by useInitialRedirect +import { useLocation, useNavigate, useParams } from 'react-router-dom'; + +// Import the module from which the hook is defined +import { useInitialRedirect } from './dom'; + +jest.mock('react-router-dom', () => ({ + ...jest.requireActual('react-router-dom'), + useLocation: jest.fn(), + useNavigate: jest.fn(), + useParams: jest.fn(), +})); + +describe('useInitialRedirect', () => { + const mockNavigate = jest.fn(); + + beforeEach(() => { + // Reset mocks before each test + mockNavigate.mockReset(); + (useNavigate as jest.Mock).mockReturnValue(mockNavigate); + (useLocation as jest.Mock).mockReturnValue({ + pathname: '/docs/default/Component/backstage-demo', + }); + // Simulate that no current path is provided + (useParams as jest.Mock).mockReturnValue({ '*': '' }); + }); + + const TestComponent: React.FC<{ defaultPath?: string }> = ({ + defaultPath, + }) => { + // Call hook that should trigger a redirect on mount only if defaultPath is a non-empty string. + useInitialRedirect(defaultPath); + return <div>Test</div>; + }; + + it('should not navigate when defaultPath is undefined', () => { + render(<TestComponent defaultPath={undefined} />); + expect(mockNavigate).not.toHaveBeenCalled(); + }); + + it('should navigate when defaultPath is a non-empty string', () => { + render(<TestComponent defaultPath="/overview" />); + expect(mockNavigate).toHaveBeenCalledWith( + '/docs/default/Component/backstage-demo/overview', + { replace: true }, + ); + }); + + it('should not navigate if currPath is non-empty', () => { + // Override useParams to simulate a non-empty currPath + (useParams as jest.Mock).mockReturnValue({ '*': 'existing-path' }); + render(<TestComponent defaultPath={undefined} />); + expect(mockNavigate).not.toHaveBeenCalled(); + }); +}); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index 0c4d6dee3e..35e847a091 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -61,15 +61,13 @@ const MOBILE_MEDIA_QUERY = 'screen and (max-width: 76.1875em)'; // If a defaultPath is specified then we should navigate to that path replacing the // current location in the history. This should only happen on the initial load so // navigating to the root of the docs doesn't also redirect. -const useInitialRedirect = (defaultPath?: string) => { - // const hasRun = useRef(false); - +export const useInitialRedirect = (defaultPath?: string) => { const location = useLocation(); const navigate = useNavigate(); const { '*': currPath = '' } = useParams(); useLayoutEffect(() => { - if (currPath === '' && defaultPath !== '') { + if (currPath === '' && defaultPath) { navigate(`${location.pathname}${defaultPath}`, { replace: true }); } }, []); // eslint-disable-line react-hooks/exhaustive-deps diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 679a2ef2b8..7408a3c6d8 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,54 @@ # @backstage/plugin-user-settings-backend +## 0.3.4-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.1 + - @backstage/backend-plugin-api@1.4.1-next.0 + - @backstage/plugin-auth-node@0.6.5-next.0 + - @backstage/plugin-signals-node@0.1.22-next.0 + +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.1-next.0 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.21 + - @backstage/plugin-user-settings-common@0.0.1 + +## 0.3.3 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0 + - @backstage/plugin-auth-node@0.6.4 + - @backstage/backend-plugin-api@1.4.0 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-node@0.1.21 + - @backstage/plugin-user-settings-common@0.0.1 + +## 0.3.3-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-defaults@0.11.0-next.2 + - @backstage/backend-plugin-api@1.4.0-next.1 + - @backstage/errors@1.2.7 + - @backstage/types@1.2.1 + - @backstage/plugin-auth-node@0.6.4-next.1 + - @backstage/plugin-signals-node@0.1.21-next.1 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.3.3-next.1 ### Patch Changes diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index b5e513ede8..54c499095c 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.3-next.1", + "version": "0.3.4-next.1", "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 bc936a1007..410d1f4725 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,89 @@ # @backstage/plugin-user-settings +## 0.8.24-next.2 + +### Patch Changes + +- aa3b054: Added `signIn` and `signOut` analytic events to the `@backstage/core-components` of sign in and sign out. +- Updated dependencies + - @backstage/theme@0.6.7-next.1 + - @backstage/core-app-api@1.18.0-next.1 + - @backstage/core-components@0.17.4-next.2 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + +## 0.8.24-next.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@1.7.5-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.1 + - @backstage/core-app-api@1.17.2-next.0 + - @backstage/core-components@0.17.4-next.1 + - @backstage/core-plugin-api@1.10.9-next.0 + - @backstage/core-compat-api@0.4.4-next.1 + - @backstage/frontend-plugin-api@0.10.4-next.1 + - @backstage/plugin-signals-react@0.0.15-next.0 + +## 0.8.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/theme@0.6.7-next.0 + - @backstage/plugin-catalog-react@1.19.1-next.0 + - @backstage/core-components@0.17.4-next.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.1 + - @backstage/core-compat-api@0.4.4-next.0 + - @backstage/core-plugin-api@1.10.8 + - @backstage/errors@1.2.7 + - @backstage/frontend-plugin-api@0.10.4-next.0 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.14 + - @backstage/plugin-user-settings-common@0.0.1 + +## 0.8.23 + +### Patch Changes + +- 18c64e9: Added the `info.packageJson` option to the plugin instance for the new frontend system. +- ab6d3dd: Add some translation to user-setting plugin +- c83cd8b: Fixed some circular or otherwise unclear imports +- Updated dependencies + - @backstage/core-components@0.17.3 + - @backstage/core-plugin-api@1.10.8 + - @backstage/frontend-plugin-api@0.10.3 + - @backstage/plugin-catalog-react@1.19.0 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.1 + - @backstage/core-compat-api@0.4.3 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.14 + - @backstage/plugin-user-settings-common@0.0.1 + +## 0.8.23-next.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.17.3-next.0 + - @backstage/plugin-catalog-react@1.19.0-next.2 + - @backstage/frontend-plugin-api@0.10.3-next.1 + - @backstage/catalog-model@1.7.4 + - @backstage/core-app-api@1.17.0 + - @backstage/core-compat-api@0.4.3-next.2 + - @backstage/core-plugin-api@1.10.7 + - @backstage/errors@1.2.7 + - @backstage/theme@0.6.6 + - @backstage/types@1.2.1 + - @backstage/plugin-signals-react@0.0.13 + - @backstage/plugin-user-settings-common@0.0.1 + ## 0.8.23-next.1 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 2acac5406c..fcc5efabc7 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.23-next.1", + "version": "0.8.24-next.2", "description": "A Backstage plugin that provides a settings page", "backstage": { "role": "frontend-plugin", diff --git a/plugins/user-settings/src/components/General/UserSettingsMenu.tsx b/plugins/user-settings/src/components/General/UserSettingsMenu.tsx index 6fb55896e9..dc954e862f 100644 --- a/plugins/user-settings/src/components/General/UserSettingsMenu.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsMenu.tsx @@ -25,6 +25,7 @@ import { identityApiRef, errorApiRef, useApi, + useAnalytics, } from '@backstage/core-plugin-api'; import { useTranslationRef } from '@backstage/frontend-plugin-api'; import { userSettingsTranslationRef } from '../../translation'; @@ -36,6 +37,7 @@ export const UserSettingsMenu = () => { const [open, setOpen] = useState(false); const [anchorEl, setAnchorEl] = useState<undefined | HTMLElement>(undefined); const { t } = useTranslationRef(userSettingsTranslationRef); + const analytics = useAnalytics(); const handleOpen = (event: MouseEvent<HTMLButtonElement>) => { setAnchorEl(event.currentTarget); @@ -59,9 +61,10 @@ export const UserSettingsMenu = () => { <Menu anchorEl={anchorEl} open={open} onClose={handleClose}> <MenuItem data-testid="sign-out" - onClick={() => - identityApi.signOut().catch(error => errorApi.post(error)) - } + onClick={() => { + identityApi.signOut().catch(error => errorApi.post(error)); + analytics.captureEvent('signOut', 'success'); + }} > <ListItemIcon> <SignOutIcon /> diff --git a/plugins/user-settings/src/index.ts b/plugins/user-settings/src/index.ts index bc878ecbb3..e64d3c926c 100644 --- a/plugins/user-settings/src/index.ts +++ b/plugins/user-settings/src/index.ts @@ -26,4 +26,4 @@ export { userSettingsPlugin as plugin, UserSettingsPage, } from './plugin'; -export * from './components/'; +export * from './components'; diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 96cf1e99bb..50401c732b 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -588,14 +588,14 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.20.0": - version: 0.20.0 - resolution: "@eslint/config-array@npm:0.20.0" +"@eslint/config-array@npm:^0.20.1": + version: 0.20.1 + resolution: "@eslint/config-array@npm:0.20.1" dependencies: "@eslint/object-schema": "npm:^2.1.6" debug: "npm:^4.3.1" minimatch: "npm:^3.1.2" - checksum: 10/9db7f6cbb5363f2f98ee4805ce09d1a95c4349e86f3f456f2c23a0849b7a6aa8d2be4c25e376ee182af062762e15a101844881c89b566eea0856c481ffcb2090 + checksum: 10/d72cc90f516c5730da5f37fa04aa8ba26ea0d92c7457ee77980902158f844f3483518272ccfe16f273c3313c3bfec8da713d4e51d3da49bdeccd34e919a2b903 languageName: node linkType: hard @@ -606,12 +606,21 @@ __metadata: languageName: node linkType: hard -"@eslint/core@npm:^0.13.0": - version: 0.13.0 - resolution: "@eslint/core@npm:0.13.0" +"@eslint/core@npm:^0.14.0": + version: 0.14.0 + resolution: "@eslint/core@npm:0.14.0" dependencies: "@types/json-schema": "npm:^7.0.15" - checksum: 10/737fd1c237405b62592e8daa4b7e25b45ab22108bfec65258cabd091d5717b7c9573acea1f27c4ee7198cefc5a0874f5caefe3d9636851227b1f12d28ef52cf2 + checksum: 10/d9b060cf97468150675ddf4fb3db55edaa32467e0adf9f80919a5bfd15d0835ad7765456f4397ec2d16b0a1bb702af63f6d4712f94194d34fea118231ae1e2db + languageName: node + linkType: hard + +"@eslint/core@npm:^0.15.0": + version: 0.15.0 + resolution: "@eslint/core@npm:0.15.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10/27c9cb5bdc5c9dead5b06f2b2a6a66d8bbe5e2e19397e2c5ff9ea582c9d4e4478bf1bc1bdd4eaec7bb3a0d6fa53f152e595acf637354776c14bb58c321ea5aa3 languageName: node linkType: hard @@ -632,10 +641,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.26.0": - version: 9.26.0 - resolution: "@eslint/js@npm:9.26.0" - checksum: 10/863d35df8f6675250bb5a917037e0f6833965437eba4c4649633fd0b55a93e8d727bcd36e9b5cc82047898ee9348cb40363e196f333914ae3a6bb36159495212 +"@eslint/js@npm:9.29.0": + version: 9.29.0 + resolution: "@eslint/js@npm:9.29.0" + checksum: 10/7f7fd586b35bd08537dd65a9bda764f474350c36b4ccbdd342462d1a26be28f7ee0ebd0611dd4762b69829674336ba04c281b9658aeccb3e6ab1d0fec7e6d08c languageName: node linkType: hard @@ -646,13 +655,13 @@ __metadata: languageName: node linkType: hard -"@eslint/plugin-kit@npm:^0.2.8": - version: 0.2.8 - resolution: "@eslint/plugin-kit@npm:0.2.8" +"@eslint/plugin-kit@npm:^0.3.1": + version: 0.3.2 + resolution: "@eslint/plugin-kit@npm:0.3.2" dependencies: - "@eslint/core": "npm:^0.13.0" + "@eslint/core": "npm:^0.15.0" levn: "npm:^0.4.1" - checksum: 10/2e7fe7a88ebdbbf805e9e7265347b7dcfb6bf50beec314def997572b2e8ae4a7b9504fb67b1698a70c348a0dd87251d1e9028292a96fd49b58cb5277d88bdea7 + checksum: 10/26ba99936f72ca124036fbc5ca93168713fab5984117109b1447642a93725fbb75aa457622683dc8797509e40294497d74b584caa26f285373bdde17ceba8eac languageName: node linkType: hard @@ -779,24 +788,6 @@ __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" @@ -1552,12 +1543,12 @@ __metadata: linkType: hard "@types/react@npm:^18.0.0": - version: 18.3.21 - resolution: "@types/react@npm:18.3.21" + version: 18.3.23 + resolution: "@types/react@npm:18.3.23" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/89adc2fe391fd63b620db0aaa10b2be89fad338039415fd748e60c962e47bf2b0f1897cb5226f181eda963f37a1c8ac1bf0b3b29a0e35313bacd0355e5d6c736 + checksum: 10/4b965dffe34a1f8aac8e2d7e976f113373f38134f9e37239f7e75d7ac6b3c2e1333a8df21febf1fe7749640f8de5708f7668cdfc70bffebda1cc4d3346724fd5 languageName: node linkType: hard @@ -1884,16 +1875,6 @@ __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" @@ -1903,12 +1884,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.12.1, acorn@npm:^8.14.0": - version: 8.14.0 - resolution: "acorn@npm:8.14.0" +"acorn@npm:^8.12.1, acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" bin: acorn: bin/acorn - checksum: 10/6df29c35556782ca9e632db461a7f97947772c6c1d5438a81f0c873a3da3a792487e83e404d1c6c25f70513e91aa18745f6eafb1fcc3a43ecd1920b21dd173d2 + checksum: 10/77f2de5051a631cf1729c090e5759148459cdb76b5f5c70f890503d629cf5052357b0ce783c0f976dd8a93c5150f59f6d18df1def3f502396a20f81282482fa4 languageName: node linkType: hard @@ -2092,23 +2073,6 @@ __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" @@ -2158,13 +2122,6 @@ __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" @@ -2208,16 +2165,6 @@ __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" @@ -2355,22 +2302,6 @@ __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" @@ -2378,31 +2309,7 @@ __metadata: languageName: node linkType: hard -"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": +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -2427,7 +2334,7 @@ __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, debug@npm:^4.3.5, debug@npm:^4.4.0": +"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.4.0 resolution: "debug@npm:4.4.0" dependencies: @@ -2471,13 +2378,6 @@ __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" @@ -2535,13 +2435,6 @@ __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" @@ -2563,13 +2456,6 @@ __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" @@ -2812,13 +2698,6 @@ __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" @@ -2856,13 +2735,13 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.3.0": - version: 8.3.0 - resolution: "eslint-scope@npm:8.3.0" +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10/ee1ff009e949423639a8b53453c0cb189967d9142c5d94dc3752bed9880140a0760007148ac6b0bd03557d70ede9cd7c3b1e66f9a7f3427b2dbeca2a5be22c91 + checksum: 10/e8e611701f65375e034c62123946e628894f0b54aa8cb11abe224816389abe5cd74cf16b62b72baa36504f22d1a958b9b8b0169b82397fe2e7997674c0d09b06 languageName: node linkType: hard @@ -2873,29 +2752,28 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.2.0": - version: 4.2.0 - resolution: "eslint-visitor-keys@npm:4.2.0" - checksum: 10/9651b3356b01760e586b4c631c5268c0e1a85236e3292bf754f0472f465bf9a856c0ddc261fceace155334118c0151778effafbab981413dbf9288349343fa25 +"eslint-visitor-keys@npm:^4.2.0, eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10/3ee00fc6a7002d4b0ffd9dc99e13a6a7882c557329e6c25ab254220d71e5c9c4f89dca4695352949ea678eb1f3ba912a18ef8aac0a7fe094196fd92f441bfce2 languageName: node linkType: hard "eslint@npm:^9.11.1": - version: 9.26.0 - resolution: "eslint@npm:9.26.0" + version: 9.29.0 + resolution: "eslint@npm:9.29.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.20.0" + "@eslint/config-array": "npm:^0.20.1" "@eslint/config-helpers": "npm:^0.2.1" - "@eslint/core": "npm:^0.13.0" + "@eslint/core": "npm:^0.14.0" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.26.0" - "@eslint/plugin-kit": "npm:^0.2.8" + "@eslint/js": "npm:9.29.0" + "@eslint/plugin-kit": "npm:^0.3.1" "@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" @@ -2903,9 +2781,9 @@ __metadata: cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.3.0" - eslint-visitor-keys: "npm:^4.2.0" - espree: "npm:^10.3.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -2920,7 +2798,6 @@ __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: @@ -2928,18 +2805,18 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10/b87092cb7e87f1d0963475c1a1e15e551842ea122925cf13231e742fae565bf3582029a5b0b4aecf793f25c26ee0be3ee1f32190bc361e0c3f3633b9cbace948 + checksum: 10/be0c8e123207c9d653fb75ddc610b85dfbf295a2bfa1cbecc78f191dcba9c421525b5befd5d499ce561eca607c9c33f455e4fff0b1c2d4202c2896dafe95094a languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.3.0": - version: 10.3.0 - resolution: "espree@npm:10.3.0" +"espree@npm:^10.0.1, espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" dependencies: - acorn: "npm:^8.14.0" + acorn: "npm:^8.15.0" acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.2.0" - checksum: 10/3412d44d4204c9e29d6b5dd0277400cfa0cd68495dc09eae1b9ce79d0c8985c1c5cc09cb9ba32a1cd963f48a49b0c46bdb7736afe395a300aa6bb1c0d86837e8 + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10/9b355b32dbd1cc9f57121d5ee3be258fab87ebeb7c83fc6c02e5af1a74fc8c5ba79fe8c663e69ea112c3e84a1b95e6a2067ac4443ee7813bb85ac7581acb8bf9 languageName: node linkType: hard @@ -3008,29 +2885,6 @@ __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" @@ -3038,50 +2892,6 @@ __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" @@ -3150,20 +2960,6 @@ __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" @@ -3210,20 +3006,6 @@ __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" @@ -3275,7 +3057,7 @@ __metadata: languageName: node linkType: hard -"get-intrinsic@npm:^1.2.4, get-intrinsic@npm:^1.2.5, get-intrinsic@npm:^1.3.0": +"get-intrinsic@npm:^1.2.4": version: 1.3.0 resolution: "get-intrinsic@npm:1.3.0" dependencies: @@ -3448,19 +3230,6 @@ __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" @@ -3481,7 +3250,7 @@ __metadata: languageName: node linkType: hard -"iconv-lite@npm:0.6.3, iconv-lite@npm:^0.6.2, iconv-lite@npm:^0.6.3": +"iconv-lite@npm:^0.6.2": version: 0.6.3 resolution: "iconv-lite@npm:0.6.3" dependencies: @@ -3521,7 +3290,7 @@ __metadata: languageName: node linkType: hard -"inherits@npm:2.0.4, inherits@npm:^2.0.3": +"inherits@npm:^2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 10/cd45e923bee15186c07fa4c89db0aace24824c482fb887b528304694b2aa6ff8a898da8657046a5dcf3e46cd6db6c61629551f9215f208d7c3f157cf9b290521 @@ -3538,13 +3307,6 @@ __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" @@ -3626,13 +3388,6 @@ __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" @@ -3899,13 +3654,6 @@ __metadata: 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" @@ -3915,13 +3663,6 @@ __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" @@ -3939,22 +3680,6 @@ __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" @@ -4117,13 +3842,6 @@ __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" @@ -4162,38 +3880,6 @@ __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" @@ -4262,13 +3948,6 @@ __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" @@ -4300,13 +3979,6 @@ __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" @@ -4335,13 +4007,6 @@ __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" @@ -4420,16 +4085,6 @@ __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" @@ -4437,15 +4092,6 @@ __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" @@ -4453,25 +4099,6 @@ __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" @@ -4720,19 +4347,6 @@ __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" @@ -4742,13 +4356,6 @@ __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" @@ -4790,37 +4397,6 @@ __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" @@ -4835,13 +4411,6 @@ __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" @@ -4858,54 +4427,6 @@ __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" @@ -4978,13 +4499,6 @@ __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" @@ -5151,13 +4665,6 @@ __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" @@ -5233,17 +4740,6 @@ __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" @@ -5303,13 +4799,6 @@ __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" @@ -5370,13 +4859,6 @@ __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" @@ -5491,13 +4973,6 @@ __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" @@ -5533,19 +5008,3 @@ __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 d8736591ea..083d80ba3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2148,17 +2148,17 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-string-parser@npm:7.25.9" - checksum: 10/c28656c52bd48e8c1d9f3e8e68ecafd09d949c57755b0d353739eb4eae7ba4f7e67e92e4036f1cd43378cc1397a2c943ed7bcaf5949b04ab48607def0258b775 +"@babel/helper-string-parser@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-string-parser@npm:7.27.1" + checksum: 10/0ae29cc2005084abdae2966afdb86ed14d41c9c37db02c3693d5022fba9f5d59b011d039380b8e537c34daf117c549f52b452398f576e908fb9db3c7abbb3a00 languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.22.5, @babel/helper-validator-identifier@npm:^7.24.7, @babel/helper-validator-identifier@npm:^7.25.9": - version: 7.25.9 - resolution: "@babel/helper-validator-identifier@npm:7.25.9" - checksum: 10/3f9b649be0c2fd457fa1957b694b4e69532a668866b8a0d81eabfa34ba16dbf3107b39e0e7144c55c3c652bf773ec816af8df4a61273a2bb4eb3145ca9cf478e +"@babel/helper-validator-identifier@npm:^7.22.5, @babel/helper-validator-identifier@npm:^7.24.7, @babel/helper-validator-identifier@npm:^7.25.9, @babel/helper-validator-identifier@npm:^7.27.1": + version: 7.27.1 + resolution: "@babel/helper-validator-identifier@npm:7.27.1" + checksum: 10/75041904d21bdc0cd3b07a8ac90b11d64cd3c881e89cb936fa80edd734bf23c35e6bd1312611e8574c4eab1f3af0f63e8a5894f4699e9cfdf70c06fcf4252320 languageName: node linkType: hard @@ -2202,14 +2202,14 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2, @babel/parser@npm:^7.27.0": - version: 7.27.0 - resolution: "@babel/parser@npm:7.27.0" +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.7, @babel/parser@npm:^7.25.9, @babel/parser@npm:^7.26.0, @babel/parser@npm:^7.26.2, @babel/parser@npm:^7.26.7, @babel/parser@npm:^7.27.0, @babel/parser@npm:^7.27.5": + version: 7.27.5 + resolution: "@babel/parser@npm:7.27.5" dependencies: - "@babel/types": "npm:^7.27.0" + "@babel/types": "npm:^7.27.3" bin: parser: ./bin/babel-parser.js - checksum: 10/0fee9f05c6db753882ca9d10958301493443da9f6986d7020ebd7a696b35886240016899bc0b47d871aea2abcafd64632343719742e87432c8145e0ec2af2a03 + checksum: 10/0ad671be7994dba7d31ec771bd70ea5090aa34faf73e93b1b072e3c0a704ab69f4a7a68ebfb9d6a7fa455e0aa03dfa65619c4df6bae1cf327cba925b1d233fc4 languageName: node linkType: hard @@ -3370,13 +3370,13 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": - version: 7.27.0 - resolution: "@babel/types@npm:7.27.0" +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.18.9, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.5, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.25.9, @babel/types@npm:^7.26.0, @babel/types@npm:^7.27.0, @babel/types@npm:^7.27.3, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": + version: 7.27.6 + resolution: "@babel/types@npm:7.27.6" dependencies: - "@babel/helper-string-parser": "npm:^7.25.9" - "@babel/helper-validator-identifier": "npm:^7.25.9" - checksum: 10/2c322bce107c8a534dc4a23be60d570e6a4cc7ca2e44d4f0eee08c0b626104eb7e60ab8de03463bc5da1773a2f69f1e6edec1648d648d65461d6520a7f3b0770 + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.27.1" + checksum: 10/174741c667775680628a09117828bbeffb35ea543f59bf80649d0d60672f7815a0740ddece3cca87516199033a039166a6936434131fce2b6a820227e64f91ae languageName: node linkType: hard @@ -3602,6 +3602,7 @@ __metadata: cron: "npm:^3.0.0" express: "npm:^4.17.1" express-promise-router: "npm:^4.1.0" + express-rate-limit: "npm:^7.5.0" fs-extra: "npm:^11.2.0" git-url-parse: "npm:^15.0.0" helmet: "npm:^6.0.0" @@ -3624,6 +3625,7 @@ __metadata: pg: "npm:^8.11.3" pg-connection-string: "npm:^2.3.0" pg-format: "npm:^1.0.4" + rate-limit-redis: "npm:^4.2.0" raw-body: "npm:^2.4.1" selfsigned: "npm:^2.0.0" supertest: "npm:^7.0.0" @@ -3722,22 +3724,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.1.1, @backstage/backend-plugin-api@npm:^1.3.1": - version: 1.3.1 - resolution: "@backstage/backend-plugin-api@npm:1.3.1" +"@backstage/backend-plugin-api@npm:^1.0.0, @backstage/backend-plugin-api@npm:^1.1.1, @backstage/backend-plugin-api@npm:^1.4.0": + version: 1.4.0 + resolution: "@backstage/backend-plugin-api@npm:1.4.0" dependencies: "@backstage/cli-common": "npm:^0.1.15" "@backstage/config": "npm:^1.3.2" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.3" + "@backstage/plugin-auth-node": "npm:^0.6.4" "@backstage/plugin-permission-common": "npm:^0.9.0" - "@backstage/plugin-permission-node": "npm:^0.10.0" + "@backstage/plugin-permission-node": "npm:^0.10.1" "@backstage/types": "npm:^1.2.1" "@types/express": "npm:^4.17.6" + "@types/json-schema": "npm:^7.0.6" "@types/luxon": "npm:^3.0.0" + json-schema: "npm:^0.4.0" knex: "npm:^3.0.0" luxon: "npm:^3.0.0" - checksum: 10/c78a0cd73c9e2ab3f611e62b1570b83fd496f501616caf00877c5191a7c431671f66f77d6e7fd2a35982915f8cb1b69a85cf7d26e3f236a28f709a6ae7af8eaf + zod: "npm:^3.22.4" + checksum: 10/6e8f0afbc690ac34fc4bee926009666d51a17cc4c4392524d345f208dbb92df8bb1b3d703d70a08a27cddf8975adf7d09a443e737665c62d6caea270e86a4d29 languageName: node linkType: hard @@ -3776,6 +3781,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-events-node": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" "@backstage/types": "workspace:^" "@keyv/memcache": "npm:^2.0.1" "@keyv/redis": "npm:^4.0.1" @@ -3800,6 +3806,8 @@ __metadata: textextensions: "npm:^5.16.0" uuid: "npm:^11.0.0" yn: "npm:^4.0.0" + zod: "npm:^3.22.4" + zod-to-json-schema: "npm:^3.20.4" languageName: unknown linkType: soft @@ -3813,7 +3821,6 @@ __metadata: "@storybook/addon-essentials": "npm:^8.6.12" "@storybook/addon-interactions": "npm:^8.6.12" "@storybook/addon-styling-webpack": "npm:^1.0.1" - "@storybook/addon-themes": "npm:^8.6.12" "@storybook/addon-webpack5-compiler-swc": "npm:^3.0.0" "@storybook/blocks": "npm:^8.6.12" "@storybook/react": "npm:^8.6.12" @@ -3829,7 +3836,9 @@ __metadata: globals: "npm:^15.11.0" lightningcss: "npm:^1.29.1" mini-css-extract-plugin: "npm:^2.9.2" + motion: "npm:^12.20.1" react: "npm:^18.0.2" + react-aria-components: "npm:^1.10.1" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" storybook: "npm:^8.6.12" @@ -3844,15 +3853,15 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@npm:^1.10.0, @backstage/catalog-client@npm:^1.9.1": - version: 1.10.0 - resolution: "@backstage/catalog-client@npm:1.10.0" +"@backstage/catalog-client@npm:^1.10.1, @backstage/catalog-client@npm:^1.9.1": + version: 1.10.1 + resolution: "@backstage/catalog-client@npm:1.10.1" dependencies: "@backstage/catalog-model": "npm:^1.7.4" "@backstage/errors": "npm:^1.2.7" cross-fetch: "npm:^4.0.0" uri-template: "npm:^2.0.0" - checksum: 10/565512931ef340c272ebbffcac7c5f54348ed67a73be80cdecb8768c547a1da41c72b02c0830f3036a06edbc8c77b9720ee5f1f498b2308f799fd409bb4bbbe1 + checksum: 10/85a220800db02f13f9fdf5beb1bf06d43d6aa0c39c6e6c69c0c6d69ae7bc283f8933a42d984f6d7cb1c4eb1dc91fba7b14c94f7cc0ed775dc3a6ae6fc7e4fc15 languageName: node linkType: hard @@ -3869,7 +3878,19 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-model@npm:^1.4.3, @backstage/catalog-model@npm:^1.7.3, @backstage/catalog-model@npm:^1.7.4, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": +"@backstage/catalog-model@npm:^1.4.3, @backstage/catalog-model@npm:^1.7.3, @backstage/catalog-model@npm:^1.7.4": + version: 1.7.4 + resolution: "@backstage/catalog-model@npm:1.7.4" + dependencies: + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.1" + ajv: "npm:^8.10.0" + lodash: "npm:^4.17.21" + checksum: 10/48c2db2a8144e891319879cec6cae1980088165a910a757b8ebc07dc337b4d8d5c743fbaa3fa916cdd1b5e06635b3a3edbd1e7d8519ac135824f1ba37c5e3ce2 + languageName: node + linkType: hard + +"@backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": version: 0.0.0-use.local resolution: "@backstage/catalog-model@workspace:packages/catalog-model" dependencies: @@ -4078,7 +4099,6 @@ __metadata: optional: true bin: backstage-cli: bin/backstage-cli - backstage-cli-alpha: bin/backstage-cli-alpha languageName: unknown linkType: soft @@ -4099,7 +4119,30 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config-loader@npm:^1.9.1, @backstage/config-loader@workspace:^, @backstage/config-loader@workspace:packages/config-loader": +"@backstage/config-loader@npm:^1.9.1": + version: 1.10.1 + resolution: "@backstage/config-loader@npm:1.10.1" + dependencies: + "@backstage/cli-common": "npm:^0.1.15" + "@backstage/config": "npm:^1.3.2" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.1" + "@types/json-schema": "npm:^7.0.6" + ajv: "npm:^8.10.0" + chokidar: "npm:^3.5.2" + fs-extra: "npm:^11.2.0" + json-schema: "npm:^0.4.0" + json-schema-merge-allof: "npm:^0.8.1" + json-schema-traverse: "npm:^1.0.0" + lodash: "npm:^4.17.21" + minimist: "npm:^1.2.5" + typescript-json-schema: "npm:^0.65.0" + yaml: "npm:^2.0.0" + checksum: 10/61f66e65bf747b07f72c3475a3875c5409442087ca541c749a1ecff6668cb0564294f6b760a2e76e135871bdaf61f2b8bcb2e62d4786d12693400c57422f3b85 + languageName: node + linkType: hard + +"@backstage/config-loader@workspace:^, @backstage/config-loader@workspace:packages/config-loader": version: 0.0.0-use.local resolution: "@backstage/config-loader@workspace:packages/config-loader" dependencies: @@ -4127,7 +4170,18 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config@npm:^1.1.1, @backstage/config@npm:^1.2.0, @backstage/config@npm:^1.3.2, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": +"@backstage/config@npm:^1.1.1, @backstage/config@npm:^1.2.0, @backstage/config@npm:^1.3.2": + version: 1.3.2 + resolution: "@backstage/config@npm:1.3.2" + dependencies: + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.1" + ms: "npm:^2.1.3" + checksum: 10/cc2e4ff7cd0db7542ed258fb273826057aff1455f745c1f9379303c3407ea6ca4f9f908a73f29470b9ca3155ef4603263e9d1dda5bd4d6930c42e794c70885e4 + languageName: node + linkType: hard + +"@backstage/config@workspace:^, @backstage/config@workspace:packages/config": version: 0.0.0-use.local resolution: "@backstage/config@workspace:packages/config" dependencies: @@ -4139,7 +4193,35 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-app-api@npm:^1.17.0, @backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": +"@backstage/core-app-api@npm:^1.17.1": + version: 1.17.1 + resolution: "@backstage/core-app-api@npm:1.17.1" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/types": "npm:^1.2.1" + "@backstage/version-bridge": "npm:^1.0.11" + "@types/prop-types": "npm:^15.7.3" + history: "npm:^5.0.0" + i18next: "npm:^22.4.15" + lodash: "npm:^4.17.21" + prop-types: "npm:^15.7.2" + react-use: "npm:^17.2.4" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.22.4" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/04248e92fbbfa0c994ce16369a536b27574f3ad9bb19b34c789c41c872c2cfe71f0cb0c00f89676d387097477125ca45a794df149b89a5654e6f6d92afcb2752 + languageName: node + linkType: hard + +"@backstage/core-app-api@workspace:^, @backstage/core-app-api@workspace:packages/core-app-api": version: 0.0.0-use.local resolution: "@backstage/core-app-api@workspace:packages/core-app-api" dependencies: @@ -4183,13 +4265,13 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-compat-api@npm:^0.4.2": - version: 0.4.2 - resolution: "@backstage/core-compat-api@npm:0.4.2" +"@backstage/core-compat-api@npm:^0.4.3": + version: 0.4.3 + resolution: "@backstage/core-compat-api@npm:0.4.3" dependencies: - "@backstage/core-plugin-api": "npm:^1.10.7" - "@backstage/frontend-plugin-api": "npm:^0.10.2" - "@backstage/plugin-catalog-react": "npm:^1.18.0" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/frontend-plugin-api": "npm:^0.10.3" + "@backstage/plugin-catalog-react": "npm:^1.19.0" "@backstage/version-bridge": "npm:^1.0.11" lodash: "npm:^4.17.21" peerDependencies: @@ -4200,7 +4282,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/7e93c3ff7a9979ee8cb0ab582e66bd0febb5995c91cc0545473e9e86fc75c1ebbb0a24a901e120d7e4ecf122bbb31de1b4f8fc78c7da5f6d84daa9d6b9194035 + checksum: 10/ade339ad577d6b875232b628b2388f4cf902cb1a5c75c0a1c96b62b8573f77d23a81c0e9e426abc546c6ffb90415ff7804a92d309aea500ba6451beec47ddc8e languageName: node linkType: hard @@ -4344,7 +4426,61 @@ __metadata: languageName: node linkType: hard -"@backstage/core-components@npm:^0.17.1, @backstage/core-components@npm:^0.17.2, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@npm:^0.17.1, @backstage/core-components@npm:^0.17.3": + version: 0.17.3 + resolution: "@backstage/core-components@npm:0.17.3" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/errors": "npm:^1.2.7" + "@backstage/theme": "npm:^0.6.6" + "@backstage/version-bridge": "npm:^1.0.11" + "@dagrejs/dagre": "npm:^1.1.4" + "@date-io/core": "npm:^1.3.13" + "@material-table/core": "npm:^3.1.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@react-hookz/web": "npm:^24.0.0" + "@testing-library/react": "npm:^16.0.0" + "@types/react-sparklines": "npm:^1.7.0" + ansi-regex: "npm:^6.0.1" + classnames: "npm:^2.2.6" + d3-selection: "npm:^3.0.0" + d3-shape: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + js-yaml: "npm:^4.1.0" + linkify-react: "npm:4.1.3" + linkifyjs: "npm:4.1.3" + lodash: "npm:^4.17.21" + pluralize: "npm:^8.0.0" + qs: "npm:^6.9.4" + rc-progress: "npm:3.5.1" + react-helmet: "npm:6.1.0" + react-hook-form: "npm:^7.12.2" + react-idle-timer: "npm:5.7.2" + react-markdown: "npm:^8.0.0" + react-sparklines: "npm:^1.7.0" + react-syntax-highlighter: "npm:^15.4.5" + react-use: "npm:^17.3.2" + react-virtualized-auto-sizer: "npm:^1.0.11" + react-window: "npm:^1.8.6" + remark-gfm: "npm:^3.0.1" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.22.4" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/9c826ba203dcee62077cb9e3c83fbeb1d72558dca4b3fcb41c19c587a8162862dcab061d56b1f3cbdfba96fa19dbcae4959caf9009d3288930606ec2646056c6 + languageName: node + linkType: hard + +"@backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": version: 0.0.0-use.local resolution: "@backstage/core-components@workspace:packages/core-components" dependencies: @@ -4422,7 +4558,28 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-plugin-api@npm:^1.10.0, @backstage/core-plugin-api@npm:^1.10.6, @backstage/core-plugin-api@npm:^1.10.7, @backstage/core-plugin-api@npm:^1.8.2, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": +"@backstage/core-plugin-api@npm:^1.10.0, @backstage/core-plugin-api@npm:^1.10.6, @backstage/core-plugin-api@npm:^1.10.8, @backstage/core-plugin-api@npm:^1.8.2": + version: 1.10.8 + resolution: "@backstage/core-plugin-api@npm:1.10.8" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.1" + "@backstage/version-bridge": "npm:^1.0.11" + history: "npm:^5.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/4b92c70ce68e6ec3a1b1cf77fd30591a066cc109f2af352d25583257fe83b71a0ea7f848f24bad7313028128d45819ef10ed526695bfd894ebe4dadeb420f2d1 + languageName: node + linkType: hard + +"@backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": version: 0.0.0-use.local resolution: "@backstage/core-plugin-api@workspace:packages/core-plugin-api" dependencies: @@ -4543,7 +4700,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/eslint-plugin@workspace:^, @backstage/eslint-plugin@workspace:packages/eslint-plugin": +"@backstage/eslint-plugin@workspace:*, @backstage/eslint-plugin@workspace:^, @backstage/eslint-plugin@workspace:packages/eslint-plugin": version: 0.0.0-use.local resolution: "@backstage/eslint-plugin@workspace:packages/eslint-plugin" dependencies: @@ -4555,16 +4712,16 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-app-api@npm:^0.11.2": - version: 0.11.2 - resolution: "@backstage/frontend-app-api@npm:0.11.2" +"@backstage/frontend-app-api@npm:^0.11.3": + version: 0.11.3 + resolution: "@backstage/frontend-app-api@npm:0.11.3" dependencies: "@backstage/config": "npm:^1.3.2" - "@backstage/core-app-api": "npm:^1.17.0" - "@backstage/core-plugin-api": "npm:^1.10.7" + "@backstage/core-app-api": "npm:^1.17.1" + "@backstage/core-plugin-api": "npm:^1.10.8" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-defaults": "npm:^0.2.2" - "@backstage/frontend-plugin-api": "npm:^0.10.2" + "@backstage/frontend-defaults": "npm:^0.2.3" + "@backstage/frontend-plugin-api": "npm:^0.10.3" "@backstage/types": "npm:^1.2.1" "@backstage/version-bridge": "npm:^1.0.11" lodash: "npm:^4.17.21" @@ -4577,7 +4734,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/06cf128df60b0322c07c14853146da5096625d58a344555571a65ead221f03dc1f4e2fe7da364b2afec010be13a4d4a4574060a3f65c023bdcdc93d20d09c33d + checksum: 10/a1b012462db67f09c0fe49056fe4d3482da5a9c43a0f3056eb2a6833a66e35d34a6aaa2d0eb9572f4f1b180a9d7511f9357835c441825f5967eb1183a7f78d27 languageName: node linkType: hard @@ -4615,15 +4772,15 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-defaults@npm:^0.2.2": - version: 0.2.2 - resolution: "@backstage/frontend-defaults@npm:0.2.2" +"@backstage/frontend-defaults@npm:^0.2.3": + version: 0.2.3 + resolution: "@backstage/frontend-defaults@npm:0.2.3" dependencies: "@backstage/config": "npm:^1.3.2" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-app-api": "npm:^0.11.2" - "@backstage/frontend-plugin-api": "npm:^0.10.2" - "@backstage/plugin-app": "npm:^0.1.9" + "@backstage/frontend-app-api": "npm:^0.11.3" + "@backstage/frontend-plugin-api": "npm:^0.10.3" + "@backstage/plugin-app": "npm:^0.1.10" "@react-hookz/web": "npm:^24.0.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -4633,7 +4790,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/23e6156314cb80d9cc1128ee3ad2d3afee3e2a6e88c049853b19133fc95e75f4361e735f26903e44fa62b29d39df5381c53200799a8cc3b3fd0943682a5e51f5 + checksum: 10/f9b84a850e219c389f4e28ea459f1df03057d1092cfe126365bb2149540fd564269b03a8d0220ca9ba1c4ebd000fbc84f4ee842551e00cd1daec133fa3edf86b languageName: node linkType: hard @@ -4698,12 +4855,12 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-plugin-api@npm:^0.10.2": - version: 0.10.2 - resolution: "@backstage/frontend-plugin-api@npm:0.10.2" +"@backstage/frontend-plugin-api@npm:^0.10.3": + version: 0.10.3 + resolution: "@backstage/frontend-plugin-api@npm:0.10.3" dependencies: - "@backstage/core-components": "npm:^0.17.2" - "@backstage/core-plugin-api": "npm:^1.10.7" + "@backstage/core-components": "npm:^0.17.3" + "@backstage/core-plugin-api": "npm:^1.10.8" "@backstage/types": "npm:^1.2.1" "@backstage/version-bridge": "npm:^1.0.11" "@material-ui/core": "npm:^4.12.4" @@ -4718,7 +4875,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/4f01cf2ae8352dd8b5f7ff66a97739c6f9f8b19b182ae7cb05db542d02fa0cdee805712c0d355068de3c7cd851404fe6e424c5b7a0ffbcec686c42bc78030510 + checksum: 10/70bd14ad75502d8a7de38b48a21d9672e883e6db49cfe6ce4f1298fb53fe30e5da940e090c63d3bf9e17521d18ae6297c0f322db924184d64ae12fe6f11b8f23 languageName: node linkType: hard @@ -4756,15 +4913,15 @@ __metadata: languageName: unknown linkType: soft -"@backstage/frontend-test-utils@npm:^0.3.2": - version: 0.3.2 - resolution: "@backstage/frontend-test-utils@npm:0.3.2" +"@backstage/frontend-test-utils@npm:^0.3.3": + version: 0.3.3 + resolution: "@backstage/frontend-test-utils@npm:0.3.3" dependencies: "@backstage/config": "npm:^1.3.2" - "@backstage/frontend-app-api": "npm:^0.11.2" - "@backstage/frontend-plugin-api": "npm:^0.10.2" - "@backstage/plugin-app": "npm:^0.1.9" - "@backstage/test-utils": "npm:^1.7.8" + "@backstage/frontend-app-api": "npm:^0.11.3" + "@backstage/frontend-plugin-api": "npm:^0.10.3" + "@backstage/plugin-app": "npm:^0.1.10" + "@backstage/test-utils": "npm:^1.7.9" "@backstage/types": "npm:^1.2.1" "@backstage/version-bridge": "npm:^1.0.11" zod: "npm:^3.22.4" @@ -4777,7 +4934,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/3b33d0165e2bda8460e433649fec04d7594c7d987b7cdc59b46a955208266481d15df0f6d529a0bfbece7050d6510c0e320bf77fc81296579747e9a469d8e7d0 + checksum: 10/0310892cc13256a28315e5a9afe9a3b00723bffbcfee67849f01301538d1accdb48361db7dbf092889313aa2e5f7a531e31350b32096e0f223832a6329b75dd8 languageName: node linkType: hard @@ -4811,7 +4968,22 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-aws-node@npm:^0.1.12, @backstage/integration-aws-node@workspace:^, @backstage/integration-aws-node@workspace:packages/integration-aws-node": +"@backstage/integration-aws-node@npm:^0.1.12": + version: 0.1.16 + resolution: "@backstage/integration-aws-node@npm:0.1.16" + dependencies: + "@aws-sdk/client-sts": "npm:^3.350.0" + "@aws-sdk/credential-provider-node": "npm:^3.350.0" + "@aws-sdk/credential-providers": "npm:^3.350.0" + "@aws-sdk/types": "npm:^3.347.0" + "@aws-sdk/util-arn-parser": "npm:^3.310.0" + "@backstage/config": "npm:^1.3.2" + "@backstage/errors": "npm:^1.2.7" + checksum: 10/89963dda10ac49a9b015b0c3e05fc5995b024c5f7a2a3b519d8e5bb1fe1a8d0a070c4d2a24638f7e4eac46b109696963e4661fe9fb479541265a5f4a935e10b8 + languageName: node + linkType: hard + +"@backstage/integration-aws-node@workspace:^, @backstage/integration-aws-node@workspace:packages/integration-aws-node": version: 0.0.0-use.local resolution: "@backstage/integration-aws-node@workspace:packages/integration-aws-node" dependencies: @@ -4830,7 +5002,28 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@npm:^1.1.24, @backstage/integration-react@npm:^1.2.7, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@npm:^1.1.24, @backstage/integration-react@npm:^1.2.8": + version: 1.2.8 + resolution: "@backstage/integration-react@npm:1.2.8" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/integration": "npm:^1.17.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/f4d6ea7bd0fa9017dcdd4162e0f6a9a16737891441e0659270674fedd49c1cee594435d92b039230ec4b6c493df390efe12f9c653d09d51b5283f4add74ac4db + languageName: node + linkType: hard + +"@backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": version: 0.0.0-use.local resolution: "@backstage/integration-react@workspace:packages/integration-react" dependencies: @@ -4861,7 +5054,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration@npm:^1.15.0, @backstage/integration@npm:^1.9.0, @backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": +"@backstage/integration@npm:^1.15.0, @backstage/integration@npm:^1.17.0, @backstage/integration@npm:^1.9.0": + version: 1.17.0 + resolution: "@backstage/integration@npm:1.17.0" + dependencies: + "@azure/identity": "npm:^4.0.0" + "@azure/storage-blob": "npm:^12.5.0" + "@backstage/config": "npm:^1.3.2" + "@backstage/errors": "npm:^1.2.7" + "@octokit/auth-app": "npm:^4.0.0" + "@octokit/rest": "npm:^19.0.3" + cross-fetch: "npm:^4.0.0" + git-url-parse: "npm:^15.0.0" + lodash: "npm:^4.17.21" + luxon: "npm:^3.0.0" + checksum: 10/651a717a2268be73f6b1d34072585641cf3ad0e8e5122fc4cca249e773fda4dcde904588331d352ae455a420120fb1ef7847cd859d8d9132203eec63c8c6b01a + languageName: node + linkType: hard + +"@backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": version: 0.0.0-use.local resolution: "@backstage/integration@workspace:packages/integration" dependencies: @@ -5022,15 +5233,15 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-app@npm:^0.1.9": - version: 0.1.9 - resolution: "@backstage/plugin-app@npm:0.1.9" +"@backstage/plugin-app@npm:^0.1.10": + version: 0.1.10 + resolution: "@backstage/plugin-app@npm:0.1.10" dependencies: - "@backstage/core-components": "npm:^0.17.2" - "@backstage/core-plugin-api": "npm:^1.10.7" - "@backstage/frontend-plugin-api": "npm:^0.10.2" - "@backstage/integration-react": "npm:^1.2.7" - "@backstage/plugin-permission-react": "npm:^0.4.34" + "@backstage/core-components": "npm:^0.17.3" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/frontend-plugin-api": "npm:^0.10.3" + "@backstage/integration-react": "npm:^1.2.8" + "@backstage/plugin-permission-react": "npm:^0.4.35" "@backstage/theme": "npm:^0.6.6" "@backstage/types": "npm:^1.2.1" "@material-ui/core": "npm:^4.9.13" @@ -5045,7 +5256,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/9c357605a8427f8bf5099968c612f90015d34d757a9494ea22d487bec20593cd80a7a50326e46344fc877ed163a14f2675d33530c1d756411316b0d373096aab + checksum: 10/1961abd525bed732d8c8642db42dba26a5cd3ce1193f869ed754806629c74ab3ffc66b5d71dcab1d240500b515b04836def3181d2d783ac87ee6b625e34674ff languageName: node linkType: hard @@ -5401,7 +5612,7 @@ __metadata: "@backstage/plugin-auth-backend": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/types": "workspace:^" - "@davidzemon/passport-okta-oauth": "npm:^0.0.5" + "@davidzemon/passport-okta-oauth": "npm:^0.0.6" express: "npm:^4.18.2" passport: "npm:^0.7.0" supertest: "npm:^7.0.0" @@ -5536,12 +5747,12 @@ __metadata: languageName: node linkType: hard -"@backstage/plugin-auth-node@npm:^0.6.3": - version: 0.6.3 - resolution: "@backstage/plugin-auth-node@npm:0.6.3" +"@backstage/plugin-auth-node@npm:^0.6.4": + version: 0.6.4 + resolution: "@backstage/plugin-auth-node@npm:0.6.4" dependencies: - "@backstage/backend-plugin-api": "npm:^1.3.1" - "@backstage/catalog-client": "npm:^1.10.0" + "@backstage/backend-plugin-api": "npm:^1.4.0" + "@backstage/catalog-client": "npm:^1.10.1" "@backstage/catalog-model": "npm:^1.7.4" "@backstage/config": "npm:^1.3.2" "@backstage/errors": "npm:^1.2.7" @@ -5555,7 +5766,7 @@ __metadata: zod: "npm:^3.22.4" zod-to-json-schema: "npm:^3.21.4" zod-validation-error: "npm:^3.4.0" - checksum: 10/7f7fd4c1072512c8db6902e7a3e75319f73e2e487da0d202f3fde446eb340a34e7b1c0ce92d16f4dc2a5525aecd1df78979cdcfc650a48a41b2441f221fc0579 + checksum: 10/bbd91e6fc8ebd6762db0b59ae2b3bb4e17c2e24637158ce3442407901103b55c694fa5325d4ac2ed11ea590c11f832c18fe8af33cff55f259b165774c05becd2 languageName: node linkType: hard @@ -6058,7 +6269,6 @@ __metadata: "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-permission-node": "workspace:^" "@backstage/repo-tools": "workspace:^" - "@backstage/test-utils": "workspace:^" "@backstage/types": "workspace:^" "@opentelemetry/api": "npm:^1.9.0" "@types/core-js": "npm:^2.5.4" @@ -6092,7 +6302,18 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@npm:^1.0.20, @backstage/plugin-catalog-common@npm:^1.1.4, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@npm:^1.0.20, @backstage/plugin-catalog-common@npm:^1.1.4": + version: 1.1.4 + resolution: "@backstage/plugin-catalog-common@npm:1.1.4" + dependencies: + "@backstage/catalog-model": "npm:^1.7.4" + "@backstage/plugin-permission-common": "npm:^0.9.0" + "@backstage/plugin-search-common": "npm:^1.2.18" + checksum: 10/e65331add32185a54de3c0bae4187824bd0b2d4a729cf4cd659bda54d3dd1cb94a9aeaf4553ee08ae44439742cc45149767329b3ef07bc39ed014433a62d7bd0 + languageName: node + linkType: hard + +"@backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-common@workspace:plugins/catalog-common" dependencies: @@ -6218,22 +6439,22 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@npm:^1.17.0, @backstage/plugin-catalog-react@npm:^1.18.0, @backstage/plugin-catalog-react@npm:^1.9.3": - version: 1.18.0 - resolution: "@backstage/plugin-catalog-react@npm:1.18.0" +"@backstage/plugin-catalog-react@npm:^1.17.0, @backstage/plugin-catalog-react@npm:^1.19.0, @backstage/plugin-catalog-react@npm:^1.9.3": + version: 1.19.0 + resolution: "@backstage/plugin-catalog-react@npm:1.19.0" dependencies: - "@backstage/catalog-client": "npm:^1.10.0" + "@backstage/catalog-client": "npm:^1.10.1" "@backstage/catalog-model": "npm:^1.7.4" - "@backstage/core-compat-api": "npm:^0.4.2" - "@backstage/core-components": "npm:^0.17.2" - "@backstage/core-plugin-api": "npm:^1.10.7" + "@backstage/core-compat-api": "npm:^0.4.3" + "@backstage/core-components": "npm:^0.17.3" + "@backstage/core-plugin-api": "npm:^1.10.8" "@backstage/errors": "npm:^1.2.7" - "@backstage/frontend-plugin-api": "npm:^0.10.2" - "@backstage/frontend-test-utils": "npm:^0.3.2" - "@backstage/integration-react": "npm:^1.2.7" + "@backstage/frontend-plugin-api": "npm:^0.10.3" + "@backstage/frontend-test-utils": "npm:^0.3.3" + "@backstage/integration-react": "npm:^1.2.8" "@backstage/plugin-catalog-common": "npm:^1.1.4" "@backstage/plugin-permission-common": "npm:^0.9.0" - "@backstage/plugin-permission-react": "npm:^0.4.34" + "@backstage/plugin-permission-react": "npm:^0.4.35" "@backstage/types": "npm:^1.2.1" "@backstage/version-bridge": "npm:^1.0.11" "@material-ui/core": "npm:^4.12.2" @@ -6255,7 +6476,7 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10/1dec452c7bb1bd3538839a3a99943f5a5b50e5f414e31c679461e4810e5ea0e623cd41de6f08922fe577f3855459903f999c0ee0635982e1dd6bffbb46eceb08 + checksum: 10/54e77c4190675e37b4b685e5aa807461cb6d39ef4925cd3fe575b2a45a83af572aa9310996012b751e28de3f4f068330c2bfdba7acbb8ab5ce50d6f922779180 languageName: node linkType: hard @@ -6647,6 +6868,22 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-events-backend-module-kafka@workspace:plugins/events-backend-module-kafka": + version: 0.0.0-use.local + resolution: "@backstage/plugin-events-backend-module-kafka@workspace:plugins/events-backend-module-kafka" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-events-backend-test-utils": "workspace:^" + "@backstage/plugin-events-node": "workspace:^" + "@backstage/types": "workspace:^" + kafkajs: "npm:^2.2.4" + luxon: "npm:^3.0.0" + languageName: unknown + linkType: soft + "@backstage/plugin-events-backend-test-utils@workspace:^, @backstage/plugin-events-backend-test-utils@workspace:plugins/events-backend-test-utils": version: 0.0.0-use.local resolution: "@backstage/plugin-events-backend-test-utils@workspace:plugins/events-backend-test-utils" @@ -6727,6 +6964,7 @@ __metadata: "@backstage/cli": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@material-ui/core": "npm:^4.12.2" "@material-ui/icons": "npm:^4.9.1" "@rjsf/utils": "npm:5.23.2" @@ -6952,6 +7190,9 @@ __metadata: "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@types/react": "npm:^18.0.0" + "@xterm/addon-attach": "npm:^0.11.0" + "@xterm/addon-fit": "npm:^0.10.0" + "@xterm/xterm": "npm:^5.5.0" cronstrue: "npm:^2.32.0" jest-websocket-mock: "npm:^2.5.0" js-yaml: "npm:^4.1.0" @@ -6963,9 +7204,6 @@ __metadata: react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" react-use: "npm:^17.4.0" - xterm: "npm:^5.3.0" - xterm-addon-attach: "npm:^0.9.0" - xterm-addon-fit: "npm:^0.8.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 @@ -7001,6 +7239,9 @@ __metadata: "@testing-library/jest-dom": "npm:^6.0.0" "@testing-library/react": "npm:^16.0.0" "@types/react": "npm:^18.0.0" + "@xterm/addon-attach": "npm:^0.11.0" + "@xterm/addon-fit": "npm:^0.10.0" + "@xterm/xterm": "npm:^5.5.0" cronstrue: "npm:^2.2.0" js-yaml: "npm:^4.0.0" kubernetes-models: "npm:^4.1.0" @@ -7009,9 +7250,6 @@ __metadata: react: "npm:^18.0.2" react-dom: "npm:^18.0.2" react-router-dom: "npm:^6.3.0" - xterm: "npm:^5.2.1" - xterm-addon-attach: "npm:^0.9.0" - xterm-addon-fit: "npm:^0.8.0" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 react: ^17.0.0 || ^18.0.0 @@ -7023,6 +7261,26 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-mcp-actions-backend@workspace:^, @backstage/plugin-mcp-actions-backend@workspace:plugins/mcp-actions-backend": + version: 0.0.0-use.local + resolution: "@backstage/plugin-mcp-actions-backend@workspace:plugins/mcp-actions-backend" + dependencies: + "@backstage/backend-defaults": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/catalog-client": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" + "@backstage/types": "workspace:^" + "@modelcontextprotocol/sdk": "npm:^1.12.3" + "@types/express": "npm:^4.17.6" + express: "npm:^4.17.1" + express-promise-router: "npm:^4.1.0" + zod: "npm:^3.22.4" + languageName: unknown + linkType: soft + "@backstage/plugin-notifications-backend-module-email@workspace:plugins/notifications-backend-module-email": version: 0.0.0-use.local resolution: "@backstage/plugin-notifications-backend-module-email@workspace:plugins/notifications-backend-module-email" @@ -7306,7 +7564,22 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-common@npm:^0.9.0, @backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": +"@backstage/plugin-permission-common@npm:^0.9.0": + version: 0.9.0 + resolution: "@backstage/plugin-permission-common@npm:0.9.0" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/errors": "npm:^1.2.7" + "@backstage/types": "npm:^1.2.1" + cross-fetch: "npm:^4.0.0" + uuid: "npm:^11.0.0" + zod: "npm:^3.22.4" + zod-to-json-schema: "npm:^3.20.4" + checksum: 10/f165e3018cf5010a5c6ce8ec9778f2ed0024810c38e2b02f9cca637f7c37b26b23822ba5341794e4dc18a8e153ac21b9fde456dddc19150d328b5f20a4668017 + languageName: node + linkType: hard + +"@backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-common@workspace:plugins/permission-common" dependencies: @@ -7322,21 +7595,21 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-node@npm:^0.10.0": - version: 0.10.0 - resolution: "@backstage/plugin-permission-node@npm:0.10.0" +"@backstage/plugin-permission-node@npm:^0.10.1": + version: 0.10.1 + resolution: "@backstage/plugin-permission-node@npm:0.10.1" dependencies: - "@backstage/backend-plugin-api": "npm:^1.3.1" + "@backstage/backend-plugin-api": "npm:^1.4.0" "@backstage/config": "npm:^1.3.2" "@backstage/errors": "npm:^1.2.7" - "@backstage/plugin-auth-node": "npm:^0.6.3" + "@backstage/plugin-auth-node": "npm:^0.6.4" "@backstage/plugin-permission-common": "npm:^0.9.0" "@types/express": "npm:^4.17.6" express: "npm:^4.17.1" express-promise-router: "npm:^4.1.0" zod: "npm:^3.22.4" zod-to-json-schema: "npm:^3.20.4" - checksum: 10/ffd6efc8625c36c5afbde08cf31aeac56a64c3b05547c8b2ba77b6301044652d2c5c4145f8445694ec8b1155173100472f21df1a4ec80b95d454485feb35231b + checksum: 10/c10771a569d8eef7e4028703840ec5c38f918d0a3813d768b620da495a1b4f4a7ad9d9c2ae38613be637d30b61675f585365b451602e6f9f0d1d5a7b2ab3f66e languageName: node linkType: hard @@ -7363,7 +7636,27 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-react@npm:^0.4.34, @backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": +"@backstage/plugin-permission-react@npm:^0.4.35": + version: 0.4.35 + resolution: "@backstage/plugin-permission-react@npm:0.4.35" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/plugin-permission-common": "npm:^0.9.0" + swr: "npm:^2.0.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/af73b9362b3520a702a01446db2aa064cf67836ae37c187f73e1e02c11c89383b2601878aa115204a0b72a8569710efd5eb67132551fc07ea74192f33443e396 + languageName: node + linkType: hard + +"@backstage/plugin-permission-react@workspace:^, @backstage/plugin-permission-react@workspace:plugins/permission-react": version: 0.0.0-use.local resolution: "@backstage/plugin-permission-react@workspace:plugins/permission-react" dependencies: @@ -7630,7 +7923,6 @@ __metadata: "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" - "@backstage/core-app-api": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" @@ -7717,7 +8009,6 @@ __metadata: resolution: "@backstage/plugin-scaffolder-backend@workspace:plugins/scaffolder-backend" dependencies: "@backstage/backend-app-api": "workspace:^" - "@backstage/backend-common": "npm:^0.25.0" "@backstage/backend-defaults": "workspace:^" "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" @@ -7832,6 +8123,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/integration": "workspace:^" + "@backstage/plugin-permission-common": "workspace:^" "@backstage/plugin-scaffolder-common": "workspace:^" "@backstage/types": "workspace:^" "@isomorphic-git/pgp-plugin": "npm:^0.0.7" @@ -8158,6 +8450,16 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-search-common@npm:^1.2.18": + version: 1.2.18 + resolution: "@backstage/plugin-search-common@npm:1.2.18" + dependencies: + "@backstage/plugin-permission-common": "npm:^0.9.0" + "@backstage/types": "npm:^1.2.1" + checksum: 10/9a7c262a5e1fc2d1bc40a9738d6e29ba215867a10efa22c1c3e3b7847d2b6f7566fde4c22f6989933a47d2f1928ee7544ade4f88fb2e0956f4dde7cf02cb85a6 + languageName: node + linkType: hard + "@backstage/plugin-search-common@workspace:^, @backstage/plugin-search-common@workspace:plugins/search-common": version: 0.0.0-use.local resolution: "@backstage/plugin-search-common@workspace:plugins/search-common" @@ -8332,6 +8634,7 @@ __metadata: dependencies: "@backstage/cli": "workspace:^" "@backstage/core-app-api": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" @@ -8715,7 +9018,7 @@ __metadata: "@backstage/config-loader": "workspace:^" "@backstage/errors": "workspace:^" "@backstage/types": "workspace:^" - "@electric-sql/pglite": "npm:^0.2.15" + "@electric-sql/pglite": "npm:^0.3.0" "@manypkg/get-packages": "npm:^1.1.3" "@microsoft/api-documenter": "npm:^7.25.7" "@microsoft/api-extractor": "npm:^7.47.2" @@ -8772,7 +9075,36 @@ __metadata: languageName: unknown linkType: soft -"@backstage/test-utils@npm:^1.7.8, @backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": +"@backstage/test-utils@npm:^1.7.9": + version: 1.7.9 + resolution: "@backstage/test-utils@npm:1.7.9" + dependencies: + "@backstage/config": "npm:^1.3.2" + "@backstage/core-app-api": "npm:^1.17.1" + "@backstage/core-plugin-api": "npm:^1.10.8" + "@backstage/plugin-permission-common": "npm:^0.9.0" + "@backstage/plugin-permission-react": "npm:^0.4.35" + "@backstage/theme": "npm:^0.6.6" + "@backstage/types": "npm:^1.2.1" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + cross-fetch: "npm:^4.0.0" + i18next: "npm:^22.4.15" + zen-observable: "npm:^0.10.0" + peerDependencies: + "@testing-library/react": ^16.0.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/3ad5ebdbfe09138fb5b264ebc35163336d55d7a818b09754d120cd2600e1a55e5964c0fc898e111eb4fe4022eaa6b30e70c62656b3ce53c44a22d2e834970d25 + languageName: node + linkType: hard + +"@backstage/test-utils@workspace:^, @backstage/test-utils@workspace:packages/test-utils": version: 0.0.0-use.local resolution: "@backstage/test-utils@workspace:packages/test-utils" dependencies: @@ -8824,7 +9156,27 @@ __metadata: languageName: node linkType: hard -"@backstage/theme@npm:^0.6.0, @backstage/theme@npm:^0.6.6, @backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": +"@backstage/theme@npm:^0.6.0, @backstage/theme@npm:^0.6.6": + version: 0.6.6 + resolution: "@backstage/theme@npm:0.6.6" + dependencies: + "@emotion/react": "npm:^11.10.5" + "@emotion/styled": "npm:^11.10.5" + "@mui/material": "npm:^5.12.2" + peerDependencies: + "@material-ui/core": ^4.12.2 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + react-router-dom: ^6.3.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10/80297d992e853b3882fd158d192844a85738e89da6db7b6b47d27744129f04ea3c9ec946cb08fe226627f5e882670464d14bd07234e65debe98b3572eccbee15 + languageName: node + linkType: hard + +"@backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": version: 0.0.0-use.local resolution: "@backstage/theme@workspace:packages/theme" dependencies: @@ -8975,9 +9327,9 @@ __metadata: languageName: node linkType: hard -"@changesets/assemble-release-plan@npm:^6.0.8": - version: 6.0.8 - resolution: "@changesets/assemble-release-plan@npm:6.0.8" +"@changesets/assemble-release-plan@npm:^6.0.9": + version: 6.0.9 + resolution: "@changesets/assemble-release-plan@npm:6.0.9" dependencies: "@changesets/errors": "npm:^0.2.0" "@changesets/get-dependents-graph": "npm:^2.1.3" @@ -8985,7 +9337,7 @@ __metadata: "@changesets/types": "npm:^6.1.0" "@manypkg/get-packages": "npm:^1.1.3" semver: "npm:^7.5.3" - checksum: 10/5d01fc42c67229874cc70b93fbdc971e11909aa7a72f1909c585ecb3fdc69f3ac105d243e1341cd5b07c02dee133be461fa48138125f00d137e71f8b7e8f428e + checksum: 10/f84656eabb700ed77f97751b282e1701636ed45a44b443abd9af0291870495cc046fee301478010f39a1dc455799065ae007b9d7d2bb5ae8b793b65bbb8e052a languageName: node linkType: hard @@ -8999,16 +9351,16 @@ __metadata: linkType: hard "@changesets/cli@npm:^2.14.0": - version: 2.29.4 - resolution: "@changesets/cli@npm:2.29.4" + version: 2.29.5 + resolution: "@changesets/cli@npm:2.29.5" dependencies: "@changesets/apply-release-plan": "npm:^7.0.12" - "@changesets/assemble-release-plan": "npm:^6.0.8" + "@changesets/assemble-release-plan": "npm:^6.0.9" "@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.12" + "@changesets/get-release-plan": "npm:^4.0.13" "@changesets/git": "npm:^3.0.4" "@changesets/logger": "npm:^0.1.1" "@changesets/pre": "npm:^2.0.2" @@ -9032,7 +9384,7 @@ __metadata: term-size: "npm:^2.1.0" bin: changeset: bin.js - checksum: 10/fc325447b81a811464107e72a687f6c0414c5f928e518cb122d1efde1d71c205b1972464795ab97fb26087900ea55b99a551e9a010c9681def3d7561fd1c3f0b + checksum: 10/f401da29025d7bcc07b732bb09a9627f785bfc21c7c2005861d11ffea732bc14d33394fc2fcae50cc5f2b710f6080c5babe2fa90d432de5fdb47ae6afc147936 languageName: node linkType: hard @@ -9072,17 +9424,17 @@ __metadata: languageName: node linkType: hard -"@changesets/get-release-plan@npm:^4.0.12": - version: 4.0.12 - resolution: "@changesets/get-release-plan@npm:4.0.12" +"@changesets/get-release-plan@npm:^4.0.13": + version: 4.0.13 + resolution: "@changesets/get-release-plan@npm:4.0.13" dependencies: - "@changesets/assemble-release-plan": "npm:^6.0.8" + "@changesets/assemble-release-plan": "npm:^6.0.9" "@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/d6482ecb6f1c2c47266493a36d05b484f0950d0a4472820649e953d073e3fdd612cdd8a4df9e3d7e00756d4e446dae639f9d6e0dab8a25f76bb6df77cd91c21c + checksum: 10/9983fae5a68012c4c418ddd62f2fb3d325363f21160252ff7b868503a1a2effb8fdd32e4a0289b72653afc3605ce19d163ff69205c942a0004efb571a5f78fd0 languageName: node linkType: hard @@ -9218,8 +9570,8 @@ __metadata: linkType: hard "@codemirror/language@npm:^6.0.0": - version: 6.11.0 - resolution: "@codemirror/language@npm:6.11.0" + version: 6.11.1 + resolution: "@codemirror/language@npm:6.11.1" dependencies: "@codemirror/state": "npm:^6.0.0" "@codemirror/view": "npm:^6.23.0" @@ -9227,7 +9579,7 @@ __metadata: "@lezer/highlight": "npm:^1.0.0" "@lezer/lr": "npm:^1.0.0" style-mod: "npm:^4.0.0" - checksum: 10/571d8d18c51173c25dc3a1c1327fa2265e3d666acd2f2e3dbab828f49bc7f350159bbe8d46ee4baf88cb3eb0fbcfb3351028df6e2981455a55fd2951254c950f + checksum: 10/024969113d61ccb5d497b75a8a9875d43e1bfc8466de2819bb5db23f01b200937366800a812e8d33eb5e34e3a5d2aa22d0ff8205f34a5e8aeb4cd1d221bcaa78 languageName: node linkType: hard @@ -9284,13 +9636,14 @@ __metadata: linkType: hard "@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.23.0": - version: 6.36.8 - resolution: "@codemirror/view@npm:6.36.8" + version: 6.37.2 + resolution: "@codemirror/view@npm:6.37.2" dependencies: "@codemirror/state": "npm:^6.5.0" + crelt: "npm:^1.0.6" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10/cb2cbfb8a41ee1d6cfb17c2ea91b9763e692f85d523b55276cc46ab7fda3f6ba0bcc03e2194ad6cf6b413ec34d309ab408300c19b62d5d6d6f9e2071fb99d489 + checksum: 10/410ef68c5bd3395c556f5a1298afd6a37eecd88b825435dc19ce7143b0eb5c4114486b444e56fd156371fcc329ad9425635c0703322384ff582a75a4d61727a7 languageName: node linkType: hard @@ -9322,11 +9675,11 @@ __metadata: linkType: hard "@dagrejs/dagre@npm:^1.1.4": - version: 1.1.4 - resolution: "@dagrejs/dagre@npm:1.1.4" + version: 1.1.5 + resolution: "@dagrejs/dagre@npm:1.1.5" dependencies: "@dagrejs/graphlib": "npm:2.2.4" - checksum: 10/0b3744b170c68ae0666e03aca19c3100d5131feafeb54b3ea096b749a9f0fe5385b8bd8889c11a49493cfab945b2486b9e30bc41b321755ed718e9f5cb4b74f1 + checksum: 10/c00abd1e04d19f90ad8dfa0a4e16365371bc4309affead3827a1b39f6b0b946643b8af0b1e5519011deca3fda4c7471b27e9ebb03423309a94f95ac0b881ac4f languageName: node linkType: hard @@ -9355,15 +9708,25 @@ __metadata: languageName: node linkType: hard -"@davidzemon/passport-okta-oauth@npm:^0.0.5": - version: 0.0.5 - resolution: "@davidzemon/passport-okta-oauth@npm:0.0.5" +"@davidzemon/passport-okta-oauth@npm:^0.0.6": + version: 0.0.6 + resolution: "@davidzemon/passport-okta-oauth@npm:0.0.6" dependencies: "@types/passport-oauth2": "npm:^1.4.11" passport-oauth2: "npm:^1.6.1" pkginfo: "npm:^0.4.1" uid2: "npm:^1.0.0" - checksum: 10/6932aca4213f1e47c41454cbb9ef1309643ffa14fa3da401f18e397e76f3fa1669be76d96db65dd6435854d7f05b30d3a27f645d10e3c6ed955ad30930d47b3e + checksum: 10/d29a26459ff387d7507c031a6814e3581eb47e2dc9dda6848667362973455cd125e2a251b30ad6c093dfd6cd8ee7adf48301f395808b212008a6ee702a507354 + languageName: node + linkType: hard + +"@dependents/detective-less@npm:^5.0.1": + version: 5.0.1 + resolution: "@dependents/detective-less@npm:5.0.1" + dependencies: + gonzales-pe: "npm:^4.3.0" + node-source-walk: "npm:^7.0.1" + checksum: 10/647d4427f6aad119bdfea607ab19ca30be827f7f0462677b5a538d8f8ab4ebda2c05ece60fceb5d3abb093e69ab3393e24a8e966936d2745b07421cfd014b14f languageName: node linkType: hard @@ -9390,13 +9753,20 @@ __metadata: languageName: node linkType: hard -"@electric-sql/pglite@npm:^0.2.14, @electric-sql/pglite@npm:^0.2.15": +"@electric-sql/pglite@npm:^0.2.14": version: 0.2.17 resolution: "@electric-sql/pglite@npm:0.2.17" checksum: 10/cb04d18614c9a3d985fa63f5ca889104eab55b15a9aa569df264e335ea559f2279ee64c1284aa68d5b92ff516dd37ae553299cf55c226a3e541d6c46416c7160 languageName: node linkType: hard +"@electric-sql/pglite@npm:^0.3.0": + version: 0.3.3 + resolution: "@electric-sql/pglite@npm:0.3.3" + checksum: 10/fa2b8698c4edb4716b34efd2e8ef1847e5b0c8059afffb21ad5b7cc21dcdca9d86075ac4f1798f1e83a5b2cc1ee8902e1d322fe63d761e065089c7da494ea089 + languageName: node + linkType: hard + "@emotion/babel-plugin@npm:^11.13.5": version: 11.13.5 resolution: "@emotion/babel-plugin@npm:11.13.5" @@ -9587,9 +9957,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/aix-ppc64@npm:0.25.4" +"@esbuild/aix-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/aix-ppc64@npm:0.25.5" conditions: os=aix & cpu=ppc64 languageName: node linkType: hard @@ -9601,9 +9971,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-arm64@npm:0.25.4" +"@esbuild/android-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm64@npm:0.25.5" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -9615,9 +9985,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-arm@npm:0.25.4" +"@esbuild/android-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-arm@npm:0.25.5" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -9629,9 +9999,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/android-x64@npm:0.25.4" +"@esbuild/android-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/android-x64@npm:0.25.5" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -9643,9 +10013,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/darwin-arm64@npm:0.25.4" +"@esbuild/darwin-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-arm64@npm:0.25.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -9657,9 +10027,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/darwin-x64@npm:0.25.4" +"@esbuild/darwin-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/darwin-x64@npm:0.25.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -9671,9 +10041,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/freebsd-arm64@npm:0.25.4" +"@esbuild/freebsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-arm64@npm:0.25.5" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -9685,9 +10055,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/freebsd-x64@npm:0.25.4" +"@esbuild/freebsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/freebsd-x64@npm:0.25.5" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -9699,9 +10069,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-arm64@npm:0.25.4" +"@esbuild/linux-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm64@npm:0.25.5" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -9713,9 +10083,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-arm@npm:0.25.4" +"@esbuild/linux-arm@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-arm@npm:0.25.5" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -9727,9 +10097,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-ia32@npm:0.25.4" +"@esbuild/linux-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ia32@npm:0.25.5" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -9741,9 +10111,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-loong64@npm:0.25.4" +"@esbuild/linux-loong64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-loong64@npm:0.25.5" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -9755,9 +10125,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-mips64el@npm:0.25.4" +"@esbuild/linux-mips64el@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-mips64el@npm:0.25.5" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -9769,9 +10139,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-ppc64@npm:0.25.4" +"@esbuild/linux-ppc64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-ppc64@npm:0.25.5" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -9783,9 +10153,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-riscv64@npm:0.25.4" +"@esbuild/linux-riscv64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-riscv64@npm:0.25.5" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -9797,9 +10167,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-s390x@npm:0.25.4" +"@esbuild/linux-s390x@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-s390x@npm:0.25.5" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -9811,16 +10181,16 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/linux-x64@npm:0.25.4" +"@esbuild/linux-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/linux-x64@npm:0.25.5" conditions: os=linux & cpu=x64 languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/netbsd-arm64@npm:0.25.4" +"@esbuild/netbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-arm64@npm:0.25.5" conditions: os=netbsd & cpu=arm64 languageName: node linkType: hard @@ -9832,16 +10202,16 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/netbsd-x64@npm:0.25.4" +"@esbuild/netbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/netbsd-x64@npm:0.25.5" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/openbsd-arm64@npm:0.25.4" +"@esbuild/openbsd-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-arm64@npm:0.25.5" conditions: os=openbsd & cpu=arm64 languageName: node linkType: hard @@ -9853,9 +10223,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/openbsd-x64@npm:0.25.4" +"@esbuild/openbsd-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/openbsd-x64@npm:0.25.5" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -9867,9 +10237,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/sunos-x64@npm:0.25.4" +"@esbuild/sunos-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/sunos-x64@npm:0.25.5" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -9881,9 +10251,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-arm64@npm:0.25.4" +"@esbuild/win32-arm64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-arm64@npm:0.25.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -9895,9 +10265,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-ia32@npm:0.25.4" +"@esbuild/win32-ia32@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-ia32@npm:0.25.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -9909,9 +10279,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.4": - version: 0.25.4 - resolution: "@esbuild/win32-x64@npm:0.25.4" +"@esbuild/win32-x64@npm:0.25.5": + version: 0.25.5 + resolution: "@esbuild/win32-x64@npm:0.25.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -10089,16 +10459,16 @@ __metadata: languageName: node linkType: hard -"@gerrit0/mini-shiki@npm:^3.2.2": - version: 3.4.0 - resolution: "@gerrit0/mini-shiki@npm:3.4.0" +"@gerrit0/mini-shiki@npm:^3.7.0": + version: 3.7.0 + resolution: "@gerrit0/mini-shiki@npm:3.7.0" dependencies: - "@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/engine-oniguruma": "npm:^3.7.0" + "@shikijs/langs": "npm:^3.7.0" + "@shikijs/themes": "npm:^3.7.0" + "@shikijs/types": "npm:^3.7.0" "@shikijs/vscode-textmate": "npm:^10.0.2" - checksum: 10/2e116b8a6cb4d63db0fc8d96ba696befb4b4d66d4eaf4673c2585e798ae871e7e7cc581252cdc08169e5c19ae1aa496d93286da7626a36f7b0d298c660178855 + checksum: 10/d9af211439551e2198309b6b3a274174ef7f84ab74d55872a6ab0302fe5b41a2000eaad65574ef5cd8fedad13d5a099e64863d489a991df7a3efd8e3250f8912 languageName: node linkType: hard @@ -10169,14 +10539,14 @@ __metadata: linkType: hard "@google-cloud/cloud-sql-connector@npm:^1.4.0": - version: 1.8.0 - resolution: "@google-cloud/cloud-sql-connector@npm:1.8.0" + version: 1.8.1 + resolution: "@google-cloud/cloud-sql-connector@npm:1.8.1" dependencies: - "@googleapis/sqladmin": "npm:^28.0.0" + "@googleapis/sqladmin": "npm:^29.0.0" gaxios: "npm:^6.1.1" google-auth-library: "npm:^9.2.0" p-throttle: "npm:^7.0.0" - checksum: 10/a2cd85beb822a37e1eaabd556e2f540f89c41a1614e520b8c120b8107d7edb3c335dcbf62fc34dd8973ae091a2a06882780ae6510c45f2b702a59df4953ba427 + checksum: 10/e13e2e47993d74c770c73318a73047b017266f6f8102e26ebcf90c484fd78ba6141b0fc60f4eced55d438150655b8514423c19e3f30da73a1aaf1c77baadd0ab languageName: node linkType: hard @@ -10278,12 +10648,12 @@ __metadata: languageName: node linkType: hard -"@googleapis/sqladmin@npm:^28.0.0": - version: 28.0.0 - resolution: "@googleapis/sqladmin@npm:28.0.0" +"@googleapis/sqladmin@npm:^29.0.0": + version: 29.0.0 + resolution: "@googleapis/sqladmin@npm:29.0.0" dependencies: googleapis-common: "npm:^7.0.0" - checksum: 10/ee4074b63d45276e0513a75b461bbf3d39abcb894e5eb660f0f28a9d0665c6638079958f04cb171767c69bcbb324ecdd2033199e6ecff61bf10d6a09adecdd16 + checksum: 10/3bf9eea2e6333d394da88404172aeed5ee5fca5fe2ebbba84d427990aab0083281a4cb948c023817ae6dbb618a1e43a296176c7703badd66a60bde695d7c5b3f languageName: node linkType: hard @@ -10902,40 +11272,40 @@ __metadata: languageName: unknown linkType: soft -"@internationalized/date@npm:^3.7.0": - version: 3.7.0 - resolution: "@internationalized/date@npm:3.7.0" +"@internationalized/date@npm:^3.8.2": + version: 3.8.2 + resolution: "@internationalized/date@npm:3.8.2" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10/59adffb57f1391e17bfe9b2e9b1a71c0957a1835b0176a86ad5a99fd2f2919f094d3f553fb39be157d84c315a88917e0eda779c9101026e3bfac217ace18a559 + checksum: 10/ca281d8785acff520ba4782d4eb0c542bb6c2783dbd3eb5457aeddbb68b9674f860fc00c6dd467f39c12eff9a04381742b07f0b9e210a115c094507e046d7b5a languageName: node linkType: hard -"@internationalized/message@npm:^3.1.6": - version: 3.1.6 - resolution: "@internationalized/message@npm:3.1.6" +"@internationalized/message@npm:^3.1.8": + version: 3.1.8 + resolution: "@internationalized/message@npm:3.1.8" dependencies: "@swc/helpers": "npm:^0.5.0" intl-messageformat: "npm:^10.1.0" - checksum: 10/31611a81bba7c0b18fad02ccb363632cbef3a0cc6e6e564578dfc7ae42cfde48b0ec18b89bff24390bb748df63d40cd2028483068e7d1492aee0533648a71e04 + checksum: 10/8f27a31f5d1eef7084447ed141e896e12cc19d786a1346ba60137de5b8bcc58a9da978d79954e2a74302aa673f942fb772130ebd6195565e33db30bd7eb4ef47 languageName: node linkType: hard -"@internationalized/number@npm:^3.6.0": - version: 3.6.0 - resolution: "@internationalized/number@npm:3.6.0" +"@internationalized/number@npm:^3.6.3": + version: 3.6.3 + resolution: "@internationalized/number@npm:3.6.3" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10/76428f75fd289008580108beef02816f94aaca37d48c9e26c2274fdbb81e8f32c4247ff624b8f854384b18ce2556cc8d817ccf1bc39b0fd87b0da1f3d911209b + checksum: 10/b6f1793d12f89aca4c5bd88b60643739c4b02cade50a42c61192a2a5e0a913088ee35e4013b7ef3f675bdc919ebaf9a1631f1c40b1ceffd998f3fc14201a01c3 languageName: node linkType: hard -"@internationalized/string@npm:^3.2.5": - version: 3.2.5 - resolution: "@internationalized/string@npm:3.2.5" +"@internationalized/string@npm:^3.2.7": + version: 3.2.7 + resolution: "@internationalized/string@npm:3.2.7" dependencies: "@swc/helpers": "npm:^0.5.0" - checksum: 10/7a20359796545cd5724aa546018554b81ea168c58738e98884c54ef0de2fba7b802504c79678d690709b0a435478912aa771c426feed72aae1d93ee782b7ac5d + checksum: 10/38b54817cf125ba88d1136a6bca4fb57c46672d26d21490f838efe928049546800df6d9c8048411696455fc8caacb8ac23c2de2a1b61f2258b1302c1c97cc128 languageName: node linkType: hard @@ -10953,6 +11323,22 @@ __metadata: languageName: node linkType: hard +"@isaacs/balanced-match@npm:^4.0.1": + version: 4.0.1 + resolution: "@isaacs/balanced-match@npm:4.0.1" + checksum: 10/102fbc6d2c0d5edf8f6dbf2b3feb21695a21bc850f11bc47c4f06aa83bd8884fde3fe9d6d797d619901d96865fdcb4569ac2a54c937992c48885c5e3d9967fe8 + languageName: node + linkType: hard + +"@isaacs/brace-expansion@npm:^5.0.0": + version: 5.0.0 + resolution: "@isaacs/brace-expansion@npm:5.0.0" + dependencies: + "@isaacs/balanced-match": "npm:^4.0.1" + checksum: 10/cf3b7f206aff12128214a1df764ac8cdbc517c110db85249b945282407e3dfc5c6e66286383a7c9391a059fc8e6e6a8ca82262fc9d2590bd615376141fbebd2d + languageName: node + linkType: hard + "@isaacs/cliui@npm:^8.0.2": version: 8.0.2 resolution: "@isaacs/cliui@npm:8.0.2" @@ -11414,23 +11800,25 @@ __metadata: linkType: hard "@keyv/memcache@npm:^1.3.5": - version: 1.3.7 - resolution: "@keyv/memcache@npm:1.3.7" + version: 1.4.1 + resolution: "@keyv/memcache@npm:1.4.1" dependencies: json-buffer: "npm:^3.0.1" - memjs: "npm:^1.3.1" - checksum: 10/61c836c1b0c264c35281a38dbc380a102b2899eab46226a0e31614d3d7c4708ecdb06d8858f711893212ae5b357a79213c7a7b7a81b6196e56dd889dd1ca498b + memjs: "npm:^1.3.2" + checksum: 10/a3db21549ec445925c28419b19210984875aa03d1ce513792064336c7785c0e6d3ac015e71ca328a2e83f4ed409475f057a37d86c7fb2a11832740b024bcc3ff languageName: node linkType: hard "@keyv/memcache@npm:^2.0.1": - version: 2.0.1 - resolution: "@keyv/memcache@npm:2.0.1" + version: 2.0.2 + resolution: "@keyv/memcache@npm:2.0.2" dependencies: - "@keyv/serialize": "npm:*" + "@keyv/serialize": "npm:^1.0.3" buffer: "npm:^6.0.3" memjs: "npm:^1.3.2" - checksum: 10/618fd2441cb7e0b71c6af37b87bddc39d3439c9863def47cff8c75c9c05ca45e035bfc1bfa140a661befce5a06a491f25388aab6d330c0eaefe7b87534274c48 + peerDependencies: + keyv: ^5.3.4 + checksum: 10/8328bfa4a348d26c1aa1a563561a50114f75288af5fe18e8dc2ce6ff2f7f6aa63c27f8293c5e8b35bc0dcc9740159e56d8ff1b4015fc6a56e18d62850a8a9b83 languageName: node linkType: hard @@ -11444,31 +11832,32 @@ __metadata: linkType: hard "@keyv/redis@npm:^4.0.1": - version: 4.1.0 - resolution: "@keyv/redis@npm:4.1.0" + version: 4.4.1 + resolution: "@keyv/redis@npm:4.4.1" dependencies: + "@redis/client": "npm:^1.6.0" cluster-key-slot: "npm:^1.1.2" - keyv: "npm:^5.2.1" - redis: "npm:^4.7.0" - checksum: 10/5f1d6c6596829bbae5c6369dff6e70de4d7f91c7c8312585b2bef336638582c9969c792a4b328ee07b80b50136e67769fffd844e237d4db32a44ea7de20fb3f5 + peerDependencies: + keyv: ^5.3.4 + checksum: 10/85bf5830d5a19f45fbba864f7eb830cff0059f3f7c59c711a65b439821008a3321a94fb06c4d1f09295f0f233422586458652d266696c0925b5c8d1d695700ec languageName: node linkType: hard -"@keyv/serialize@npm:*, @keyv/serialize@npm:^1.0.2": - version: 1.0.2 - resolution: "@keyv/serialize@npm:1.0.2" +"@keyv/serialize@npm:^1.0.3": + version: 1.0.3 + resolution: "@keyv/serialize@npm:1.0.3" dependencies: buffer: "npm:^6.0.3" - checksum: 10/6a42a5778a6b4542f6903ba7e6a17c5bd116441798d75c95fba9908c76c7606db527fad710b5c54abc6175e49b1bbaaafe3b836ad4b91e1af701394134f1d504 + checksum: 10/d6a9194dd781bc26cc4d55f392d843810c1fdc0da81e69203e633cb289fc0a8edc8bc6466f66c4cbb55da0a5b405e89f14a68b48d6e73919ae82f8249fb5e444 languageName: node linkType: hard "@keyv/valkey@npm:^1.0.1": - version: 1.0.3 - resolution: "@keyv/valkey@npm:1.0.3" + version: 1.0.6 + resolution: "@keyv/valkey@npm:1.0.6" dependencies: - iovalkey: "npm:^0.3.1" - checksum: 10/ff6ba62e4d19c426e45a1437fe215ed2baddc58e811d97507dd75ead0058c3105d679c8c7c4241ddf732abe56320357c2e568c014620e50d7c0eaef1f2528b88 + iovalkey: "npm:^0.3.3" + checksum: 10/36e48930928845e6a14dfb4f9f8069851ff10d3aeef62277f78c8dcea735f8dca5faf4399f67fc2730231b24270d9ab6be77a46e122e9006d3f6e55cc5c0b97a languageName: node linkType: hard @@ -11960,6 +12349,25 @@ __metadata: languageName: node linkType: hard +"@modelcontextprotocol/sdk@npm:^1.12.3": + version: 1.13.1 + resolution: "@modelcontextprotocol/sdk@npm:1.13.1" + dependencies: + ajv: "npm:^6.12.6" + content-type: "npm:^1.0.5" + cors: "npm:^2.8.5" + cross-spawn: "npm:^7.0.5" + 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/b516d72e1cd14c67c8a2e5cb95fcc1c03c50be989850e3e963a7ed11000acb604e65efeaad47ea93c847130536ee51859a8d34e6dbe99d408e1a24224592e57f + languageName: node + linkType: hard + "@module-federation/bridge-react-webpack-plugin@npm:0.9.1": version: 0.9.1 resolution: "@module-federation/bridge-react-webpack-plugin@npm:0.9.1" @@ -12046,10 +12454,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/error-codes@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/error-codes@npm:0.13.1" - checksum: 10/fa9bbc0936ad9d037116c05f741bfb0ff741c3e2eaa682fa0132f4825d66ef0c63da0c20ec931ca16c8e7b660c1fdc60101a24ccab8951f19e07fba642aa1c6a +"@module-federation/error-codes@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/error-codes@npm:0.14.3" + checksum: 10/3b6c25940d966648c2531b80edabafd36eddccdd8f68ba6702686a1e4fd2dbaa4a34ef6fb22a4d8a615b907e907e233dd0fd312c49a7369a49669bc1ab414a19 languageName: node linkType: hard @@ -12117,13 +12525,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-core@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/runtime-core@npm:0.13.1" +"@module-federation/runtime-core@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/runtime-core@npm:0.14.3" dependencies: - "@module-federation/error-codes": "npm:0.13.1" - "@module-federation/sdk": "npm:0.13.1" - checksum: 10/81b52eb7866f623115922af325bcc1dba6c85f95216dfa2cbf2f777b37ec11ba856846892a4a5ab446105a18837c495b0a05cb5d574f7d2e50f340d1e57413f4 + "@module-federation/error-codes": "npm:0.14.3" + "@module-federation/sdk": "npm:0.14.3" + checksum: 10/7e974a8c051334929e28231fe1552489258ab1b545f0f5ad59644b0288765642f3e908b48c3cc59bc74a326f4f92325788b1a898f89d8f795f8036aa812b2bb6 languageName: node linkType: hard @@ -12137,13 +12545,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime-tools@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/runtime-tools@npm:0.13.1" +"@module-federation/runtime-tools@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/runtime-tools@npm:0.14.3" dependencies: - "@module-federation/runtime": "npm:0.13.1" - "@module-federation/webpack-bundler-runtime": "npm:0.13.1" - checksum: 10/821f32979ef2235cd043b3ef561fd63f7554211bd86d3af7e17a57dd5c709c6ee2ae57935222318694732115d7771c122cf51fb9f71f879269d452bc5ab0a8d2 + "@module-federation/runtime": "npm:0.14.3" + "@module-federation/webpack-bundler-runtime": "npm:0.14.3" + checksum: 10/09bc9d482ead7d38d5f9f6e1d45c85f257833302315447b2a17c26dc44046f8aa0563ffdb2f5addbddbf5f8f972c13dc8e80932a8a34780738232332bbdf5ce9 languageName: node linkType: hard @@ -12157,14 +12565,14 @@ __metadata: languageName: node linkType: hard -"@module-federation/runtime@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/runtime@npm:0.13.1" +"@module-federation/runtime@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/runtime@npm:0.14.3" dependencies: - "@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 + "@module-federation/error-codes": "npm:0.14.3" + "@module-federation/runtime-core": "npm:0.14.3" + "@module-federation/sdk": "npm:0.14.3" + checksum: 10/b7009953998f2624f5ece3da4174d8427d30b2af3b108154f0fa2365aa6c08d167e23b9771508f2c6038a397f21e0e26b93dd09f3d4b7b90eac6b0ee7f1b9ee2 languageName: node linkType: hard @@ -12179,10 +12587,10 @@ __metadata: languageName: node linkType: hard -"@module-federation/sdk@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/sdk@npm:0.13.1" - checksum: 10/7cfc98a6ee09aa338a9d76df146d064b5f4c6163b985daf3aa641e3f0e52d7cc40116b449cd1265b1333a66eeec31ce8bb40e1944f3ac62797d87323ce0e91ec +"@module-federation/sdk@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/sdk@npm:0.14.3" + checksum: 10/f359fa15ecebe6e1e80e8fbd4d4969ae2d2d92f0d3e338443c45fba4772327368e086b108adcec585763d974e7462227a9e51b0531826292e2249f2612d0f822 languageName: node linkType: hard @@ -12204,13 +12612,13 @@ __metadata: languageName: node linkType: hard -"@module-federation/webpack-bundler-runtime@npm:0.13.1": - version: 0.13.1 - resolution: "@module-federation/webpack-bundler-runtime@npm:0.13.1" +"@module-federation/webpack-bundler-runtime@npm:0.14.3": + version: 0.14.3 + resolution: "@module-federation/webpack-bundler-runtime@npm:0.14.3" dependencies: - "@module-federation/runtime": "npm:0.13.1" - "@module-federation/sdk": "npm:0.13.1" - checksum: 10/bebccaa4411c00355bb399e2dddb946586967642b90da4d4436554093fecb614fdff6c65dc73cef944c7344f169d2c0ecc22ed0b5b899e21d4535507806145e0 + "@module-federation/runtime": "npm:0.14.3" + "@module-federation/sdk": "npm:0.14.3" + checksum: 10/016d1c51e82e230b2d33f67aba7c9ff2cd9f939306a48dfa3aeb0f3939dd82f797e06b478c009459d7516fadbf43024cd0fc13618555a64c470192d9b87ab926 languageName: node linkType: hard @@ -12315,9 +12723,9 @@ __metadata: languageName: node linkType: hard -"@mswjs/interceptors@npm:^0.37.0": - version: 0.37.1 - resolution: "@mswjs/interceptors@npm:0.37.1" +"@mswjs/interceptors@npm:^0.39.1": + version: 0.39.2 + resolution: "@mswjs/interceptors@npm:0.39.2" dependencies: "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/logger": "npm:^0.3.0" @@ -12325,7 +12733,7 @@ __metadata: is-node-process: "npm:^1.2.0" outvariant: "npm:^1.4.3" strict-event-emitter: "npm:^0.5.1" - checksum: 10/332d8aa50beb4834ccbda6a800ca00b1204adc0eba23e1c1f7bb9f4e564a92707e563f7a2424d4a8607404ec91424e5d8c34a87c250b191ca7b24dff12eba2c5 + checksum: 10/faaa95d636363a197f125c32066457fa74d5063d8ccae4c9c0e0510179060d92b1faf8640df45a0623e0bf42a30d610c83364a58e0eb0ca412c87b2e835936c1 languageName: node linkType: hard @@ -12516,18 +12924,18 @@ __metadata: languageName: node linkType: hard -"@nestjs/common@npm:11.0.20": - version: 11.0.20 - resolution: "@nestjs/common@npm:11.0.20" +"@nestjs/common@npm:11.1.3": + version: 11.1.3 + resolution: "@nestjs/common@npm:11.1.3" dependencies: - file-type: "npm:20.4.1" + file-type: "npm:21.0.0" iterare: "npm:1.2.1" load-esm: "npm:1.0.2" tslib: "npm:2.8.1" uid: "npm:2.0.2" peerDependencies: - class-transformer: "*" - class-validator: "*" + class-transformer: ">=0.4.1" + class-validator: ">=0.13.2" reflect-metadata: ^0.1.12 || ^0.2.0 rxjs: ^7.1.0 peerDependenciesMeta: @@ -12535,13 +12943,13 @@ __metadata: optional: true class-validator: optional: true - checksum: 10/ab06f3d910a86351e895829773b28236dcb4a80a3eba1ed902c51dbc44db9d40f619d5f8713c5ce42518fc95633bbe9c80b1c7188ffc4fd3d5d934b561e2f495 + checksum: 10/b1c9699cdf9cee96ed0178e5eed3b1829c210bbdbd703bb9e1e0def2c7456273d73329635439b16164e43de8a8d54dd5b7d1dd5dba65417d746bd731b8c5d685 languageName: node linkType: hard -"@nestjs/core@npm:11.0.20": - version: 11.0.20 - resolution: "@nestjs/core@npm:11.0.20" +"@nestjs/core@npm:11.1.3": + version: 11.1.3 + resolution: "@nestjs/core@npm:11.1.3" dependencies: "@nuxt/opencollective": "npm:0.4.1" fast-safe-stringify: "npm:2.1.1" @@ -12563,7 +12971,7 @@ __metadata: optional: true "@nestjs/websockets": optional: true - checksum: 10/0e18e6744eaa912442d43d6a8d5543bc4b7e09d350943881d76110aa53a0d26876818898d1ddb71f92d24e6f7154ab3264c6c5604c9670221a5f63552e61d431 + checksum: 10/b46a9f877170f7e96429da5970684525277bcb341f3a24032b6748ad503b5c7164b77410daf9c0f3ca99b5f6b99985d5eb739501cfc01c941218397f32f2d095 languageName: node linkType: hard @@ -13282,10 +13690,10 @@ __metadata: languageName: node linkType: hard -"@octokit/openapi-types@npm:^22.2.0": - version: 22.2.0 - resolution: "@octokit/openapi-types@npm:22.2.0" - checksum: 10/0471b0c789fada5aa2390e6f82ba477738228ef7d2d986dda9aab0cb625d1562bd178ba0ba4d2655ce841079cd5efff9e58ece2077c27e569ea22109ea301830 +"@octokit/openapi-types@npm:^24.2.0": + version: 24.2.0 + resolution: "@octokit/openapi-types@npm:24.2.0" + checksum: 10/000897ebc6e247c2591049d6081e95eb5636f73798dadd695ee6048496772b58065df88823e74a760201828545a7ac601dd3c1bcd2e00079a62a9ee9d389409c languageName: node linkType: hard @@ -13298,14 +13706,14 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-paginate-rest@npm:11.3.1": - version: 11.3.1 - resolution: "@octokit/plugin-paginate-rest@npm:11.3.1" +"@octokit/plugin-paginate-rest@npm:11.4.4-cjs.2": + version: 11.4.4-cjs.2 + resolution: "@octokit/plugin-paginate-rest@npm:11.4.4-cjs.2" dependencies: - "@octokit/types": "npm:^13.5.0" + "@octokit/types": "npm:^13.7.0" peerDependencies: "@octokit/core": 5 - checksum: 10/82f5bcc3a536a44bed0a205c8301176c0d210b7a1c6d035a79b31a102e2e02f46234a38629cc984a21be544194ac69151814e9a909416aa7389cdffd1297bcd9 + checksum: 10/e0f696b3b69febe4e7c736d909065871f38bb8346a07f19a9c83246a02972568ac672667db472f846baef20a9611adf26ce8f0f189a11004c4b6618765078e19 languageName: node linkType: hard @@ -13341,14 +13749,14 @@ __metadata: languageName: node linkType: hard -"@octokit/plugin-rest-endpoint-methods@npm:13.2.2": - version: 13.2.2 - resolution: "@octokit/plugin-rest-endpoint-methods@npm:13.2.2" +"@octokit/plugin-rest-endpoint-methods@npm:13.3.2-cjs.1": + version: 13.3.2-cjs.1 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:13.3.2-cjs.1" dependencies: - "@octokit/types": "npm:^13.5.0" + "@octokit/types": "npm:^13.8.0" peerDependencies: "@octokit/core": ^5 - checksum: 10/9eccc1a22aa0b65f3f9378f26a74c386683db420c33202998918df1eef492e93212e1849e1d85530f425602663cfc2bfbf385a30991b8a04470334c74ba2386b + checksum: 10/479827e62466e55bc1a50129d51597807bddc6c909e56be9e8dd9c1a91efa0f466a2f56b7d80438649e21ab0a3a195f840b3fccf2ae7f11fb0a919db8e62bc62 languageName: node linkType: hard @@ -13490,12 +13898,12 @@ __metadata: languageName: node linkType: hard -"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0, @octokit/types@npm:^13.5.0": - version: 13.6.2 - resolution: "@octokit/types@npm:13.6.2" +"@octokit/types@npm:^13.0.0, @octokit/types@npm:^13.1.0, @octokit/types@npm:^13.7.0, @octokit/types@npm:^13.8.0": + version: 13.10.0 + resolution: "@octokit/types@npm:13.10.0" dependencies: - "@octokit/openapi-types": "npm:^22.2.0" - checksum: 10/8e614796f3554d28dfb77c570e80ef52d68ef311bdd4614ec263f8ea2266b9c06d4f7963fe2989f32cbfe4ea0c05e13eba9a64a6e0f64afb997cd975af154d52 + "@octokit/openapi-types": "npm:^24.2.0" + checksum: 10/32f8f5010d7faae128b0cdd0c221f0ca8c3781fe44483ecd87162b3da507db667f7369acda81340f6e2c9c374d9a938803409c6085c2c01d98210b6c58efb99a languageName: node linkType: hard @@ -13542,10 +13950,10 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks-methods@npm:^4.0.0": - version: 4.0.0 - resolution: "@octokit/webhooks-methods@npm:4.0.0" - checksum: 10/f26892ed868488bf08d5be1fdacbc51f5b6ba84cef21067e0b1ff969d087202989e74303049691a77c75bb1940e7835ad6522b0f4f151ceef015e6d083789c80 +"@octokit/webhooks-methods@npm:^4.1.0": + version: 4.1.0 + resolution: "@octokit/webhooks-methods@npm:4.1.0" + checksum: 10/a95ad68600c43798b09ea29d5a356fb69de25b45d38fbddf0ade00aadb0492b1d59985031e072a66d12e91034999a536653e5d2d4d01350d38cccf735d9ca270 languageName: node linkType: hard @@ -13556,10 +13964,10 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks-types@npm:7.1.0": - version: 7.1.0 - resolution: "@octokit/webhooks-types@npm:7.1.0" - checksum: 10/80b41945586243df9178a24dce9a5c4b2784eb963c5f6a1c76bcf5600a56e9cb51d8dc4a0da2108abfab3323cde577474a6365615fa1dfaac84c14e9295b54f9 +"@octokit/webhooks-types@npm:7.6.1": + version: 7.6.1 + resolution: "@octokit/webhooks-types@npm:7.6.1" + checksum: 10/0b11bd7e8d13b5a9cf14214421298a423d0180a5e1aaaea876ee4db6f97b5cca536f48d89af63105db75419d777a2402733eb0e110002d4dd59581ef36037bdc languageName: node linkType: hard @@ -13575,15 +13983,15 @@ __metadata: languageName: node linkType: hard -"@octokit/webhooks@npm:^12.0.4": - version: 12.0.10 - resolution: "@octokit/webhooks@npm:12.0.10" +"@octokit/webhooks@npm:^12.0.4, @octokit/webhooks@npm:^12.3.1": + version: 12.3.1 + resolution: "@octokit/webhooks@npm:12.3.1" dependencies: "@octokit/request-error": "npm:^5.0.0" - "@octokit/webhooks-methods": "npm:^4.0.0" - "@octokit/webhooks-types": "npm:7.1.0" + "@octokit/webhooks-methods": "npm:^4.1.0" + "@octokit/webhooks-types": "npm:7.6.1" aggregate-error: "npm:^3.1.0" - checksum: 10/ab7d216d1a1fae91bc3f75057c093707c1f6dbd5ff2106656154a7f471b1a1b5b78c947474e21362590e5510516f9537c4ab79ad6780cc91bfaaaa685aa366e0 + checksum: 10/373266807eb8dcf8d8c6f4685594106f1a257798a1b391cbe36e5b5faf60aca38a0467acd1356427ea7375edc111c03be3d443bc04efb16019a84e853c8a1bc3 languageName: node linkType: hard @@ -13628,21 +14036,21 @@ __metadata: linkType: hard "@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": - version: 2.20.0 - resolution: "@openapitools/openapi-generator-cli@npm:2.20.0" + version: 2.20.5 + resolution: "@openapitools/openapi-generator-cli@npm:2.20.5" dependencies: "@nestjs/axios": "npm:4.0.0" - "@nestjs/common": "npm:11.0.20" - "@nestjs/core": "npm:11.0.20" + "@nestjs/common": "npm:11.1.3" + "@nestjs/core": "npm:11.1.3" "@nuxtjs/opencollective": "npm:0.3.2" - axios: "npm:1.8.4" + axios: "npm:1.9.0" 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:11.3.0" - glob: "npm:9.3.5" + glob: "npm:11.0.3" inquirer: "npm:8.2.6" lodash: "npm:4.17.21" proxy-agent: "npm:6.5.0" @@ -13651,7 +14059,7 @@ __metadata: tslib: "npm:2.8.1" bin: openapi-generator-cli: main.js - checksum: 10/b5114d7811505e4fd3e7fa050c1262a76510b1d9e0c1a0e24a417f9570ca986a2edc0c7234eea0e6e30aae2ed698d2e9815ccdd8a5d1663b5794e9e505868edb + checksum: 10/76e40b238074c8bf09c99c70222cd80aa1d8be8b9abe6c400542f1bd46c3058238247085835a08a57bb0a43dceeff40b3cb8c4ea736692d040c1d29c48bd1db7 languageName: node linkType: hard @@ -13669,6 +14077,15 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/api-logs@npm:0.202.0, @opentelemetry/api-logs@npm:^0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/api-logs@npm:0.202.0" + dependencies: + "@opentelemetry/api": "npm:^1.3.0" + checksum: 10/22171137ad1d876a79a6f046b4adcc44a13941a07f2172948e4c8dbb6cacfe276b1fa5087f15e4acdd567f748010d531321c93ebf93a6c09586351c9048bf6c7 + languageName: node + linkType: hard + "@opentelemetry/api-logs@npm:0.54.2": version: 0.54.2 resolution: "@opentelemetry/api-logs@npm:0.54.2" @@ -13678,15 +14095,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/api-logs@npm:0.56.0, @opentelemetry/api-logs@npm:^0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/api-logs@npm:0.56.0" - dependencies: - "@opentelemetry/api": "npm:^1.3.0" - checksum: 10/5a6e25015acada7449d11124e9adbbe6670e1e9f7e8b46c60360ac89bb1537f2be326bcf18c66dcbcdee9f34e3a18bd4807c5a40faa0a4ac0135cb3675efb2a9 - languageName: node - linkType: hard - "@opentelemetry/api@npm:^1.3.0, @opentelemetry/api@npm:^1.4.0, @opentelemetry/api@npm:^1.9.0, @opentelemetry/api@npm:~1.9.0": version: 1.9.0 resolution: "@opentelemetry/api@npm:1.9.0" @@ -13694,60 +14102,62 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/auto-instrumentations-node@npm:^0.54.0": - version: 0.54.0 - resolution: "@opentelemetry/auto-instrumentations-node@npm:0.54.0" +"@opentelemetry/auto-instrumentations-node@npm:^0.61.0": + version: 0.61.0 + resolution: "@opentelemetry/auto-instrumentations-node@npm:0.61.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/instrumentation-amqplib": "npm:^0.45.0" - "@opentelemetry/instrumentation-aws-lambda": "npm:^0.49.0" - "@opentelemetry/instrumentation-aws-sdk": "npm:^0.48.0" - "@opentelemetry/instrumentation-bunyan": "npm:^0.44.0" - "@opentelemetry/instrumentation-cassandra-driver": "npm:^0.44.0" - "@opentelemetry/instrumentation-connect": "npm:^0.42.0" - "@opentelemetry/instrumentation-cucumber": "npm:^0.12.0" - "@opentelemetry/instrumentation-dataloader": "npm:^0.15.0" - "@opentelemetry/instrumentation-dns": "npm:^0.42.0" - "@opentelemetry/instrumentation-express": "npm:^0.46.0" - "@opentelemetry/instrumentation-fastify": "npm:^0.43.0" - "@opentelemetry/instrumentation-fs": "npm:^0.18.0" - "@opentelemetry/instrumentation-generic-pool": "npm:^0.42.0" - "@opentelemetry/instrumentation-graphql": "npm:^0.46.0" - "@opentelemetry/instrumentation-grpc": "npm:^0.56.0" - "@opentelemetry/instrumentation-hapi": "npm:^0.44.0" - "@opentelemetry/instrumentation-http": "npm:^0.56.0" - "@opentelemetry/instrumentation-ioredis": "npm:^0.46.0" - "@opentelemetry/instrumentation-kafkajs": "npm:^0.6.0" - "@opentelemetry/instrumentation-knex": "npm:^0.43.0" - "@opentelemetry/instrumentation-koa": "npm:^0.46.0" - "@opentelemetry/instrumentation-lru-memoizer": "npm:^0.43.0" - "@opentelemetry/instrumentation-memcached": "npm:^0.42.0" - "@opentelemetry/instrumentation-mongodb": "npm:^0.50.0" - "@opentelemetry/instrumentation-mongoose": "npm:^0.45.0" - "@opentelemetry/instrumentation-mysql": "npm:^0.44.0" - "@opentelemetry/instrumentation-mysql2": "npm:^0.44.0" - "@opentelemetry/instrumentation-nestjs-core": "npm:^0.43.0" - "@opentelemetry/instrumentation-net": "npm:^0.42.0" - "@opentelemetry/instrumentation-pg": "npm:^0.49.0" - "@opentelemetry/instrumentation-pino": "npm:^0.45.0" - "@opentelemetry/instrumentation-redis": "npm:^0.45.0" - "@opentelemetry/instrumentation-redis-4": "npm:^0.45.0" - "@opentelemetry/instrumentation-restify": "npm:^0.44.0" - "@opentelemetry/instrumentation-router": "npm:^0.43.0" - "@opentelemetry/instrumentation-socket.io": "npm:^0.45.0" - "@opentelemetry/instrumentation-tedious": "npm:^0.17.0" - "@opentelemetry/instrumentation-undici": "npm:^0.9.0" - "@opentelemetry/instrumentation-winston": "npm:^0.43.0" - "@opentelemetry/resource-detector-alibaba-cloud": "npm:^0.29.6" - "@opentelemetry/resource-detector-aws": "npm:^1.9.0" - "@opentelemetry/resource-detector-azure": "npm:^0.4.0" - "@opentelemetry/resource-detector-container": "npm:^0.5.2" - "@opentelemetry/resource-detector-gcp": "npm:^0.31.0" - "@opentelemetry/resources": "npm:^1.24.0" - "@opentelemetry/sdk-node": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/instrumentation-amqplib": "npm:^0.49.0" + "@opentelemetry/instrumentation-aws-lambda": "npm:^0.53.1" + "@opentelemetry/instrumentation-aws-sdk": "npm:^0.55.0" + "@opentelemetry/instrumentation-bunyan": "npm:^0.48.0" + "@opentelemetry/instrumentation-cassandra-driver": "npm:^0.48.0" + "@opentelemetry/instrumentation-connect": "npm:^0.46.0" + "@opentelemetry/instrumentation-cucumber": "npm:^0.17.1" + "@opentelemetry/instrumentation-dataloader": "npm:^0.20.0" + "@opentelemetry/instrumentation-dns": "npm:^0.46.0" + "@opentelemetry/instrumentation-express": "npm:^0.51.1" + "@opentelemetry/instrumentation-fastify": "npm:^0.47.1" + "@opentelemetry/instrumentation-fs": "npm:^0.22.0" + "@opentelemetry/instrumentation-generic-pool": "npm:^0.46.1" + "@opentelemetry/instrumentation-graphql": "npm:^0.50.0" + "@opentelemetry/instrumentation-grpc": "npm:^0.202.0" + "@opentelemetry/instrumentation-hapi": "npm:^0.49.0" + "@opentelemetry/instrumentation-http": "npm:^0.202.0" + "@opentelemetry/instrumentation-ioredis": "npm:^0.50.1" + "@opentelemetry/instrumentation-kafkajs": "npm:^0.11.0" + "@opentelemetry/instrumentation-knex": "npm:^0.47.0" + "@opentelemetry/instrumentation-koa": "npm:^0.50.2" + "@opentelemetry/instrumentation-lru-memoizer": "npm:^0.47.0" + "@opentelemetry/instrumentation-memcached": "npm:^0.46.0" + "@opentelemetry/instrumentation-mongodb": "npm:^0.55.1" + "@opentelemetry/instrumentation-mongoose": "npm:^0.49.0" + "@opentelemetry/instrumentation-mysql": "npm:^0.48.1" + "@opentelemetry/instrumentation-mysql2": "npm:^0.48.1" + "@opentelemetry/instrumentation-nestjs-core": "npm:^0.48.1" + "@opentelemetry/instrumentation-net": "npm:^0.46.1" + "@opentelemetry/instrumentation-oracledb": "npm:^0.28.0" + "@opentelemetry/instrumentation-pg": "npm:^0.54.1" + "@opentelemetry/instrumentation-pino": "npm:^0.49.1" + "@opentelemetry/instrumentation-redis": "npm:^0.50.0" + "@opentelemetry/instrumentation-restify": "npm:^0.48.2" + "@opentelemetry/instrumentation-router": "npm:^0.47.0" + "@opentelemetry/instrumentation-runtime-node": "npm:^0.16.0" + "@opentelemetry/instrumentation-socket.io": "npm:^0.49.0" + "@opentelemetry/instrumentation-tedious": "npm:^0.21.1" + "@opentelemetry/instrumentation-undici": "npm:^0.13.2" + "@opentelemetry/instrumentation-winston": "npm:^0.47.0" + "@opentelemetry/resource-detector-alibaba-cloud": "npm:^0.31.2" + "@opentelemetry/resource-detector-aws": "npm:^2.2.0" + "@opentelemetry/resource-detector-azure": "npm:^0.9.0" + "@opentelemetry/resource-detector-container": "npm:^0.7.2" + "@opentelemetry/resource-detector-gcp": "npm:^0.36.0" + "@opentelemetry/resources": "npm:^2.0.0" + "@opentelemetry/sdk-node": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.4.1 - checksum: 10/0b5992c0ab3eeb22a81ffcd8f4f1b9c4694e0c2fa5e208918774119838f20921e7918e39f801fb1194bf8ceca5e58d87c82f153ab1e26d7bd502a7644b125330 + "@opentelemetry/core": ^2.0.0 + checksum: 10/845728e050aa40f501ca3c67ad10ef64859413cb3af6926c6e64dbec206dc4ee7c8cff4d93df95345cebf8970427a03665032f97941a6a1153699f0fbbac1a6f languageName: node linkType: hard @@ -13760,12 +14170,12 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/context-async-hooks@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/context-async-hooks@npm:1.29.0" +"@opentelemetry/context-async-hooks@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/context-async-hooks@npm:2.0.1" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/bfd960bd03d8c5b2d92447fd7435b86ff919299b56821fe8f99f2ffe19831f0ecadba25bd52a37faff0f98c4fecdcc76e6886e56a3844b367360c6232674ae0e + checksum: 10/198dacdce36377f6ded7062eb9fc77f86c9fcc8c86dd395e9cb276e14a01c7906ea2f0271999cae21bf251dbec44641286b8bd34a39c67c88c08ed925a986f4b languageName: node linkType: hard @@ -13780,18 +14190,18 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/core@npm:1.29.0" +"@opentelemetry/core@npm:2.0.1, @opentelemetry/core@npm:^2.0.0": + version: 2.0.1 + resolution: "@opentelemetry/core@npm:2.0.1" dependencies: - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/eb62dce11cb0cb637acfb3582ad25e48766d9fb37b6b70e57f3c60521c7680a85431a0853c50d98cc8e807e5e3c2fddda314623d879e932bf1a5f629344b39ce + checksum: 10/dd891afd427067a9e6c610c36ab5638b0b9e5303ccca7c75ad744f5db53c6162a4b5d9cd2f5a77cdc3e4bda2eae850a4e29983ea244c929b7b872b7e086fc61c languageName: node linkType: hard -"@opentelemetry/core@npm:1.30.1, @opentelemetry/core@npm:^1.0.0, @opentelemetry/core@npm:^1.1.0, @opentelemetry/core@npm:^1.25.0, @opentelemetry/core@npm:^1.25.1, @opentelemetry/core@npm:^1.26.0, @opentelemetry/core@npm:^1.29.0, @opentelemetry/core@npm:^1.8.0": +"@opentelemetry/core@npm:^1.29.0": version: 1.30.1 resolution: "@opentelemetry/core@npm:1.30.1" dependencies: @@ -13802,6 +14212,22 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/exporter-logs-otlp-grpc@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.202.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/sdk-logs": "npm:0.202.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/b293a3778b6d5b1fe07b9203fe83d0dde3ea57e48efec8d4ccfc27d007fdb5cdafe7446ff10ea3eca69eb2fba8bdb97546a7dfcfc21b73d303e94e15f917b253 + languageName: node + linkType: hard + "@opentelemetry/exporter-logs-otlp-grpc@npm:0.54.2": version: 0.54.2 resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.54.2" @@ -13817,18 +14243,18 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-grpc@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-logs-otlp-grpc@npm:0.56.0" +"@opentelemetry/exporter-logs-otlp-http@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.202.0" dependencies: - "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-grpc-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/sdk-logs": "npm:0.56.0" + "@opentelemetry/api-logs": "npm:0.202.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/sdk-logs": "npm:0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/e798faebab931c25941d7d6ebac91dbc0f0fd86b9a6ec641b12ba76ce4675a68acd4d7099ef41d5c040650b0ee2881c61f5cab053dcd280f3cb6dcc29fc450d5 + checksum: 10/850be5f7a2b0468887357845ffb7f8ca53dfedcd8fc79fbd4d8825cabbdc9a27be107e7aecb6b21ba544cead796e13f116396fcc66ba58954e47ac8fc1eaf270 languageName: node linkType: hard @@ -13847,18 +14273,20 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-http@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-logs-otlp-http@npm:0.56.0" +"@opentelemetry/exporter-logs-otlp-proto@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-logs-otlp-proto@npm:0.202.0" dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/sdk-logs": "npm:0.56.0" + "@opentelemetry/api-logs": "npm:0.202.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-logs": "npm:0.202.0" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/f2313074b0b124a36c292a9f97d6865df4c8d97ff2c4f95a122212c32a7ab91f0a4b6c1e01c2d1fe8c6619d245be81c7d7ab5178ddf2b8cfec15b224ae38c9e5 + checksum: 10/b69e19a035ae7d2d3f99f5ef40760e9f874685de9679b09a143ae9c120a248d9b5995eeec869ebeed6df1a4ebb4e312b5a569f4189410ad02e6ec4438f8fd63f languageName: node linkType: hard @@ -13879,20 +14307,65 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-logs-otlp-proto@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-logs-otlp-proto@npm:0.56.0" +"@opentelemetry/exporter-metrics-otlp-grpc@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-metrics-otlp-grpc@npm:0.202.0" dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-logs": "npm:0.56.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.202.0" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/3d062bd054108a0d40da1295ea11f68d4b71f9cf665bde479498939680497a54e1d542f358bc9b04b3332da15c95b1bdb570f7b60ca1a96909a2675d9f26a1ae + checksum: 10/383e6eee1016dbdfe78ed8c96fb6e8e314e0103a11353373f794637594b2b935efc5707eee33712a4ed242b12c808a09584105db1b2e2deab477d80e3d2d06ed + languageName: node + linkType: hard + +"@opentelemetry/exporter-metrics-otlp-http@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-metrics-otlp-http@npm:0.202.0" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/ed694b8369bb2f979f0aa3552716b267060e5d8d7853fd645e0ae776888091a08c079e9f1e88d8f7eb93d9fe31f0fbb887ebb9469177c2ec2af16c247f8faf5d + languageName: node + linkType: hard + +"@opentelemetry/exporter-metrics-otlp-proto@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-metrics-otlp-proto@npm:0.202.0" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.202.0" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/22ecc54c4a19a36d5f6ba8cc061e25fba6ff9f2bbaff9a84f8c1e7e5253b2b0dc07ac18e21884eafcb31e6693ce59912baeb19c722a07ad4da9f22e2a6f94549 + languageName: node + linkType: hard + +"@opentelemetry/exporter-prometheus@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-prometheus@npm:0.202.0" + dependencies: + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/e6cf4dfdc3bd6ccd402a00e0cb1bd2fa5cc4c813a86b23d27bf851e38a22788dac234c10d643a1506acf10415b1cba9bd68da7554358ba32a0ad8b978bc2d467 languageName: node linkType: hard @@ -13909,6 +14382,23 @@ __metadata: languageName: node linkType: hard +"@opentelemetry/exporter-trace-otlp-grpc@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.202.0" + dependencies: + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-grpc-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/28cd0d2a0d3ef08cd7830b044690c5e6d86eb36f0d1298d9cd921f89c382cfbd3f80272c9fb8c12ece7eb417a48f852f6b3c299c21b0eba5d340d244cd41e6e2 + languageName: node + linkType: hard + "@opentelemetry/exporter-trace-otlp-grpc@npm:0.54.2": version: 0.54.2 resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.54.2" @@ -13925,19 +14415,18 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-grpc@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-trace-otlp-grpc@npm:0.56.0" +"@opentelemetry/exporter-trace-otlp-http@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.202.0" dependencies: - "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-grpc-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/3092a8c1ae7e092904b135760c7512636e132fc53c1657e5c9beda50ed104cfb0e85fed0fe53c53d95afd6b9403f6a9767106dd581ed5ae4559120c7d0616faa + checksum: 10/f9e60e51b5dcca3d4d32f2091f04faa2bcaf677b9f8a88990c8cdc7c6345e4244e46c4e8df1c336fc6f741aea092407032ade1236089eb729196e6fcdc055ad0 languageName: node linkType: hard @@ -13956,18 +14445,18 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-http@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-trace-otlp-http@npm:0.56.0" +"@opentelemetry/exporter-trace-otlp-proto@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.202.0" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/2c9a0fb15ace10ca904c27a06d84b2a60b38ae989d045c3c8466419a5439d3dd5d0584481036ee1675cbf8eb403b863c7d51a086c10ffb5435235375062b33af + checksum: 10/884d1d097f3defee57dbb64281815a062603f64ce81e2f4460477925f4fb23c1d42aac2bb9d3b188e00cacaa9d231ef11e2e0f36e4b26863d009e795953191d8 languageName: node linkType: hard @@ -13986,21 +14475,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-trace-otlp-proto@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/exporter-trace-otlp-proto@npm:0.56.0" - dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/85197740a1744a16f10ee355e250c4e7a77250454266cf9bad1875beb245dd39ba4fb19e87322fae65bf038075698fa5d1b284cc7d76160ee6eff279b728bef3 - languageName: node - linkType: hard - "@opentelemetry/exporter-zipkin@npm:1.27.0": version: 1.27.0 resolution: "@opentelemetry/exporter-zipkin@npm:1.27.0" @@ -14015,508 +14489,531 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/exporter-zipkin@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/exporter-zipkin@npm:1.29.0" +"@opentelemetry/exporter-zipkin@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/exporter-zipkin@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/8fdd2873723c48a00b9e82bfc4d878887cb65f68b7843d32e402677758481ce523759f30aa800bd538441dcca85a83765f0b4ae720d1309dbdce0c858fc93ec4 + checksum: 10/67fa9c4e33276218fd8a4c6fb04bab55aaf35383e396056034ab1f826f3d00c672b91ec890a9859df13c9f2890d3dc3d9f1a2b2904f0855272fc0cb48157e874 languageName: node linkType: hard -"@opentelemetry/instrumentation-amqplib@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-amqplib@npm:0.45.0" - dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/7a0be9861a12a7baf1a63fecd4eb221eecb9cb422223d1b62f183b14c1999e9ba039450a60bbaad139abab040d48dc843d4c93e8247b39215e31066e4ff9aa17 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-aws-lambda@npm:^0.49.0": +"@opentelemetry/instrumentation-amqplib@npm:^0.49.0": version: 0.49.0 - resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.49.0" + resolution: "@opentelemetry/instrumentation-amqplib@npm:0.49.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" - "@types/aws-lambda": "npm:8.10.143" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/efd4d8e66fd0cb418b923bf971a0bae607b06dc02bd7a0215a316013fd8bf4e2d80d7c099215b34cf4466115664fa062c9395816e4456b468314cff7ab24125e + checksum: 10/891f201f476d77b960b6f3dc0186b3af52f7c249b69da365da184b01cf2056052fa93f85196e8811156908b2f532adf6a13773f102b4a78626046275827df2b1 languageName: node linkType: hard -"@opentelemetry/instrumentation-aws-sdk@npm:^0.48.0": +"@opentelemetry/instrumentation-aws-lambda@npm:^0.53.1": + version: 0.53.1 + resolution: "@opentelemetry/instrumentation-aws-lambda@npm:0.53.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@types/aws-lambda": "npm:8.10.150" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/a305988e8332e416c5fd81ffbdad71ffd68eeb623c55ea26fa25212f3c0c254ce30022461067981e4a7c5cfb1e25d1f1ed306d09f09d68c17fbbe693ec2ed4a1 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-aws-sdk@npm:^0.55.0": + version: 0.55.0 + resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.55.0" + dependencies: + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/propagation-utils": "npm:^0.31.2" + "@opentelemetry/semantic-conventions": "npm:^1.34.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/c2ce4063f4761baa6e12d1830270952d67d9bd0fdba3d15b97ec107df54d2a21c01be6960e0c2e3dcb2358bc57ba977b694e02c78a01c0f193377d0e4557dd7e + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-bunyan@npm:^0.48.0": version: 0.48.0 - resolution: "@opentelemetry/instrumentation-aws-sdk@npm:0.48.0" + resolution: "@opentelemetry/instrumentation-bunyan@npm:0.48.0" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/propagation-utils": "npm:^0.30.14" + "@opentelemetry/api-logs": "npm:^0.202.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@types/bunyan": "npm:1.8.11" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/c0147693e5e6202198448b0ca8b2550ca972d9a72a78b3c95edb1942b6e0a1b295351c205dbfc7562e11a50b815a64b3b920ceb474d6a59daa4cfbea01d8f621 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-cassandra-driver@npm:^0.48.0": + version: 0.48.0 + resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.48.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/7d036e6467dbbaed5f3569ead30f628e73c6144c769278dec29eb9ef85804dda48c37a864c05ce0fcf69f89ea7447999e0630db612c361e72980fbe7e6615d39 + checksum: 10/7a3b2ba1b472108f0b1e1e799d7f8f8d0eb10034378b4b06fbec0e1fc59e095f3e374dd7739047856d27f00d8706603db08cfb90aa37b427fce18b43b0731932 languageName: node linkType: hard -"@opentelemetry/instrumentation-bunyan@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-bunyan@npm:0.44.0" +"@opentelemetry/instrumentation-connect@npm:^0.46.0": + version: 0.46.0 + resolution: "@opentelemetry/instrumentation-connect@npm:0.46.0" dependencies: - "@opentelemetry/api-logs": "npm:^0.56.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@types/bunyan": "npm:1.8.9" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/2be4fd8ec960bfd355626c31e7f0192a3dcd232d5749eb7fe46adfd1f33298e55bab595dd9b45a2569a4c712c6d6dc6b4aea8374bc3fe248da9d2bf9d0e0bc3b - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-cassandra-driver@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-cassandra-driver@npm:0.44.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@types/connect": "npm:3.4.38" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/7617d2097710e94389c00fbf82e1e49f6b772370b2746fd6744681b46a2b3f4bbfa909bfcd556dc0eb99ad3d095c78f1d3016abff532b782d95164339213b3d0 + checksum: 10/e5b96bde0b8fbcf2edc22ed3a5ac655758440e9a3f8c4f39ae3dcf88edda8be6dd0bb121b3cffcf8df8b53fb0aef3c5bc62549cf5c24439fb9ba15a03f93280c languageName: node linkType: hard -"@opentelemetry/instrumentation-connect@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-connect@npm:0.42.0" +"@opentelemetry/instrumentation-cucumber@npm:^0.17.1": + version: 0.17.1 + resolution: "@opentelemetry/instrumentation-cucumber@npm:0.17.1" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - "@types/connect": "npm:3.4.36" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/f03090f36028d654fcbb649d52ec57613b407aceb7e3d61978ab4f737c3617f4dd7061612139e0cb856a9df566d1ff0202f2c426ee3dacaf2a68c1ea5a34be63 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-cucumber@npm:^0.12.0": - version: 0.12.0 - resolution: "@opentelemetry/instrumentation-cucumber@npm:0.12.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/0829bd8f609fc038e1649e1f214f63398304e8479ee600706c072f54b58a6e9bed5d59af3d13d62da629ecf78b1c59d2217fff4b472a062b1967b7142130e33b + checksum: 10/ef65ef03f4aa79a3b4ae223aebf714b62b7d96261680932df02ba4590685253d4e7bf44ac58109e39a951bb0217ffef11f12224ede62ce65ac93a46f2b65e517 languageName: node linkType: hard -"@opentelemetry/instrumentation-dataloader@npm:^0.15.0": - version: 0.15.0 - resolution: "@opentelemetry/instrumentation-dataloader@npm:0.15.0" +"@opentelemetry/instrumentation-dataloader@npm:^0.20.0": + version: 0.20.0 + resolution: "@opentelemetry/instrumentation-dataloader@npm:0.20.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/2a13e5159f5977f8d14955d6ce6994a6c6249ae25da10975aca782d0a74d74355143cdc4c47c99f36abd71d6097b610aa34611bb64a5d042948bbb788c26dd77 + checksum: 10/4d9e5600644e5d941860d492be9bd1b3966acd69e281f491f151347d482464e1481f7ce8c6898e744e235d052c16868774b6353d606a58a3538b9e32d43c2b92 languageName: node linkType: hard -"@opentelemetry/instrumentation-dns@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-dns@npm:0.42.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/5bbdb5095f257c479a11eabaf49019e8ed5d49ececbeba9c5723ecc9f3343f601edecbea36d9a7581e46dcd80cb27fe7299fea1e42cd60083a1f50b3df32c1a0 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-express@npm:^0.46.0": +"@opentelemetry/instrumentation-dns@npm:^0.46.0": version: 0.46.0 - resolution: "@opentelemetry/instrumentation-express@npm:0.46.0" + resolution: "@opentelemetry/instrumentation-dns@npm:0.46.0" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/68c72e76a5c53d7d49632fb0126c097aa717c173fc0a2502f4b03be382844fbcdcc385f49dab502853006e3e64555879e5c26a24d126a87d7825ad01be1a9f01 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-express@npm:^0.51.1": + version: 0.51.1 + resolution: "@opentelemetry/instrumentation-express@npm:0.51.1" + dependencies: + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/757b074aece5ad34ac082c54f3c4a6aadcbdf6cfb55510de5d77acc3e75e28a3c7f8b80ba5727b5d47ffa70790d0fdc672fc67d5f3854ba2acbb011424ce914c + checksum: 10/a0ecb32a8599d826a032df0c5558d43bf5024b131b93e6e57c659f208e265d40bafbad15c46a5621c698e7b6383ef2a02f4600633d52ba2f4b6fb75c08eed631 languageName: node linkType: hard -"@opentelemetry/instrumentation-fastify@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-fastify@npm:0.43.0" +"@opentelemetry/instrumentation-fastify@npm:^0.47.1": + version: 0.47.1 + resolution: "@opentelemetry/instrumentation-fastify@npm:0.47.1" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/aee85c357ad0f0a88935919aa5bd09547e0b81fd11fc83aabc298fa84e4b3f7d6e64aa03f6273849ce93d11cad591a743a2b73f7838ddf26f54a48b53906259c + checksum: 10/89ffbe54deaa90a33deb873db24967f7e0380c4dc448703ed6b7abbadd345687902e3511e23c1af622ce55e9b70a12e724d3773ad61d7007e4d3d8a47615591d languageName: node linkType: hard -"@opentelemetry/instrumentation-fs@npm:^0.18.0": - version: 0.18.0 - resolution: "@opentelemetry/instrumentation-fs@npm:0.18.0" +"@opentelemetry/instrumentation-fs@npm:^0.22.0": + version: 0.22.0 + resolution: "@opentelemetry/instrumentation-fs@npm:0.22.0" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/634c23bac8aee7325be2cf2d64f0856d11a9de746f942fec52ebd96a6a7159c63913758c78295c9cb909d950f72c73e1e2685f4d9ed5f9754254ff0ce678dcc6 + checksum: 10/56090810d8c31949ddbd9f1e2c9f91ca38e7214ada60bc9abc72cfac94fb303e5cc905a63e0c1ab2b524aaf80e7a152a3ab286d792fe82bb4e814cd5eb1d2f16 languageName: node linkType: hard -"@opentelemetry/instrumentation-generic-pool@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.42.0" +"@opentelemetry/instrumentation-generic-pool@npm:^0.46.1": + version: 0.46.1 + resolution: "@opentelemetry/instrumentation-generic-pool@npm:0.46.1" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/09c2cf4b64798ce92ddc0214c6c0443bb7800f94a2a8578f2276f3d8a5dd7018b4f5ec9d82d771b4cedd29b949760ca333c1a4fec0d43244ba338af36ad150b2 + checksum: 10/10f8cbf5d052c3cb895857390f6a53ec292a1d0e8ec82df94bfb6cd49a0d0a0db0ba66cbb4127298418be4b68444d048f70833257d37cad404edf17578d477e5 languageName: node linkType: hard -"@opentelemetry/instrumentation-graphql@npm:^0.46.0": - version: 0.46.0 - resolution: "@opentelemetry/instrumentation-graphql@npm:0.46.0" +"@opentelemetry/instrumentation-graphql@npm:^0.50.0": + version: 0.50.0 + resolution: "@opentelemetry/instrumentation-graphql@npm:0.50.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/f7850db04df91a8d213396bbaaf7762535295ac2c572d1338a9798cebfb149ccf3a8b98d9255cdcdbbd42f17228440f2a75f064d2f2010da336174a55b644ec6 + checksum: 10/a359e0839381d74dacf0b3acc8c951686cf73d6201fd305583e1c58ed6f4dcf1421efa322075808ed9c9298af3e579cbe4ac275960ceb7aceb8639b7346b5990 languageName: node linkType: hard -"@opentelemetry/instrumentation-grpc@npm:^0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/instrumentation-grpc@npm:0.56.0" +"@opentelemetry/instrumentation-grpc@npm:^0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/instrumentation-grpc@npm:0.202.0" dependencies: - "@opentelemetry/instrumentation": "npm:0.56.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/instrumentation": "npm:0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/00973b36078641afecb085d0bef2dbabef7af3c68bb3abd2a3021600dbaf9a0b86011160c10d2b7db87a0ec21fcd01082547082f8656c94426c2662c29b68613 + checksum: 10/4b59bb35de3b900e9fc5e9ba44d1828b6b4eea15f1ce65a63ad9e047cff5989a9d1d23180e8c221c7bd375c104ecd23f2e7650405e37cf8d8a46edda53d447ee languageName: node linkType: hard -"@opentelemetry/instrumentation-hapi@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-hapi@npm:0.44.0" +"@opentelemetry/instrumentation-hapi@npm:^0.49.0": + version: 0.49.0 + resolution: "@opentelemetry/instrumentation-hapi@npm:0.49.0" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/d8eafb0213ea8c6971b7a21dd3a3f7ea988954cdf23e708c5fc8bd132267709fd449823d496ac2bb8cf93cf164fa452edb16089c307cefcabb54bc99f20b203e + checksum: 10/2e2640e26ae193000b63fb62aea5dc03eff2c4896442361ebc7e3e482713166bad49616a5d3c157fb8dd0fab908d0c4412e24f4d806b071435890436ccf8fd9f languageName: node linkType: hard -"@opentelemetry/instrumentation-http@npm:^0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/instrumentation-http@npm:0.56.0" +"@opentelemetry/instrumentation-http@npm:^0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/instrumentation-http@npm:0.202.0" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/instrumentation": "npm:0.56.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/instrumentation": "npm:0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" forwarded-parse: "npm:2.1.2" - semver: "npm:^7.5.2" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/3fb73d5f0df152f7ff9744a32a521a71656075df1ea6418005a991c912f16fd1b16c468bc9dcc48d4d19621be5b0b4aedc377cf6d00f828e0d2d1f8d4822e6ca + checksum: 10/fb36be7a08a33d07bb51e8c8dc64be937117f03d93a1425bc9ddb041464da1f01914adc1bc8173a145b61a7fd8b6151db8e284db1bcce12135d6b27b01c2ad0a languageName: node linkType: hard -"@opentelemetry/instrumentation-ioredis@npm:^0.46.0": +"@opentelemetry/instrumentation-ioredis@npm:^0.50.1": + version: 0.50.1 + resolution: "@opentelemetry/instrumentation-ioredis@npm:0.50.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/redis-common": "npm:^0.38.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/08e6aed4aa6d0ad0d1dd4a6d02b260f46d1ce5168b8dd1f6b2d3f8c71f0204378e1b52f175817e53cdb6f98841a6ddfc5ca2005592ea678f046316dc4edff281 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-kafkajs@npm:^0.11.0": + version: 0.11.0 + resolution: "@opentelemetry/instrumentation-kafkajs@npm:0.11.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/6998633cda7260fde928f8691482bdbf5478d7f4c7c0be4c199c366345525733ec87e94b0a38b5b65a719489998f377ad52a065ebe0eeafc010e237533f1ce55 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-knex@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/instrumentation-knex@npm:0.47.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.33.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/f3a753505fd6499d05022fbddb0977b781ad41a857df9cde7410273ca3ad9f2343afc6136138bbdde16e5358d77bba66e2ddf7f93d4dab878b6e5b85d72fc6d8 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-koa@npm:^0.50.2": + version: 0.50.2 + resolution: "@opentelemetry/instrumentation-koa@npm:0.50.2" + dependencies: + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/3598afe454d6df1861dd0b7f4a8b617f4d3b89789961577912151df9a2d159523490f25c3e8d03234b5faa62d5f8f8985e8a52b65122cae4c58016debf109edb + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-lru-memoizer@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.47.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/91fd11ce2f012b715a1bffb5c81ca5d681e5c2d9f638a2e459f2d4bead587ed755e438c234132484d6d030f0b3104a0747596708f51df109449cd970e965e6ae + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-memcached@npm:^0.46.0": version: 0.46.0 - resolution: "@opentelemetry/instrumentation-ioredis@npm:0.46.0" + resolution: "@opentelemetry/instrumentation-memcached@npm:0.46.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/redis-common": "npm:^0.36.2" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/579296b0fe06c0d727d9866845110b109160579654bb442f4e4ce05df0d47e2ad3912df1ace2bca84423b1d62584524a79d2c3f82943ae7429801e96440c3db5 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-kafkajs@npm:^0.6.0": - version: 0.6.0 - resolution: "@opentelemetry/instrumentation-kafkajs@npm:0.6.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/85723b216fea262d65b6aad9a24242842b0716e06424c6a94a1b9299470c3756dc9d128816f7053bf68eb46bde41d545385e47ac680e2aeff609b3fbc7c6b135 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-knex@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-knex@npm:0.43.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/bb801ba2198b33e0b4feba84313ad54d502969d5fddea9de26ebbeb1a59ade61f03eed2aea983f43682b53102de837916f6dd44eede3a55237980dc99527bf6c - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-koa@npm:^0.46.0": - version: 0.46.0 - resolution: "@opentelemetry/instrumentation-koa@npm:0.46.0" - dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/63d2209e7acb53d7183cd1926656d388a60cca34f3517a26af101acdb4e5612c123071076828a49e18f288a5d694a2f7994f95ee48dab8312f6c2904321d2e4a - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-lru-memoizer@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-lru-memoizer@npm:0.43.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/5c1ea5e1f453dea989285199fe8385f31f17982fbc14eb504dc801f5e3a97f8e3fdc4d0f23212ec3cc522e84c1b5750073192b4a33b8a8302cdd18f4935280cb - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-memcached@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-memcached@npm:0.42.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" "@types/memcached": "npm:^2.2.6" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/0f14d19084bdb7327113003998b89b9d0a6ddc91ff7af958799d3f09a7a6ccdb1aa565d58605ced57dc98f72efa465387b8540924a926e5aaf620a3b5feca77a + checksum: 10/12bbcc4b9dcc62b0b48ba83f555112881cc9723f174335bbf7695374ae17582fb12cb2f72295656c84608b73ef5a88afcf551d5c392afebddbf97d7a64230080 languageName: node linkType: hard -"@opentelemetry/instrumentation-mongodb@npm:^0.50.0": - version: 0.50.0 - resolution: "@opentelemetry/instrumentation-mongodb@npm:0.50.0" +"@opentelemetry/instrumentation-mongodb@npm:^0.55.1": + version: 0.55.1 + resolution: "@opentelemetry/instrumentation-mongodb@npm:0.55.1" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/beee37f0be94c614c620ea431ea7315c9293ea14f3b8f751075a5b4d663ed3daa4fe73ef9f58af812ab38d6b59d511da6252386d1fc7aab4d60b4384a8c2065c + checksum: 10/233822b1886b5693923430fb126363005424bcabd03681bf2cf4c035a52dbc6475718417b2a284d38f2fdcac1423625f5a25b767018a3686e9da54f4313f4da9 languageName: node linkType: hard -"@opentelemetry/instrumentation-mongoose@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-mongoose@npm:0.45.0" - dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/a4872b8c7783ace3510f954957aaf6880dee89c00f44a55153e463b5ffeacfdb896ca285cb8b816a746e7dec7f69901d0625c959cbdb09f4dfcba123dfa82213 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-mysql2@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-mysql2@npm:0.44.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - "@opentelemetry/sql-common": "npm:^0.40.1" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/8ba5e66d3f994a3473df480bfd417b3e870b2fa25ae77b74ed1e516afb4ad38d1625d36c3947b02d9e52298edda1ea5f2df008a7c5bc8543455d760deb9fe3e1 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-mysql@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-mysql@npm:0.44.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - "@types/mysql": "npm:2.15.26" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/7043e7446280be01ff12c88cbe843d131543c48ee73894b2862e9970cf380b43538f82a31fe32569d1fc2627acf0ccd1635e7ba3858ae93d7163a15b293036a8 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-nestjs-core@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.43.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/8d7170dfb574c77a8f11081aa205c83fb8d8ec7e9935005b1d6ca418b917bc359c55ed955747d330b8d3bdfbc599cc7eac38c0b1c5a092771847f9fd9bf49b3f - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-net@npm:^0.42.0": - version: 0.42.0 - resolution: "@opentelemetry/instrumentation-net@npm:0.42.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/ef0c3dc42cb75aa3a53c7fc72f214cad9da6276c07ac32cf65a4d9991741cea7daac4a764f7c99d0265aa84b27b8692f77a996cca98dd8a67dd299bfcd8a85a6 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-pg@npm:^0.49.0": +"@opentelemetry/instrumentation-mongoose@npm:^0.49.0": version: 0.49.0 - resolution: "@opentelemetry/instrumentation-pg@npm:0.49.0" + resolution: "@opentelemetry/instrumentation-mongoose@npm:0.49.0" dependencies: - "@opentelemetry/core": "npm:^1.26.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:1.27.0" - "@opentelemetry/sql-common": "npm:^0.40.1" - "@types/pg": "npm:8.6.1" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/4b2b320346a0d00a56438c041ddda6cbd8b34d37de613daab618892fcba380b0cbd5396886028e9e2ac81c497f25e66f7668ccbf57c3f870678d30fc16e4ae24 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-mysql2@npm:^0.48.1": + version: 0.48.1 + resolution: "@opentelemetry/instrumentation-mysql2@npm:0.48.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@opentelemetry/sql-common": "npm:^0.41.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/d52d0950201adb3c4ce8f82edc544eee489de0971045f1e907832d6e7424d372da3a1e62143f6fcde698d883dfbe7adbea243cef9653f467c96d011d98225676 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-mysql@npm:^0.48.1": + version: 0.48.1 + resolution: "@opentelemetry/instrumentation-mysql@npm:0.48.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@types/mysql": "npm:2.15.27" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/8e4493e5a685d98f0b7797b6f3996f7949cdae758e68c12e2edb57d8388be47a67e668c12340f1a2e18c692c8db990a765913120e402215c8d473695c9a281b8 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-nestjs-core@npm:^0.48.1": + version: 0.48.1 + resolution: "@opentelemetry/instrumentation-nestjs-core@npm:0.48.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.30.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/fbbd5dd411b79371725e9075bca0c492a524ebd90d5e17434c3608bae6ec1cbbd5cca4dc0385c423e59963bfc1b945efdff5cd02f09a3f2a06391fc176d52c50 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-net@npm:^0.46.1": + version: 0.46.1 + resolution: "@opentelemetry/instrumentation-net@npm:0.46.1" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/3372ac7b8643c4adea2d8ffd762a2c872e02d000d5cc5afca922f422033eea251adeeac1c5d1bd49014d76c9167a0dfa98cd41dda7b3950395c179a8a8c6de81 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-oracledb@npm:^0.28.0": + version: 0.28.0 + resolution: "@opentelemetry/instrumentation-oracledb@npm:0.28.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@types/oracledb": "npm:6.5.2" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/820a6e760d2137ad00b27ff78014316fc37e92f2be94ad4c6082e92ace8fcd89a6d2bcc744e6330358ebd0f127d7993b65843b167b0c1ea0f8d5cb0fe201c634 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-pg@npm:^0.54.1": + version: 0.54.1 + resolution: "@opentelemetry/instrumentation-pg@npm:0.54.1" + dependencies: + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/semantic-conventions": "npm:^1.27.0" + "@opentelemetry/sql-common": "npm:^0.41.0" + "@types/pg": "npm:8.15.4" "@types/pg-pool": "npm:2.0.6" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/04457e441872a0afecfa471f04bbecdf4b62782d110cd1a75423f9ff12c34396896056dda99923690f4a26c572fccc0c28d25fbd26e71f07ca2778e61fc3a7d1 + checksum: 10/eb88b8bd4417db8188af44e62659a7bc35b3f6a4b155d12c69357fc88185f3d719b3dd93e5562896088d07041c882a71a06d9672c14332846ae4f5abd26ec99a languageName: node linkType: hard -"@opentelemetry/instrumentation-pino@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-pino@npm:0.45.0" +"@opentelemetry/instrumentation-pino@npm:^0.49.1": + version: 0.49.1 + resolution: "@opentelemetry/instrumentation-pino@npm:0.49.1" dependencies: - "@opentelemetry/api-logs": "npm:^0.56.0" - "@opentelemetry/core": "npm:^1.25.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/api-logs": "npm:^0.202.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/ef56a6bca54e99e66531576727ce236bd743413434fa04bb759ae6ebf8564fba303f3756ee7333fdaaea61a72a7e29f21ae19267c16e6d2d651596970b07ff10 + checksum: 10/2e2bff24bef74f68da8f4b4655d198c3fba7f7425f2f6317077385bd34dfbfb44c3747165859fec7a9f90aa0c77f830148e112e3d8257f6ac35dc28bbe157041 languageName: node linkType: hard -"@opentelemetry/instrumentation-redis-4@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-redis-4@npm:0.45.0" +"@opentelemetry/instrumentation-redis@npm:^0.50.0": + version: 0.50.0 + resolution: "@opentelemetry/instrumentation-redis@npm:0.50.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/redis-common": "npm:^0.36.2" + "@opentelemetry/instrumentation": "npm:^0.202.0" + "@opentelemetry/redis-common": "npm:^0.38.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/8e2cd153936681b9a1e13a1e059df18d31a8d2a85c0db11e3d3b00c496f360efe3c864acac9e87de80cc13318f17ce35ecfe0aea22d502b42109cd1164e39619 + checksum: 10/94a05b1401fa071c2483d241a0cffb3fc79492c44ee7a2d2e320118cbc948eb7972c70e93f128bae29febd4958fc95904afff56b5c49da8b5407666e8f26e748 languageName: node linkType: hard -"@opentelemetry/instrumentation-redis@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-redis@npm:0.45.0" +"@opentelemetry/instrumentation-restify@npm:^0.48.2": + version: 0.48.2 + resolution: "@opentelemetry/instrumentation-restify@npm:0.48.2" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/redis-common": "npm:^0.36.2" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/8bb137bc04be5b6d4afef365c5c52d8148c65f40c77af2010173ae2c481a85a24e9d1ffe36821315af7f177ba22f32f05b5d017d1cbcf1c298f4bbf3dff56d8a + checksum: 10/b2a0917248ae9defaff8dd9d5c650fb627241b56355474a776a54fd9541a29b3bbb487b54dba7042ac2f7e15cb0a79f738360fe8a3322e4066ff06728e027292 languageName: node linkType: hard -"@opentelemetry/instrumentation-restify@npm:^0.44.0": - version: 0.44.0 - resolution: "@opentelemetry/instrumentation-restify@npm:0.44.0" +"@opentelemetry/instrumentation-router@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/instrumentation-router@npm:0.47.0" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/be37a7f3aeaa085e8ed3f3cddcc8cd6616a73f865337b5a5e086cc05161b258a01ee45cc41e22d2f8c8a9d2254be04c8d3b866b31b76315cf3087a8b0914c864 + checksum: 10/620a54e97e311692c197e86f12bf43bf1a04f2b3009f71dfdf6e0972b5c282606cc649739ba0041f60aea3fae9b027b315378e71a5f764c85952ece2e5b28078 languageName: node linkType: hard -"@opentelemetry/instrumentation-router@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-router@npm:0.43.0" +"@opentelemetry/instrumentation-runtime-node@npm:^0.16.0": + version: 0.16.0 + resolution: "@opentelemetry/instrumentation-runtime-node@npm:0.16.0" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/85d5a1302558fc99d5ec411ef4ea7306bb2c5295807ef3e75f61272b46be1752a19de145d22dbe530803ddfef65a4811ecccb5944183302ff60b8762005de70d + languageName: node + linkType: hard + +"@opentelemetry/instrumentation-socket.io@npm:^0.49.0": + version: 0.49.0 + resolution: "@opentelemetry/instrumentation-socket.io@npm:0.49.0" + dependencies: + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/3c2670151a24528910b15b85e3f8ec69468170d35ffe574850dbafee1efb055a14c6d231ccda03151870add4976c9da41171190aff859ca993be514fd5c6e104 + checksum: 10/16ab1664c3692267a6b9d0fdaf9256d66d9b705c056c019fb15d261d1f09296d325ddbbc9402b60b0d3b5722b55cdb6c7d0980c8ee965ef4346461742cdf87a1 languageName: node linkType: hard -"@opentelemetry/instrumentation-socket.io@npm:^0.45.0": - version: 0.45.0 - resolution: "@opentelemetry/instrumentation-socket.io@npm:0.45.0" +"@opentelemetry/instrumentation-tedious@npm:^0.21.1": + version: 0.21.1 + resolution: "@opentelemetry/instrumentation-tedious@npm:0.21.1" dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" - "@opentelemetry/semantic-conventions": "npm:^1.27.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/fa0550011cde7a06f5c25f655c9ea1d0df284d63e2eb61599add27a16225d952b2c573364478654f9a0694f4716334f5618ffa9e8991bb078b202b78041cd7c0 - languageName: node - linkType: hard - -"@opentelemetry/instrumentation-tedious@npm:^0.17.0": - version: 0.17.0 - resolution: "@opentelemetry/instrumentation-tedious@npm:0.17.0" - dependencies: - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" "@types/tedious": "npm:^4.0.14" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/62594f42fddc7af06aa14575e70d821551842671d2f2013e583d58d508d6ca8e6749faf89453e8254e6257483390cf8fd9ca0076327850d54e088d436f13ac51 + checksum: 10/5347d20e2cb4e07a1d25275edb25c47dcf87700a19de630fb33b7fc06bda0eb5934fb058fcb15b33be2eeb49a7430720c011f8540405b5ee10d22e689c93b94a languageName: node linkType: hard -"@opentelemetry/instrumentation-undici@npm:^0.9.0": - version: 0.9.0 - resolution: "@opentelemetry/instrumentation-undici@npm:0.9.0" +"@opentelemetry/instrumentation-undici@npm:^0.13.2": + version: 0.13.2 + resolution: "@opentelemetry/instrumentation-undici@npm:0.13.2" dependencies: - "@opentelemetry/core": "npm:^1.8.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.7.0 - checksum: 10/e7300fb0353cc0648cc8a1779287e6780b6aaf3b4f13698a203367cfb9d571677e8f5897b3e79cfede196e72007ac2a1f4fbb8128a61ceb2b85517ebb30effd4 + checksum: 10/9f806706096d629796d1205a0f2a9f18cf3542f61dfb60f20199ca5ba269044145a593f5ee0ad495b046eb01b8a14448a61a8bbe6a18611f6967f3473923d97e languageName: node linkType: hard -"@opentelemetry/instrumentation-winston@npm:^0.43.0": - version: 0.43.0 - resolution: "@opentelemetry/instrumentation-winston@npm:0.43.0" +"@opentelemetry/instrumentation-winston@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/instrumentation-winston@npm:0.47.0" dependencies: - "@opentelemetry/api-logs": "npm:^0.56.0" - "@opentelemetry/instrumentation": "npm:^0.56.0" + "@opentelemetry/api-logs": "npm:^0.202.0" + "@opentelemetry/instrumentation": "npm:^0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/9cd8e4b047bea576e6277c911d1f44df2b880b2c9f01081fff9b5ee4f1131dd6dd481ee11ab887344d22535211af1f6a75bcd9c34185183855acdd12975024ca + checksum: 10/1a046490c1509f77ae70552ef6cdbf6c21baba4f5d6f45e0c473ffa102156b009acd20036dafbf5d29e92132df88c4aad078b62606af3bda0fbdd4ab90073937 + languageName: node + linkType: hard + +"@opentelemetry/instrumentation@npm:0.202.0, @opentelemetry/instrumentation@npm:^0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/instrumentation@npm:0.202.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.202.0" + import-in-the-middle: "npm:^1.8.1" + require-in-the-middle: "npm:^7.1.1" + peerDependencies: + "@opentelemetry/api": ^1.3.0 + checksum: 10/da1db1ebc4ca847cc68d894b2e3a6c6552851d93af8ea793d42474e920f710664575c8991dc269e1a83fcf8f5abdda2cc724fa24e9cc4ae19fa6f70eb68ffc0e languageName: node linkType: hard @@ -14536,19 +15033,15 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/instrumentation@npm:0.56.0, @opentelemetry/instrumentation@npm:^0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/instrumentation@npm:0.56.0" +"@opentelemetry/otlp-exporter-base@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/otlp-exporter-base@npm:0.202.0" dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@types/shimmer": "npm:^1.2.0" - import-in-the-middle: "npm:^1.8.1" - require-in-the-middle: "npm:^7.1.1" - semver: "npm:^7.5.2" - shimmer: "npm:^1.2.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-transformer": "npm:0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/7c3802eb6b55b39b6904526d052b918619c9cde0f71a35bc2f23ac6c3a10ea66b08a65adf6862cdbaac7b231fb5119204b4d5531be25b96933a9d8b91a9ce062 + checksum: 10/229778895ba1971451a8b1ec8a4787b0cdba337c87f3ca6aaf6d649f5e2b26439ba1befa36d31824fffa3cef62382792680ec70194f57aac6ebe98e95acecc69 languageName: node linkType: hard @@ -14564,15 +15057,17 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-exporter-base@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/otlp-exporter-base@npm:0.56.0" +"@opentelemetry/otlp-grpc-exporter-base@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.202.0" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" + "@grpc/grpc-js": "npm:^1.7.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/otlp-exporter-base": "npm:0.202.0" + "@opentelemetry/otlp-transformer": "npm:0.202.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/a640641ae86a9dceeecb84e32e4b8cd56be9d2af202e44ad84022380530bcda4b27b4f7089b87238db8088930256550d385aeca200369021a8085e1288ef34db + checksum: 10/ba52acee1d46c3bca38d6eba8fea41d5c7389c26a34dd7ed4f4fbc46da5fcbf109ae139c5149c9730cd076ba51d4933d9bc06c223470ee8a463a31fed75b4ed6 languageName: node linkType: hard @@ -14590,17 +15085,20 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-grpc-exporter-base@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/otlp-grpc-exporter-base@npm:0.56.0" +"@opentelemetry/otlp-transformer@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/otlp-transformer@npm:0.202.0" dependencies: - "@grpc/grpc-js": "npm:^1.7.1" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/otlp-exporter-base": "npm:0.56.0" - "@opentelemetry/otlp-transformer": "npm:0.56.0" + "@opentelemetry/api-logs": "npm:0.202.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-logs": "npm:0.202.0" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" + protobufjs: "npm:^7.3.0" peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 10/8dd8117357035fff225d23637d0d2298b842f1b5611bf17612aff666a41068ad4d986868818a501a2136f91d788905fb99952b19d60e96fd08f2898422dc22a3 + checksum: 10/67e189af60bf8308a5b93deb85bef9709a5604b8c7915d0d42a7a2bff932b7257dfdfc071812c77913e136955a985ffe838c72b5f10059021087ea0bc52d84cd languageName: node linkType: hard @@ -14621,29 +15119,12 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/otlp-transformer@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/otlp-transformer@npm:0.56.0" - dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-logs": "npm:0.56.0" - "@opentelemetry/sdk-metrics": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" - protobufjs: "npm:^7.3.0" - peerDependencies: - "@opentelemetry/api": ^1.3.0 - checksum: 10/d673b0295fac96b45535e86dea54c7c8e1270c161579de908cf7724fee00f4d5b49652da0a886dcac49c5d12a0d8f2b8be1cf639f4094bc0d16eec7808bddb78 - languageName: node - linkType: hard - -"@opentelemetry/propagation-utils@npm:^0.30.14": - version: 0.30.16 - resolution: "@opentelemetry/propagation-utils@npm:0.30.16" +"@opentelemetry/propagation-utils@npm:^0.31.2": + version: 0.31.2 + resolution: "@opentelemetry/propagation-utils@npm:0.31.2" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/8d282553d08aadfc9f8879ea6fbb33089765a535f960d72ae27fb98e8dab9c7e0394217cb9e2999479f9c2486fa9454cf4ffffeda30f4a96f8fe011276942b75 + checksum: 10/13828caae31651a1c4fa736f254b49a46b62348ff9ece945b19253fb52562db08bd268b5b0944ef3d2cb951f20cd4118ba14158b34a192e63868f0f3aa9c7cc9 languageName: node linkType: hard @@ -14658,14 +15139,14 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/propagator-b3@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/propagator-b3@npm:1.29.0" +"@opentelemetry/propagator-b3@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/propagator-b3@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" + "@opentelemetry/core": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/002e9400d9b605f1af1140dc4d20579d1494d7eb4c93e72862ee5207cc46cd3ed54f41eb42a0a00f0676b3e8f0f1afb2df36918582d833c97fbf4a23f5632f75 + checksum: 10/9871854b3d7516273c1f867c7a6a8eac5791f77954edf7509cc320ee6c109de8ef44778076edd4a64421b84f31632e4d283cb685f9220c5e7f1fe823c0e81a06 languageName: node linkType: hard @@ -14680,87 +15161,87 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/propagator-jaeger@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/propagator-jaeger@npm:1.29.0" +"@opentelemetry/propagator-jaeger@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/propagator-jaeger@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" + "@opentelemetry/core": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/75849bcfaa60823cc8b2bb10a4094ef7d933d998c84977f8319930ed7fdba37aba5906ba56f5ed96f28e07bb2cf5e185b7340993c06a36135a1de25de945df74 + checksum: 10/d6a51e7dd58dde7ada4c0fe510a14f9a33f1972f0db11b64fccb867be23652d4d8a55012dd5a295753ad27c456b21d502eb3a24dfbce6a0a3304e1fa6e1f4896 languageName: node linkType: hard -"@opentelemetry/redis-common@npm:^0.36.2": - version: 0.36.2 - resolution: "@opentelemetry/redis-common@npm:0.36.2" - checksum: 10/e7f610f79c95bab9156a9831162c7b55b94ab43c5e47ecb9efcc10c08a236395fdd54b6bb018da981e6641bac9da6fda1b50636fb49db584e87d988750d255e1 +"@opentelemetry/redis-common@npm:^0.38.0": + version: 0.38.0 + resolution: "@opentelemetry/redis-common@npm:0.38.0" + checksum: 10/c7caa450ed27ad02aeefa3e4d643e7d065d76e5f1cd275c8c3d0c7d324bf7d06434666dbd836d219f6b2e1057285f30ccaa89b949438b38118a70786ec31fa52 languageName: node linkType: hard -"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.29.6": - version: 0.29.7 - resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.29.7" +"@opentelemetry/resource-detector-alibaba-cloud@npm:^0.31.2": + version: 0.31.2 + resolution: "@opentelemetry/resource-detector-alibaba-cloud@npm:0.31.2" dependencies: - "@opentelemetry/core": "npm:^1.26.0" - "@opentelemetry/resources": "npm:^1.10.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/resources": "npm:^2.0.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/1baba0bef0201b4b6f442e3dd47846051b551e5dd0d9c9aaf6223ff292ae719f9263edb81496487166956a5ad67f79f73dda65ec012912f2fab9758fe1199656 + checksum: 10/1e2498d5dc970addaeb88c9a1c4be85248438fe69876f2223ee526535ccb50d1ffaca366bfc5c03d0c84933050fd5d821726d8ce92ac5829da18f4a91025ce2f languageName: node linkType: hard -"@opentelemetry/resource-detector-aws@npm:^1.9.0": - version: 1.12.0 - resolution: "@opentelemetry/resource-detector-aws@npm:1.12.0" +"@opentelemetry/resource-detector-aws@npm:^2.2.0": + version: 2.2.0 + resolution: "@opentelemetry/resource-detector-aws@npm:2.2.0" dependencies: - "@opentelemetry/core": "npm:^1.0.0" - "@opentelemetry/resources": "npm:^1.10.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/resources": "npm:^2.0.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/95702aed5d2a22fab0429439d57ede5c53b3512618af21ffc098b0e6fca86266e9703a32325427082bb5776697d5e74a4e99f6c757e4b6bcf497e32663e705f9 + checksum: 10/91e6b67936d6846ba98b31438a9f32e5486f27153cfcd57978c8b6c5df9cc400c821ea63b06fff158c6a7e97b3ded3396f91f02410f28d4113d6b5bdf75f2bec languageName: node linkType: hard -"@opentelemetry/resource-detector-azure@npm:^0.4.0": - version: 0.4.0 - resolution: "@opentelemetry/resource-detector-azure@npm:0.4.0" +"@opentelemetry/resource-detector-azure@npm:^0.9.0": + version: 0.9.0 + resolution: "@opentelemetry/resource-detector-azure@npm:0.9.0" dependencies: - "@opentelemetry/core": "npm:^1.25.1" - "@opentelemetry/resources": "npm:^1.10.1" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/resources": "npm:^2.0.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/e48408787b9834b0079690d7e5505dd5aad6a2234e61324ae8a84237b26c79c4b585cac93c1aab280be63f4c9ef32b74923858771c7363211036bfebee7a6234 + checksum: 10/93d85781e73f37ab186b8d39ad144c944d80e69d19361ef4f846499e2bf0ba27df2cd00e334b1408415c80488f1633764696e1d29b639ed7829631ec29c92286 languageName: node linkType: hard -"@opentelemetry/resource-detector-container@npm:^0.5.2": - version: 0.5.3 - resolution: "@opentelemetry/resource-detector-container@npm:0.5.3" +"@opentelemetry/resource-detector-container@npm:^0.7.2": + version: 0.7.2 + resolution: "@opentelemetry/resource-detector-container@npm:0.7.2" dependencies: - "@opentelemetry/core": "npm:^1.26.0" - "@opentelemetry/resources": "npm:^1.10.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/resources": "npm:^2.0.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/79fd039e3f960a8fa31af9df52bffc948c3bc2f384d574061f0e0c47fa9bdb0d4b95ff34e94f72b33746a92a427cf284c069633ad07bfdca52c62e03e5d005c5 + checksum: 10/d556c4f64737726e31f211c6163ea359e403b4b0c65bca9fff565ec6a579a18340f4c6dc68d78ac707413c44bcb5f2a362eab2794fbd99524faf161517793e0e languageName: node linkType: hard -"@opentelemetry/resource-detector-gcp@npm:^0.31.0": - version: 0.31.0 - resolution: "@opentelemetry/resource-detector-gcp@npm:0.31.0" +"@opentelemetry/resource-detector-gcp@npm:^0.36.0": + version: 0.36.0 + resolution: "@opentelemetry/resource-detector-gcp@npm:0.36.0" dependencies: - "@opentelemetry/core": "npm:^1.0.0" - "@opentelemetry/resources": "npm:^1.10.0" + "@opentelemetry/core": "npm:^2.0.0" + "@opentelemetry/resources": "npm:^2.0.0" "@opentelemetry/semantic-conventions": "npm:^1.27.0" gcp-metadata: "npm:^6.0.0" peerDependencies: "@opentelemetry/api": ^1.0.0 - checksum: 10/3ad40b7633dd106bac3f7ef27265d88b0fbb197f3754469b3a334e0973335da0e432f9b740a18e8b2731358da565f0a330e60fbf446ac00855bc262fcd75e07d + checksum: 10/9bfffd5d21a90509bc8d4dd3ae27554dd56719ca1607fcc20900034cc5776ff4c0025a3d4fddbd7c12e7962dd747516bdca9a9da6ccc59223c8d485609aea732 languageName: node linkType: hard @@ -14776,27 +15257,28 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/resources@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/resources@npm:1.29.0" +"@opentelemetry/resources@npm:2.0.1, @opentelemetry/resources@npm:^2.0.0": + version: 2.0.1 + resolution: "@opentelemetry/resources@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/677b9e3478a380e93383a223a01ccade21dde7381924a4f859b2309ea82e79da9e7257338791a5b2699f763b0c198ec7e0ed6a12f03e467f5e0d8287757f0f66 + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/282f3831de2755d0fda2d8b6e37f9587ea248066d50c7d2f14c803ac9d5262a0f1db98a4185bcdc5acaeeece0b61f4fce43bc3896a79f1da79045ae4928618bf languageName: node linkType: hard -"@opentelemetry/resources@npm:^1.10.0, @opentelemetry/resources@npm:^1.10.1, @opentelemetry/resources@npm:^1.24.0": - version: 1.30.1 - resolution: "@opentelemetry/resources@npm:1.30.1" +"@opentelemetry/sdk-logs@npm:0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/sdk-logs@npm:0.202.0" dependencies: - "@opentelemetry/core": "npm:1.30.1" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/api-logs": "npm:0.202.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/9b7544b639e8fee41315e2646615676ffb1020dba0f6c81e6ec1dd2daf5409fc6ce3d2b629bbd9cd32f85decc3a8bfa5dc8cc52bb72bd84c1777ca25b4301aa0 + "@opentelemetry/api": ">=1.4.0 <1.10.0" + checksum: 10/e1b76647282a41ad7004c86c0058b0e9be70fdcf34aa2761929401e274af79bc5e3c6610a63e14fde4d51d96d8c96adaf3247c10b9a64cb70086d2ede2e421ca languageName: node linkType: hard @@ -14813,19 +15295,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-logs@npm:0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/sdk-logs@npm:0.56.0" - dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/resources": "npm:1.29.0" - peerDependencies: - "@opentelemetry/api": ">=1.4.0 <1.10.0" - checksum: 10/36f7ef72ce9f1ccc6f8bb1b99a142801bb3e6e4e2f2dda30de713144213c62b144530bb49590379bc413757c8d170fba874619d933f42d0370c5473c22fdeea0 - languageName: node - linkType: hard - "@opentelemetry/sdk-metrics@npm:1.27.0": version: 1.27.0 resolution: "@opentelemetry/sdk-metrics@npm:1.27.0" @@ -14838,15 +15307,47 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/sdk-metrics@npm:1.29.0" +"@opentelemetry/sdk-metrics@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/sdk-metrics@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/resources": "npm:1.29.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + peerDependencies: + "@opentelemetry/api": ">=1.9.0 <1.10.0" + checksum: 10/eb23d0657ce7ef0784f6c89af650de83530099782758fce574316a8e82ff2bca0eb3adffa88c5fdd04eaced6150deb53ea0ea05aae06d2783795691734e85473 + languageName: node + linkType: hard + +"@opentelemetry/sdk-node@npm:^0.202.0": + version: 0.202.0 + resolution: "@opentelemetry/sdk-node@npm:0.202.0" + dependencies: + "@opentelemetry/api-logs": "npm:0.202.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/exporter-logs-otlp-grpc": "npm:0.202.0" + "@opentelemetry/exporter-logs-otlp-http": "npm:0.202.0" + "@opentelemetry/exporter-logs-otlp-proto": "npm:0.202.0" + "@opentelemetry/exporter-metrics-otlp-grpc": "npm:0.202.0" + "@opentelemetry/exporter-metrics-otlp-http": "npm:0.202.0" + "@opentelemetry/exporter-metrics-otlp-proto": "npm:0.202.0" + "@opentelemetry/exporter-prometheus": "npm:0.202.0" + "@opentelemetry/exporter-trace-otlp-grpc": "npm:0.202.0" + "@opentelemetry/exporter-trace-otlp-http": "npm:0.202.0" + "@opentelemetry/exporter-trace-otlp-proto": "npm:0.202.0" + "@opentelemetry/exporter-zipkin": "npm:2.0.1" + "@opentelemetry/instrumentation": "npm:0.202.0" + "@opentelemetry/propagator-b3": "npm:2.0.1" + "@opentelemetry/propagator-jaeger": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/sdk-logs": "npm:0.202.0" + "@opentelemetry/sdk-metrics": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" + "@opentelemetry/sdk-trace-node": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 10/399f28da37d7176f4017cffb2b9c3d3db65bbe397e5f7232c0b2df1a9162f094d7e460e64058c959aca787a80d48e0f98b9b65dfb711b72f41fa04b5b03b0e23 + checksum: 10/80e3c6493bb5d87e5ce9c8b25dc5e2614c879b2ebe4f79f1a373d8237085b6e0bcaaff06acf01528e45849fb27e38cc0961fde6c87d7d2addf90cbcfbea43af7 languageName: node linkType: hard @@ -14876,32 +15377,6 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-node@npm:^0.56.0": - version: 0.56.0 - resolution: "@opentelemetry/sdk-node@npm:0.56.0" - dependencies: - "@opentelemetry/api-logs": "npm:0.56.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/exporter-logs-otlp-grpc": "npm:0.56.0" - "@opentelemetry/exporter-logs-otlp-http": "npm:0.56.0" - "@opentelemetry/exporter-logs-otlp-proto": "npm:0.56.0" - "@opentelemetry/exporter-trace-otlp-grpc": "npm:0.56.0" - "@opentelemetry/exporter-trace-otlp-http": "npm:0.56.0" - "@opentelemetry/exporter-trace-otlp-proto": "npm:0.56.0" - "@opentelemetry/exporter-zipkin": "npm:1.29.0" - "@opentelemetry/instrumentation": "npm:0.56.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/sdk-logs": "npm:0.56.0" - "@opentelemetry/sdk-metrics": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" - "@opentelemetry/sdk-trace-node": "npm:1.29.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" - peerDependencies: - "@opentelemetry/api": ">=1.3.0 <1.10.0" - checksum: 10/ba89ba4ef5ba7b4d75fb167b4bc5247602202d85d0da431c863932a83a313bd82aaad00f0fb3f3ef75a47242c474a5066a967649a585ee0f7cdbec6ddedfa39b - languageName: node - linkType: hard - "@opentelemetry/sdk-trace-base@npm:1.27.0": version: 1.27.0 resolution: "@opentelemetry/sdk-trace-base@npm:1.27.0" @@ -14915,16 +15390,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-base@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/sdk-trace-base@npm:1.29.0" +"@opentelemetry/sdk-trace-base@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/sdk-trace-base@npm:2.0.1" dependencies: - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/resources": "npm:1.29.0" - "@opentelemetry/semantic-conventions": "npm:1.28.0" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/resources": "npm:2.0.1" + "@opentelemetry/semantic-conventions": "npm:^1.29.0" peerDependencies: - "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/97080188cd2ded16cc489ad5414255f0e63a81ca3f8778f604cdb1409bf95691529d7f9233d37788ac14a6ff36b736fcf274ef39bba3fdf421e7201642d2d5b4 + "@opentelemetry/api": ">=1.3.0 <1.10.0" + checksum: 10/9de1e36bbce9bd7c0563e6395765fffc0f8c78806cb33cc95267e98dffd82de33857a51288073a104c10418b934e51560bcb5dcaf4e63e5c9e096f65cadd42cd languageName: node linkType: hard @@ -14944,19 +15419,16 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/sdk-trace-node@npm:1.29.0": - version: 1.29.0 - resolution: "@opentelemetry/sdk-trace-node@npm:1.29.0" +"@opentelemetry/sdk-trace-node@npm:2.0.1": + version: 2.0.1 + resolution: "@opentelemetry/sdk-trace-node@npm:2.0.1" dependencies: - "@opentelemetry/context-async-hooks": "npm:1.29.0" - "@opentelemetry/core": "npm:1.29.0" - "@opentelemetry/propagator-b3": "npm:1.29.0" - "@opentelemetry/propagator-jaeger": "npm:1.29.0" - "@opentelemetry/sdk-trace-base": "npm:1.29.0" - semver: "npm:^7.5.2" + "@opentelemetry/context-async-hooks": "npm:2.0.1" + "@opentelemetry/core": "npm:2.0.1" + "@opentelemetry/sdk-trace-base": "npm:2.0.1" peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.10.0" - checksum: 10/4af09076649bdfbd1b3b12a8d0f9fd9569cb8e821f449a9096aa64253970ab57188d93eb7fcb108c9067e8e7deadf79cdd5146939493d35795e0e66fb9d64aae + checksum: 10/7c5b4a902ba88ef03f02da83f6900080d81bb3264752419981dc44faabfe2bf4dbe91d25fec4a671afd5212151eee535a5205137772a8fd2c8d247950eb6556f languageName: node linkType: hard @@ -14974,21 +15446,28 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:^1.27.0, @opentelemetry/semantic-conventions@npm:~1.30.0": +"@opentelemetry/semantic-conventions@npm:^1.27.0, @opentelemetry/semantic-conventions@npm:^1.29.0, @opentelemetry/semantic-conventions@npm:^1.30.0, @opentelemetry/semantic-conventions@npm:^1.33.1, @opentelemetry/semantic-conventions@npm:^1.34.0": + version: 1.34.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.34.0" + checksum: 10/1892b4cc69c9e00456c809604a980e32696563e96463ff5f9d07e72d5aca73836a7378090509f28f54445ac6e072d2343a888c9d64d9ce287198e899082ff7aa + languageName: node + linkType: hard + +"@opentelemetry/semantic-conventions@npm:~1.30.0": version: 1.30.0 resolution: "@opentelemetry/semantic-conventions@npm:1.30.0" checksum: 10/78df5976f5bcfd00acaea3e609cf06fdd34517ae8db994ae216aaac16c51af97ac22c534bfcbac5218e0086db83ec5ef6cc045b95626cc6ea807686bea549a41 languageName: node linkType: hard -"@opentelemetry/sql-common@npm:^0.40.1": - version: 0.40.1 - resolution: "@opentelemetry/sql-common@npm:0.40.1" +"@opentelemetry/sql-common@npm:^0.41.0": + version: 0.41.0 + resolution: "@opentelemetry/sql-common@npm:0.41.0" dependencies: - "@opentelemetry/core": "npm:^1.1.0" + "@opentelemetry/core": "npm:^2.0.0" peerDependencies: "@opentelemetry/api": ^1.1.0 - checksum: 10/f887b4135be56c9ef6e29f040c9f75f34709e38c11897d59d284d7e73175a2dd2c6267c18061144e81a0045fc461b7813769db2e49c42a8d6becc58b1456d55c + checksum: 10/182915f050b8b685f5499e9fa4ed9a9730fcd404889e5066cfc7d94e9961b1f2780216d776070a4a5c1b94323c287ab426b06f5a8828d5fea3daf664bfd397bb languageName: node linkType: hard @@ -15037,13 +15516,13 @@ __metadata: linkType: hard "@playwright/test@npm:^1.32.3": - version: 1.52.0 - resolution: "@playwright/test@npm:1.52.0" + version: 1.53.2 + resolution: "@playwright/test@npm:1.53.2" dependencies: - playwright: "npm:1.52.0" + playwright: "npm:1.53.2" bin: playwright: cli.js - checksum: 10/e18a4eb626c7bc6cba212ff2e197cf9ae2e4da1c91bfdf08a744d62e27222751173e4b220fa27da72286a89a3b4dea7c09daf384d23708f284b64f98e9a63a88 + checksum: 10/063c4fd82e5022db7f53e85a3b31caa1c302e7d3f874b3acc751ee5fcf8f102068693e393dc5146cdeeb5850f2321366de4ebc5ef12ab81dae52e2c7113a6b49 languageName: node linkType: hard @@ -15738,119 +16217,929 @@ __metadata: languageName: node linkType: hard -"@react-aria/focus@npm:^3.20.1": - version: 3.20.1 - resolution: "@react-aria/focus@npm:3.20.1" +"@react-aria/autocomplete@npm:3.0.0-beta.5": + version: 3.0.0-beta.5 + resolution: "@react-aria/autocomplete@npm:3.0.0-beta.5" dependencies: - "@react-aria/interactions": "npm:^3.24.1" - "@react-aria/utils": "npm:^3.28.1" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/combobox": "npm:^3.12.5" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/listbox": "npm:^3.14.6" + "@react-aria/searchfield": "npm:^3.8.6" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/autocomplete": "npm:3.0.0-beta.2" + "@react-stately/combobox": "npm:^3.10.6" + "@react-types/autocomplete": "npm:3.0.0-alpha.32" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/f8b4427b310a4bd221cb0e38b031a4150c306f7d1177926c5a2bea0dd4370db6e49e220ca2358c283ea4c009f0a65611340486d161193f7943659a60e20a1881 + languageName: node + linkType: hard + +"@react-aria/breadcrumbs@npm:^3.5.26": + version: 3.5.26 + resolution: "@react-aria/breadcrumbs@npm:3.5.26" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/link": "npm:^3.8.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/breadcrumbs": "npm:^3.7.14" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/e0b9fe416cb84281e207ab12644d3bf21d6af82b4ec5d5ac204209ef6aff9abcc242660c23b2f3ef3572aa0f1e72dcc33905066f720afa1ccfc3cf3d4b916c31 + languageName: node + linkType: hard + +"@react-aria/button@npm:^3.13.3": + version: 3.13.3 + resolution: "@react-aria/button@npm:3.13.3" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/toolbar": "npm:3.0.0-beta.18" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/toggle": "npm:^3.8.5" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/c360899930f648c56f16bf5d0d2d8c01b5d59449b87fff4e758e68127aa8e453396ffbdc6ee0fa2fbcf18fce73472041da36d21929b2d28bd55770bbfd75e384 + languageName: node + linkType: hard + +"@react-aria/calendar@npm:^3.8.3": + version: 3.8.3 + resolution: "@react-aria/calendar@npm:3.8.3" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/calendar": "npm:^3.8.2" + "@react-types/button": "npm:^3.12.2" + "@react-types/calendar": "npm:^3.7.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/53570c8c95fced3704a908ba218ee316db46f5c58352a5970f3b0a863257d8293dd732b0aaeb983921358f45acd50df623db88afa49df4d6735508d08369212b + languageName: node + linkType: hard + +"@react-aria/checkbox@npm:^3.15.7": + version: 3.15.7 + resolution: "@react-aria/checkbox@npm:3.15.7" + dependencies: + "@react-aria/form": "npm:^3.0.18" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/toggle": "npm:^3.11.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/checkbox": "npm:^3.6.15" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/toggle": "npm:^3.8.5" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/a041baf3977d41f0f46db184b80da9f4d928209ca0bd764cb43202e5fc2f7a1ed44d7b9963d38f11dab934bf0ac5c0ee9311894b8177ed890c4f174b0640b8c8 + languageName: node + linkType: hard + +"@react-aria/collections@npm:3.0.0-rc.3": + version: 3.0.0-rc.3 + resolution: "@react-aria/collections@npm:3.0.0-rc.3" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/a7a6ba15554ba6f3047cf6898a222e800b8317d4f0b05dbe11cf4301bc3aaa249ea2ae855ec4b0afd9f95c33e47f9ad10227299f6c382995155d91a94ce91493 + languageName: node + linkType: hard + +"@react-aria/color@npm:^3.0.9": + version: 3.0.9 + resolution: "@react-aria/color@npm:3.0.9" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/numberfield": "npm:^3.11.16" + "@react-aria/slider": "npm:^3.7.21" + "@react-aria/spinbutton": "npm:^3.6.16" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/visually-hidden": "npm:^3.8.25" + "@react-stately/color": "npm:^3.8.6" + "@react-stately/form": "npm:^3.1.5" + "@react-types/color": "npm:^3.0.6" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/387b1609bafa89297525384925f5c7e290f5a2b592488daae4482873518401b0b5f1eb712000a3ad823f66888937499d3e0522b85ae9adc1e819ea8761abf19a + languageName: node + linkType: hard + +"@react-aria/combobox@npm:^3.12.5": + version: 3.12.5 + resolution: "@react-aria/combobox@npm:3.12.5" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/listbox": "npm:^3.14.6" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/menu": "npm:^3.18.5" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/combobox": "npm:^3.10.6" + "@react-stately/form": "npm:^3.1.5" + "@react-types/button": "npm:^3.12.2" + "@react-types/combobox": "npm:^3.13.6" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/171d27bb3a2eefc3c5e3ecdca1d760e44116a68d6169bfa214b05caf840aa879033855a3c1ea273f8d288d066800182f4f8a1299c3dbb7f0dff137db78ad9c41 + languageName: node + linkType: hard + +"@react-aria/datepicker@npm:^3.14.5": + version: 3.14.5 + resolution: "@react-aria/datepicker@npm:3.14.5" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@internationalized/number": "npm:^3.6.3" + "@internationalized/string": "npm:^3.2.7" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/form": "npm:^3.0.18" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/spinbutton": "npm:^3.6.16" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/datepicker": "npm:^3.14.2" + "@react-stately/form": "npm:^3.1.5" + "@react-types/button": "npm:^3.12.2" + "@react-types/calendar": "npm:^3.7.2" + "@react-types/datepicker": "npm:^3.12.2" + "@react-types/dialog": "npm:^3.5.19" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/440475577e9aee6c18c5e8f5d2c91f22f5a528ee283b472627eb8cf226c7a2125fa537b30b7e5f20e9d6325631e13c5dfa23f88d6f7e6c1e9fd41eff8ccb7590 + languageName: node + linkType: hard + +"@react-aria/dialog@npm:^3.5.27": + version: 3.5.27 + resolution: "@react-aria/dialog@npm:3.5.27" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/dialog": "npm:^3.5.19" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/21257bb14352ab611e5659ce555cd845d8de51d30eea0cf7882b5119848c2136cd35f796a3031bbbcbc9640bffadeb35eee6f6254b34f2791c5a739c550e60da + languageName: node + linkType: hard + +"@react-aria/disclosure@npm:^3.0.6": + version: 3.0.6 + resolution: "@react-aria/disclosure@npm:3.0.6" + dependencies: + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/disclosure": "npm:^3.0.5" + "@react-types/button": "npm:^3.12.2" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/c1e602d6bc91b8b4884b2ec4ef4c3f1ea3d3052ead219a73eb4be110348ef0bde8fd3a68af92e7d8af83a59465b208e8a0d363b1ecffde53134516ce13f0361a + languageName: node + linkType: hard + +"@react-aria/dnd@npm:^3.10.1": + version: 3.10.1 + resolution: "@react-aria/dnd@npm:3.10.1" + dependencies: + "@internationalized/string": "npm:^3.2.7" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/dnd": "npm:^3.6.0" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/bf1058f59d20054571541455d9fa25eff469363b6f65852d237d9479bee4d6323fc3bd8497115d16e93d4396ab2faba0317875416c93d65a1a566881771624c1 + languageName: node + linkType: hard + +"@react-aria/focus@npm:^3.20.5": + version: 3.20.5 + resolution: "@react-aria/focus@npm:3.20.5" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/7f4652ebbb19a4ede9abce38bf756a44fdebcd6aa6d095ac304cefbed014f1456c45b3cc4e66d0132ffc55ebd5b9c8ba1ed83f61c39b63007a307d06cc12efc3 + checksum: 10/543c2f18c1d4f10940662608ca67947d1bc22630ae272e73136b36c2aefec61243148668201442132825bf4e9c0ab64add86c9870b9a51cb7bde2dfab7453a6e languageName: node linkType: hard -"@react-aria/i18n@npm:^3.12.7": - version: 3.12.7 - resolution: "@react-aria/i18n@npm:3.12.7" +"@react-aria/form@npm:^3.0.18": + version: 3.0.18 + resolution: "@react-aria/form@npm:3.0.18" dependencies: - "@internationalized/date": "npm:^3.7.0" - "@internationalized/message": "npm:^3.1.6" - "@internationalized/number": "npm:^3.6.0" - "@internationalized/string": "npm:^3.2.5" - "@react-aria/ssr": "npm:^3.9.7" - "@react-aria/utils": "npm:^3.28.1" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/form": "npm:^3.1.5" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/5f086ccc0a7e03637a4c74b2178aa8da9d5992eb15bc55f6ae804692bce01eeca89cc05e967eda6f2705ffb61404c5f9f995f9907063fba212685a7145f54d3d + checksum: 10/8c3edfaf3f2be28a16d7b0dcf00c677926e6e505fe26ebc348201f6f4e9e44aced1f17400a6c19717b18a3378be28438f269b438317a18f2070529fe2ac1e770 languageName: node linkType: hard -"@react-aria/interactions@npm:^3.24.1": - version: 3.24.1 - resolution: "@react-aria/interactions@npm:3.24.1" +"@react-aria/grid@npm:^3.14.2": + version: 3.14.2 + resolution: "@react-aria/grid@npm:3.14.2" dependencies: - "@react-aria/ssr": "npm:^3.9.7" - "@react-aria/utils": "npm:^3.28.1" - "@react-stately/flags": "npm:^3.1.0" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/grid": "npm:^3.11.3" + "@react-stately/selection": "npm:^3.20.3" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/79e40d68b980a84b0a64d8ebf030303558693b489694d439e46e419c79355d632cf3975a52eed30ddce90fff115d9172f6e3e8af5777b69c1a143f4ad81fef1e + checksum: 10/5f19539b1fe01d82b3693166269acae3fe74467b318f257babca79e5689b932ec83e5d7894ea0cbfd5e568fae3ffa61d04ae049a8967af09384f12d8e75792fa languageName: node linkType: hard -"@react-aria/overlays@npm:^3.26.1": - version: 3.26.1 - resolution: "@react-aria/overlays@npm:3.26.1" +"@react-aria/gridlist@npm:^3.13.2": + version: 3.13.2 + resolution: "@react-aria/gridlist@npm:3.13.2" dependencies: - "@react-aria/focus": "npm:^3.20.1" - "@react-aria/i18n": "npm:^3.12.7" - "@react-aria/interactions": "npm:^3.24.1" - "@react-aria/ssr": "npm:^3.9.7" - "@react-aria/utils": "npm:^3.28.1" - "@react-aria/visually-hidden": "npm:^3.8.21" - "@react-stately/overlays": "npm:^3.6.14" - "@react-types/button": "npm:^3.11.0" - "@react-types/overlays": "npm:^3.8.13" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/grid": "npm:^3.14.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/list": "npm:^3.12.3" + "@react-stately/tree": "npm:^3.9.0" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/25d582fa5905c9ec42339ddae4aa9fce5d606a504e657d243b0b72fb77316b879c105b4d89e9c394de0aa9ea8514c38c42278d36b6fdd5de57233785fbd17a8c + checksum: 10/ea5e2f986fc74b627afe4918f94aa8cc778f146d7781822ce55788bb59a90e5765446d7c0bb5bcc86c1e4dd17ced3bd52ab6f703a50e8253bef25b70d3be0b56 languageName: node linkType: hard -"@react-aria/ssr@npm:^3.9.7": - version: 3.9.7 - resolution: "@react-aria/ssr@npm:3.9.7" +"@react-aria/i18n@npm:^3.12.10": + version: 3.12.10 + resolution: "@react-aria/i18n@npm:3.12.10" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@internationalized/message": "npm:^3.1.8" + "@internationalized/number": "npm:^3.6.3" + "@internationalized/string": "npm:^3.2.7" + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6f331ceb62a1364d32f2336257ed82715f712e27d3e04a173296efc320f6532626561e883c2c3639ef4efdcd03498005e87d7160d8942ea8255d18d57770d3a0 + languageName: node + linkType: hard + +"@react-aria/interactions@npm:^3.25.3": + version: 3.25.3 + resolution: "@react-aria/interactions@npm:3.25.3" + dependencies: + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/flags": "npm:^3.1.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/21754f3e15c35430262b8e283d00010543c8855bac9b9feeb701c967394940d4063b63380b59637c7da8e9b922f29edb3525d15d6005202a6dc22fd32a0d951f + languageName: node + linkType: hard + +"@react-aria/label@npm:^3.7.19": + version: 3.7.19 + resolution: "@react-aria/label@npm:3.7.19" + dependencies: + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/984a97ac0bca6487031bc006d8b697766d990e78e381d5e230414429542e51d84197459b45b834069615d9f17f5b162e671e464e62f78854ac8fbb458d1b3346 + languageName: node + linkType: hard + +"@react-aria/landmark@npm:^3.0.4": + version: 3.0.4 + resolution: "@react-aria/landmark@npm:3.0.4" + dependencies: + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/ab1d9cc93e83b0f486157dc878474e59b601631220bb688840c55baf6ccfd65d8fe2a04adaaa055f0e2883f3e7428b74d52cc13d32e15f930c0cb1c189309ee8 + languageName: node + linkType: hard + +"@react-aria/link@npm:^3.8.3": + version: 3.8.3 + resolution: "@react-aria/link@npm:3.8.3" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/link": "npm:^3.6.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/978f875a81616f46706da3596dfb52845af93420c3b2ce29d83c8eed5203cd0b5bdd3f988039dff3e3420de668927be46c2971333d628fa7af3f83b7796159f8 + languageName: node + linkType: hard + +"@react-aria/listbox@npm:^3.14.6": + version: 3.14.6 + resolution: "@react-aria/listbox@npm:3.14.6" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/list": "npm:^3.12.3" + "@react-types/listbox": "npm:^3.7.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/068a1e5d56516436dbc8bde6e3a98db7ec277f0a0ce73ce6334e762c196570ae751dc0b0503f1e66624569d89ce412bc9292fa67f5a52db03db3104bb1d2f300 + languageName: node + linkType: hard + +"@react-aria/live-announcer@npm:^3.4.3": + version: 3.4.3 + resolution: "@react-aria/live-announcer@npm:3.4.3" + dependencies: + "@swc/helpers": "npm:^0.5.0" + checksum: 10/73e3fc9bc2796cbccbcd0e01e2b4bd62d0b1a3915b1d0f01d2054141033c6b2a5275a7b7c52bdc0a3b9ef738881b4a84bb5d6df57d1ee066278e5bdf40a8f979 + languageName: node + linkType: hard + +"@react-aria/menu@npm:^3.18.5": + version: 3.18.5 + resolution: "@react-aria/menu@npm:3.18.5" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/menu": "npm:^3.9.5" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/tree": "npm:^3.9.0" + "@react-types/button": "npm:^3.12.2" + "@react-types/menu": "npm:^3.10.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/409f21cfa7d9cca62ca7c21b047bd72314181782b2d6ea0bb16a33075280653eef1b4d9c4b0ad15ebe72ec59e12103f3b1f86e6e7cd8886571956d6d099ae200 + languageName: node + linkType: hard + +"@react-aria/meter@npm:^3.4.24": + version: 3.4.24 + resolution: "@react-aria/meter@npm:3.4.24" + dependencies: + "@react-aria/progress": "npm:^3.4.24" + "@react-types/meter": "npm:^3.4.10" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/980e46d3b784c59523ea9eb342c28e2a29e0549937da4def5f61ce011abb2da40f833dbe3f14550ffcd384860e08d4e8ee286e58834100b04c7de93fa5587e50 + languageName: node + linkType: hard + +"@react-aria/numberfield@npm:^3.11.16": + version: 3.11.16 + resolution: "@react-aria/numberfield@npm:3.11.16" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/spinbutton": "npm:^3.6.16" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/numberfield": "npm:^3.9.13" + "@react-types/button": "npm:^3.12.2" + "@react-types/numberfield": "npm:^3.8.12" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/3e83938fe4fe76ef662c543734988b56d60824d8d2d19de5adffe06191106aa98b2b6dd4241ed49fc3e7ad3e1c4c43cbdd81c6fc32d728959e5b377faf8ffdcd + languageName: node + linkType: hard + +"@react-aria/overlays@npm:^3.26.1, @react-aria/overlays@npm:^3.27.3": + version: 3.27.3 + resolution: "@react-aria/overlays@npm:3.27.3" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/visually-hidden": "npm:^3.8.25" + "@react-stately/overlays": "npm:^3.6.17" + "@react-types/button": "npm:^3.12.2" + "@react-types/overlays": "npm:^3.8.16" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/8cee1a99bc9ff4ecf0577b76f54cccaf684dde2278d0c270e82c683ef6f42bb8100de4596ae411e775643a3765abbd87fdb1c5d46bf05481ed9df9ed9b82276a + languageName: node + linkType: hard + +"@react-aria/progress@npm:^3.4.24": + version: 3.4.24 + resolution: "@react-aria/progress@npm:3.4.24" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/progress": "npm:^3.5.13" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/e7e4cf6f904f1b35a54704aa6d473c4eb239130ba8fbba71f6298b68a92c067162132fb5adf1e6f8960bdc8a2913004d979e088575761b0ba5252c28cdf62b0b + languageName: node + linkType: hard + +"@react-aria/radio@npm:^3.11.5": + version: 3.11.5 + resolution: "@react-aria/radio@npm:3.11.5" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/form": "npm:^3.0.18" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/radio": "npm:^3.10.14" + "@react-types/radio": "npm:^3.8.10" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/63bbca9ad7a065453f60bb759c0377a8febeb0e4582807726d7eef36a05c806d6d0c9d86ad7b73c42cc0a1c47bab5a1d0369e0d670fc4f58924662395418b24d + languageName: node + linkType: hard + +"@react-aria/searchfield@npm:^3.8.6": + version: 3.8.6 + resolution: "@react-aria/searchfield@npm:3.8.6" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/searchfield": "npm:^3.5.13" + "@react-types/button": "npm:^3.12.2" + "@react-types/searchfield": "npm:^3.6.3" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/7db3dd61084e49c9d2c9fa9aa10821087aff91cfadeade78b559cd1877c67a49dcdd238b48a8b2501cd7070b9123d276e8ef6170cb5438cdce28ef6889eea366 + languageName: node + linkType: hard + +"@react-aria/select@npm:^3.15.7": + version: 3.15.7 + resolution: "@react-aria/select@npm:3.15.7" + dependencies: + "@react-aria/form": "npm:^3.0.18" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/listbox": "npm:^3.14.6" + "@react-aria/menu": "npm:^3.18.5" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/visually-hidden": "npm:^3.8.25" + "@react-stately/select": "npm:^3.6.14" + "@react-types/button": "npm:^3.12.2" + "@react-types/select": "npm:^3.9.13" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/8e0974ddc558051c384a7d9da1eeea4623cbe4a61f0f0402aebeb4be23a1cb5875b839608dcc6aaca4ab6c288143c202eb0fa2ee3ed8f62b7e33d142604f2fba + languageName: node + linkType: hard + +"@react-aria/selection@npm:^3.24.3": + version: 3.24.3 + resolution: "@react-aria/selection@npm:3.24.3" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/selection": "npm:^3.20.3" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/f2575161b69d87e7c5ee7d0d658c96c97e815a0de74d121f3744efcfb62f181efd3250f3842b0347d54e9d27c85b41292728927efb3bcc2bdcf8873d92647eb1 + languageName: node + linkType: hard + +"@react-aria/separator@npm:^3.4.10": + version: 3.4.10 + resolution: "@react-aria/separator@npm:3.4.10" + dependencies: + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/706aed510aa306f70ff98a82061995f3fb1ffa6ca0134b282ac9a776441fe4241ff87f8008fa04761b3edaadf8020c96f55720821a7ff4b5c362cad3eb7ed98a + languageName: node + linkType: hard + +"@react-aria/slider@npm:^3.7.21": + version: 3.7.21 + resolution: "@react-aria/slider@npm:3.7.21" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/slider": "npm:^3.6.5" + "@react-types/shared": "npm:^3.30.0" + "@react-types/slider": "npm:^3.7.12" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/1a063533b33f224adf782840664e517a8034a28129648908b7391c648c4141c58103dc9733c81f12b1848320669dcca64a059a3279c4114ae81df796a274da59 + languageName: node + linkType: hard + +"@react-aria/spinbutton@npm:^3.6.16": + version: 3.6.16 + resolution: "@react-aria/spinbutton@npm:3.6.16" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/2f032d3206738902e91661b0e5ff1cdd75005c40b75a219bbf6aab48b3aad16d1d1090c873ab2e1e0400dee74db56c455948ff2ebe0a6c7f1edb200dfea826e5 + languageName: node + linkType: hard + +"@react-aria/ssr@npm:^3.9.9": + version: 3.9.9 + resolution: "@react-aria/ssr@npm:3.9.9" dependencies: "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a5c8e9ffee1dfd3c5b9f66051a7faab11d53ba001ac7f476b61fa4b38fd8b4835c1a85ff2157ec25fb5b63beb88fbae9e80610fa065a30cbe30875fcbca3114b + checksum: 10/d0675357b51784e9dbe3add97aa0e6acc810ab0ef01d1c7a317ff8ead5eae64d66c60cc751ea3d10f874bb381e445099bb31cb7f1955801848ce6dce91b588a2 languageName: node linkType: hard -"@react-aria/utils@npm:^3.28.1": - version: 3.28.1 - resolution: "@react-aria/utils@npm:3.28.1" +"@react-aria/switch@npm:^3.7.5": + version: 3.7.5 + resolution: "@react-aria/switch@npm:3.7.5" dependencies: - "@react-aria/ssr": "npm:^3.9.7" - "@react-stately/flags": "npm:^3.1.0" - "@react-stately/utils": "npm:^3.10.5" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/toggle": "npm:^3.11.5" + "@react-stately/toggle": "npm:^3.8.5" + "@react-types/shared": "npm:^3.30.0" + "@react-types/switch": "npm:^3.5.12" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/1f716296b444b1db28bd55493ddabbdc403fc48a865cbbd0e383e69e2e42b97e778446383740330cf7e76a4a2dcc0de6f84fed1cddd0f0760b6ffa34118794cb + languageName: node + linkType: hard + +"@react-aria/table@npm:^3.17.5": + version: 3.17.5 + resolution: "@react-aria/table@npm:3.17.5" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/grid": "npm:^3.14.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/visually-hidden": "npm:^3.8.25" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/flags": "npm:^3.1.2" + "@react-stately/table": "npm:^3.14.3" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/table": "npm:^3.13.1" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d10e2432b66b0279413fec031673172ed8c49d93185cd1657511467b04ec780bc089a7e45d75c9ba71c94419355107e961e679acd254da8a19d446e1c9343a70 + languageName: node + linkType: hard + +"@react-aria/tabs@npm:^3.10.5": + version: 3.10.5 + resolution: "@react-aria/tabs@npm:3.10.5" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/tabs": "npm:^3.8.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/tabs": "npm:^3.3.16" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/39db9d4d5a0e87e7cbdb91bcdc5385e02fd21cb1e1c181ecf226e6b0fba9c948c03623cceb11d831a87056c6bc3a0528a7e7a9f58292b0c4dfaa6b9cce56f3bb + languageName: node + linkType: hard + +"@react-aria/tag@npm:^3.6.2": + version: 3.6.2 + resolution: "@react-aria/tag@npm:3.6.2" + dependencies: + "@react-aria/gridlist": "npm:^3.13.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/list": "npm:^3.12.3" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/fbfb3a9769077bcc23199aacd80c83b42e89f752d0690c6a5751d9a95b6e760365be2915fa134db9ad040355b341b150270f553ece6f791378a27102e90e7fca + languageName: node + linkType: hard + +"@react-aria/textfield@npm:^3.17.5": + version: 3.17.5 + resolution: "@react-aria/textfield@npm:3.17.5" + dependencies: + "@react-aria/form": "npm:^3.0.18" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@react-types/textfield": "npm:^3.12.3" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/59f2df9650b8acb961a1eaec59bb8a0b18ef6b0b1f3d8bbc2f398ae4fcfc8b3a2a2c22c357319bb53bc166b4e2c624ab58187104621b164de22f465ab342f72d + languageName: node + linkType: hard + +"@react-aria/toast@npm:^3.0.5": + version: 3.0.5 + resolution: "@react-aria/toast@npm:3.0.5" + dependencies: + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/landmark": "npm:^3.0.4" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/toast": "npm:^3.1.1" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d674bde2446e29b44fe393386da154d8a467954edf29c9c2cdb922972ea6f962e4f16220c03a410bb0be555b9f96f96f71146762123b9d9187e081a74696bee2 + languageName: node + linkType: hard + +"@react-aria/toggle@npm:^3.11.5": + version: 3.11.5 + resolution: "@react-aria/toggle@npm:3.11.5" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/toggle": "npm:^3.8.5" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6490a53496b317d3f12c93e347b2d9dd0e7e4835d66eb0823612d3918836338566c0ed2ac22bf4d94649c6019bf411d9c9a1b8f49d420d52eab8f737c2c3f8c5 + languageName: node + linkType: hard + +"@react-aria/toolbar@npm:3.0.0-beta.18": + version: 3.0.0-beta.18 + resolution: "@react-aria/toolbar@npm:3.0.0-beta.18" + dependencies: + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/0ea1d0d73dad265773678fc66712800840b20558d3e19f0b6aa1f0953ba57e48be9ad293d1f48db3e609b017998ded11227d1aebfb998bcfbe05467498318ced + languageName: node + linkType: hard + +"@react-aria/tooltip@npm:^3.8.5": + version: 3.8.5 + resolution: "@react-aria/tooltip@npm:3.8.5" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/tooltip": "npm:^3.5.5" + "@react-types/shared": "npm:^3.30.0" + "@react-types/tooltip": "npm:^3.4.18" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/4398c4ca41ab302b8e50a86c07909b461275bf5025c0163ecb5ae57bbe98e15164927ed58a1137a235996626b19d5397916736ee3257d8bc33d09ec0b1d3f70e + languageName: node + linkType: hard + +"@react-aria/tree@npm:^3.1.1": + version: 3.1.1 + resolution: "@react-aria/tree@npm:3.1.1" + dependencies: + "@react-aria/gridlist": "npm:^3.13.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/tree": "npm:^3.9.0" + "@react-types/button": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d387de7a9de81df8f45a16882ddb24ecf8da06638822b8be4d66357b0b3edd67a85aa185f8cf947c4c8f9036c396bea406dd5e277ef0f74828a29d2dd86fd809 + languageName: node + linkType: hard + +"@react-aria/utils@npm:^3.29.1": + version: 3.29.1 + resolution: "@react-aria/utils@npm:3.29.1" + dependencies: + "@react-aria/ssr": "npm:^3.9.9" + "@react-stately/flags": "npm:^3.1.2" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" clsx: "npm:^2.0.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/80c1ecf6a570a57aec5ef4eaa2a8715b12bcd4d5b2de26934e7e5af5a2c38cdd229cac116624ecd3e7c97271e534111f600d0baf1976675b778799f8711ce9bf + checksum: 10/90af5ee5b9c7db063ba66a28cfecb8594f356c2b0fc4cce2fcee94543f3b160214e4a9c280e83355af89452b88b1def8856493705c770874da7121ba1d3d0cc0 languageName: node linkType: hard -"@react-aria/visually-hidden@npm:^3.8.21": - version: 3.8.21 - resolution: "@react-aria/visually-hidden@npm:3.8.21" +"@react-aria/virtualizer@npm:^4.1.7": + version: 4.1.7 + resolution: "@react-aria/virtualizer@npm:4.1.7" dependencies: - "@react-aria/interactions": "npm:^3.24.1" - "@react-aria/utils": "npm:^3.28.1" - "@react-types/shared": "npm:^3.28.0" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-stately/virtualizer": "npm:^4.4.1" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/50b4598ae295336cd95f38523abdef8b7fbde2b6b52c99a5fa17527474d58e7444199b97d2409dd8c478321c76bf246dd928ddcb1c303ebc8ab04417a5a59516 + checksum: 10/4bb747314ac96da76c64490041ab95f4a0889bab7e5805b0e18cb71bdb290ae73932ddd866657cfcdfac8382d6b3017c3dfb1a91ae415a6d1ab45b11d88f66ec + languageName: node + linkType: hard + +"@react-aria/visually-hidden@npm:^3.8.25": + version: 3.8.25 + resolution: "@react-aria/visually-hidden@npm:3.8.25" + dependencies: + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/362a793beac35fde101e598f4cac9cd98394b9a0171309842e2428b25074ee696358c45d2ded8244bc40a9db8471440994be59b0f347d2c643092c6445aaca61 languageName: node linkType: hard @@ -15893,123 +17182,776 @@ __metadata: languageName: node linkType: hard -"@react-stately/flags@npm:^3.1.0": - version: 3.1.0 - resolution: "@react-stately/flags@npm:3.1.0" +"@react-stately/autocomplete@npm:3.0.0-beta.2": + version: 3.0.0-beta.2 + resolution: "@react-stately/autocomplete@npm:3.0.0-beta.2" dependencies: + "@react-stately/utils": "npm:^3.10.7" "@swc/helpers": "npm:^0.5.0" - checksum: 10/022f183415ad8da9484498c506eed97f7562362b427537eb6325a4f37dcbcf26a52f7d135add1574ea049d6a2baa7b0c7c835fb62ac046ca6e8a3cc43bcaf627 + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/836e593688c33ab118547b60fae93ffb1e9e440aed0ffc8c382290f414bfdc313a88964a00a6bd8b7e7b9aca691538b66adfaafee6065b93cedeb80c5aeef675 languageName: node linkType: hard -"@react-stately/overlays@npm:^3.6.14": +"@react-stately/calendar@npm:^3.8.2": + version: 3.8.2 + resolution: "@react-stately/calendar@npm:3.8.2" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/calendar": "npm:^3.7.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d2858a42dc2c324248c4c1ddb6f6fe66de11bfe2703c85f0fd739075712f668427212207626ed35af2fcbe15cd0ee6e2ad53eafe98766fd7352e0e2f134435c4 + languageName: node + linkType: hard + +"@react-stately/checkbox@npm:^3.6.15": + version: 3.6.15 + resolution: "@react-stately/checkbox@npm:3.6.15" + dependencies: + "@react-stately/form": "npm:^3.1.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/4c2b5213395e59432c59609b3cd2ca8316cf77f18716dcee2abe31af5c4a12fac3125408799b0631771e1095fc5924de7850e09c3fa6e53f56c0f79debc0db77 + languageName: node + linkType: hard + +"@react-stately/collections@npm:^3.12.5": + version: 3.12.5 + resolution: "@react-stately/collections@npm:3.12.5" + dependencies: + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/12024f72ec6602c9447e2bc134da566d99da2e29b41ffba207712be31aaf121b805505397dcfb1ee07dd8d234c40d3018c33364adae271f3231a004775a96fd8 + languageName: node + linkType: hard + +"@react-stately/color@npm:^3.8.6": + version: 3.8.6 + resolution: "@react-stately/color@npm:3.8.6" + dependencies: + "@internationalized/number": "npm:^3.6.3" + "@internationalized/string": "npm:^3.2.7" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/numberfield": "npm:^3.9.13" + "@react-stately/slider": "npm:^3.6.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/color": "npm:^3.0.6" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/20ec019c71676c5d336c89b703729c0c8098219c942843a3018e5542fee49c6db56c47b746c4c9cddfb9ea48213a3a860861722fc5b41239e3de827c2ef84351 + languageName: node + linkType: hard + +"@react-stately/combobox@npm:^3.10.6": + version: 3.10.6 + resolution: "@react-stately/combobox@npm:3.10.6" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/list": "npm:^3.12.3" + "@react-stately/overlays": "npm:^3.6.17" + "@react-stately/select": "npm:^3.6.14" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/combobox": "npm:^3.13.6" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/8bc67ee18d1c896620cc11233a8efdaf8ec7f5ec31d068fbbc80e75a54e385617518e2b420f3211c17b905e3cac6026f8b056ae96ee114ea4e897f4ce1951502 + languageName: node + linkType: hard + +"@react-stately/data@npm:^3.13.1": + version: 3.13.1 + resolution: "@react-stately/data@npm:3.13.1" + dependencies: + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/b6eea6a721d4de13ffad7aa12a76012d297e1fbbc919bad35d99f57bf481feb547d56c74467e1dadf8f0cb6be9027e54faa6b10dc4aeb1b46a5c6598218baa2c + languageName: node + linkType: hard + +"@react-stately/datepicker@npm:^3.14.2": + version: 3.14.2 + resolution: "@react-stately/datepicker@npm:3.14.2" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@internationalized/string": "npm:^3.2.7" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/overlays": "npm:^3.6.17" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/datepicker": "npm:^3.12.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/dd10f99f32e12eef9d361eb183cd52113a1857613be844da9895b5a8954260ace283e43bd1bae3d73a71f1cb5e6381627999f8a24d4ed320903dba50f864183b + languageName: node + linkType: hard + +"@react-stately/disclosure@npm:^3.0.5": + version: 3.0.5 + resolution: "@react-stately/disclosure@npm:3.0.5" + dependencies: + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/b1f3f80e6523a0fb0c325064162a59635f560f41dbadd51fd89db02bbad9bafa843b7b1565420444b3b266f2eb92dcf556916b0dabe06d32bb86a2106849c41c + languageName: node + linkType: hard + +"@react-stately/dnd@npm:^3.6.0": + version: 3.6.0 + resolution: "@react-stately/dnd@npm:3.6.0" + dependencies: + "@react-stately/selection": "npm:^3.20.3" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/9fe59a8d5849af27b5fb0609691bdf0ab14ef8cad8a27405e0c83842d44b171e6d1409716b8e80f12797b187bb553f4acf71a87cd5acd86c76137a3525449194 + languageName: node + linkType: hard + +"@react-stately/flags@npm:^3.1.2": + version: 3.1.2 + resolution: "@react-stately/flags@npm:3.1.2" + dependencies: + "@swc/helpers": "npm:^0.5.0" + checksum: 10/a020c3680c36d9624f765c5916ce95d69959f64887928e8f380f11b5362bb0499a901a5842e4e12eb8e5a776af59212b1ee0c4c6a6681ce75f61dace8b2f9c40 + languageName: node + linkType: hard + +"@react-stately/form@npm:^3.1.5": + version: 3.1.5 + resolution: "@react-stately/form@npm:3.1.5" + dependencies: + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6c392949ca8ac045163f3716a41f03d6ccbe3fd5704401cbece0ba572d43abbb350f5019534765540d5b511b9aeba70ca2c03dc4ba218074b59a3efbea601564 + languageName: node + linkType: hard + +"@react-stately/grid@npm:^3.11.3": + version: 3.11.3 + resolution: "@react-stately/grid@npm:3.11.3" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/selection": "npm:^3.20.3" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/09655c28cef0c3007024af0c01982d2c079e558169dcf1994966c2526c6346b442068e2e9700a8abbbbcbd13781cc0eb5b346d974dc9a85dd5c263722d96f5c7 + languageName: node + linkType: hard + +"@react-stately/layout@npm:^4.3.1": + version: 4.3.1 + resolution: "@react-stately/layout@npm:4.3.1" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/table": "npm:^3.14.3" + "@react-stately/virtualizer": "npm:^4.4.1" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/table": "npm:^3.13.1" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/f7d299038aefba602944b5caa81719388427778edc394582dfe14c70f9f485b74a0da6d0eeb6aab843e3fbdd6af251a3678b1a8c76c77f64adb16d695dbd22cd + languageName: node + linkType: hard + +"@react-stately/list@npm:^3.12.3": + version: 3.12.3 + resolution: "@react-stately/list@npm:3.12.3" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/e8edce5e59e061a4b71621ce7617f4e48cbef5e5c5b6377808e0977f4cad4e541726f255bdeb1c18db867b35ab19883981882bbc27ddbc3e5c26722c496b02f8 + languageName: node + linkType: hard + +"@react-stately/menu@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-stately/menu@npm:3.9.5" + dependencies: + "@react-stately/overlays": "npm:^3.6.17" + "@react-types/menu": "npm:^3.10.2" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/a8163b76de49ec10da7129c306304a96e8f4e689450ffdbf6ab5f56fd8b0316db5beb16ebc58da52c19b4abe28954a7200955c41e1a9088e40f5e5d00f975067 + languageName: node + linkType: hard + +"@react-stately/numberfield@npm:^3.9.13": + version: 3.9.13 + resolution: "@react-stately/numberfield@npm:3.9.13" + dependencies: + "@internationalized/number": "npm:^3.6.3" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/numberfield": "npm:^3.8.12" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/639e2064ed0afd23ec6681b32abca78f9453eb38d11b350247bf4038f6561579f6d444ead6c9c3317c1b1adaaaa22812053cfba8effdd8cb1b199d04b7ce1fbe + languageName: node + linkType: hard + +"@react-stately/overlays@npm:^3.6.17": + version: 3.6.17 + resolution: "@react-stately/overlays@npm:3.6.17" + dependencies: + "@react-stately/utils": "npm:^3.10.7" + "@react-types/overlays": "npm:^3.8.16" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/9c44f181cd8f0b99a40142aebf3d7e1d1df1e96a789b636a5ee877529e60fddece4ebcf32d9812d9848ce0298bf5cb6202df3b680552b32c89f1276bce3f8a7b + languageName: node + linkType: hard + +"@react-stately/radio@npm:^3.10.14": + version: 3.10.14 + resolution: "@react-stately/radio@npm:3.10.14" + dependencies: + "@react-stately/form": "npm:^3.1.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/radio": "npm:^3.8.10" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/45ad56b50f3fcd04c1b335f8f64caceaa6f8483fd84717a461ae04ed8718dabf06f88bf8bd89fcca27d2c6bc548b4a38f0e4ef53ad51f3320aeb85c67b288846 + languageName: node + linkType: hard + +"@react-stately/searchfield@npm:^3.5.13": + version: 3.5.13 + resolution: "@react-stately/searchfield@npm:3.5.13" + dependencies: + "@react-stately/utils": "npm:^3.10.7" + "@react-types/searchfield": "npm:^3.6.3" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6114f1ce0c2e8ddbeedc5b5293783c4e1ecb0cc6f24e09cb6095e32be9b49bdc67619941c16f834850e31acfafd74f4241edaa3529f510ec490a2e6c8e7cdc0b + languageName: node + linkType: hard + +"@react-stately/select@npm:^3.6.14": version: 3.6.14 - resolution: "@react-stately/overlays@npm:3.6.14" + resolution: "@react-stately/select@npm:3.6.14" dependencies: - "@react-stately/utils": "npm:^3.10.5" - "@react-types/overlays": "npm:^3.8.13" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/list": "npm:^3.12.3" + "@react-stately/overlays": "npm:^3.6.17" + "@react-types/select": "npm:^3.9.13" + "@react-types/shared": "npm:^3.30.0" "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/84ca6cd1a528fd0c94f5c8d0c6609a915fd7de75d70b222ab6e252456f8d67b0a394787aca97e59a9737b7e3d48bf0600d12e5d9487525e37305eea599a96fcf + checksum: 10/33116af11830f0d7bf7e10cb03e52ed9c36d17d94674d96440fd73ae8d7da1b93edc4de6170a3681493c76c0ad5c8b24c2961f170b2a784447743775dce32a65 languageName: node linkType: hard -"@react-stately/utils@npm:^3.10.5": - version: 3.10.5 - resolution: "@react-stately/utils@npm:3.10.5" +"@react-stately/selection@npm:^3.20.3": + version: 3.20.3 + resolution: "@react-stately/selection@npm:3.20.3" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/9903810f5f4e2f37381aba8c81525a819de69316c3bd5d66b29fc5f22e49b3d349d26792e811f7167e16857879c8e6ce8d817d9838c69367481a42977a2022a2 + languageName: node + linkType: hard + +"@react-stately/slider@npm:^3.6.5": + version: 3.6.5 + resolution: "@react-stately/slider@npm:3.6.5" + dependencies: + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@react-types/slider": "npm:^3.7.12" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/121f2f23cad929e7087825ec1a17c51610ac0e62a0b0951d042a6e6a354fcea1751575a7bf6648ae5054a260d89a7613dc0353bf0e9f76553e25c2dce1dba095 + languageName: node + linkType: hard + +"@react-stately/table@npm:^3.14.3": + version: 3.14.3 + resolution: "@react-stately/table@npm:3.14.3" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/flags": "npm:^3.1.2" + "@react-stately/grid": "npm:^3.11.3" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/table": "npm:^3.13.1" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/04cd616d526b9b806808ea09ce596dc1fe5fff931deec5ccce3258d0a76ee02c787320004ccc2d3a2d67e54d39f7ab274673210254c8598bd99b767cec8eda1a + languageName: node + linkType: hard + +"@react-stately/tabs@npm:^3.8.3": + version: 3.8.3 + resolution: "@react-stately/tabs@npm:3.8.3" + dependencies: + "@react-stately/list": "npm:^3.12.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/tabs": "npm:^3.3.16" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/5fb036bc36b5c7fde32c6e7553795fad0f5b2d999837961298341e68d211444ca2a378554c5f73ccf0909e60299cdcd4b0e8c317e57f8c1662e6384957a1ed4a + languageName: node + linkType: hard + +"@react-stately/toast@npm:^3.1.1": + version: 3.1.1 + resolution: "@react-stately/toast@npm:3.1.1" + dependencies: + "@swc/helpers": "npm:^0.5.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/c6ff8b947920e05e3e817cbb478f31c45c64035801456c7c8052c37bea5d966a3ea2c74071c2bed6f2006dec160a6b1204b7cc98965a9c10ca3b4bb4d6765f39 + languageName: node + linkType: hard + +"@react-stately/toggle@npm:^3.8.5": + version: 3.8.5 + resolution: "@react-stately/toggle@npm:3.8.5" + dependencies: + "@react-stately/utils": "npm:^3.10.7" + "@react-types/checkbox": "npm:^3.9.5" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/3d8e6c4a39c64fc904355aaa3980e3a63595c24472e3f557716597ac6f9f26299dec5b0843b327b4fe1416436054a65e45ec79073a3be1fad8f093f0d2940864 + languageName: node + linkType: hard + +"@react-stately/tooltip@npm:^3.5.5": + version: 3.5.5 + resolution: "@react-stately/tooltip@npm:3.5.5" + dependencies: + "@react-stately/overlays": "npm:^3.6.17" + "@react-types/tooltip": "npm:^3.4.18" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/0744283f1dbb811502311370495b6722a59c251fe20ac2e5373aeed8f655ec9f89d92980426a69b7191c048f7c02e3a66c78a7018452ad4638c82e5b7f99b698 + languageName: node + linkType: hard + +"@react-stately/tree@npm:^3.9.0": + version: 3.9.0 + resolution: "@react-stately/tree@npm:3.9.0" + dependencies: + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/utils": "npm:^3.10.7" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6980382c94f87a663124e9488e3cf5f8ea09b05fc2f0017cbb886aa19ce0d8525d8b6b8221f1e293a67543cc06786ea203813218c3444fe7e14b31d9eae65ca3 + languageName: node + linkType: hard + +"@react-stately/utils@npm:^3.10.7": + version: 3.10.7 + resolution: "@react-stately/utils@npm:3.10.7" dependencies: "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/76133eb64fa945216e51d8a81a0ebd06eeb78aa2d9c91d79eeb80ff44c70a6b0d6d940618b31b499fee8216640b3bf6183391151dc1769e756b56ff6b5e167ec + checksum: 10/b9c1a62d4f179e252d08a2f18120a02ffc8704424f257e81e5e74da1222e3acd1e821ce84220d87d9d7b4b6d4dc2fd58d5bb380546d198bf434b700cda8ac3d8 languageName: node linkType: hard -"@react-types/button@npm:^3.11.0": - version: 3.11.0 - resolution: "@react-types/button@npm:3.11.0" +"@react-stately/virtualizer@npm:^4.4.1": + version: 4.4.1 + resolution: "@react-stately/virtualizer@npm:4.4.1" dependencies: - "@react-types/shared": "npm:^3.28.0" + "@react-aria/utils": "npm:^3.29.1" + "@react-types/shared": "npm:^3.30.0" + "@swc/helpers": "npm:^0.5.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/6dbcc89f576eb6736deb1d5f92eab3c4de5ee5a78a8034b1cd2748c14d92ce05b40362ff5b629842e363d9843f823b5c044dabb3d7438f897777ff60fc5e3867 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/aab5c3a647dc43dd5120a6db4cdfdf24aa47f7fcabf273e05947e33af8cba172f4f442499729d53537a2aa46ca7292dcb94c6b4b4c5b017542df42c25a97a128 languageName: node linkType: hard -"@react-types/overlays@npm:^3.8.13": - version: 3.8.13 - resolution: "@react-types/overlays@npm:3.8.13" +"@react-types/autocomplete@npm:3.0.0-alpha.32": + version: 3.0.0-alpha.32 + resolution: "@react-types/autocomplete@npm:3.0.0-alpha.32" dependencies: - "@react-types/shared": "npm:^3.28.0" + "@react-types/combobox": "npm:^3.13.6" + "@react-types/searchfield": "npm:^3.6.3" + "@react-types/shared": "npm:^3.30.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/a585a2bf211af8996078dee3b7c179617ca889747049f22b8306a6e48c09afa3828a682a46f7be3c36cc1e2282f6bb98b5095c263c466634f1775a1c01599ecd + checksum: 10/ad617628499db45b2acb07d2f4a4b7aa0569124ee1b7c35b2f1eb9819a2d09a755ab4d3be489243c0d579fc16485aec24950ce3b132a655f83d302eb0d140f96 languageName: node linkType: hard -"@react-types/shared@npm:^3.28.0": - version: 3.28.0 - resolution: "@react-types/shared@npm:3.28.0" +"@react-types/breadcrumbs@npm:^3.7.14": + version: 3.7.14 + resolution: "@react-types/breadcrumbs@npm:3.7.14" + dependencies: + "@react-types/link": "npm:^3.6.2" + "@react-types/shared": "npm:^3.30.0" peerDependencies: react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 - checksum: 10/b8dcc73b950c68fdf22a1173e6e4cfc17f51c579455b3b3239cefd50ebe8bcd6a71db00f643ef8c8f6cd6a2b142780bbce243bda571f5f30be94a04ce30547cb + checksum: 10/106fa21ce01b9ee16796ba46d1133b71b466bfc069e9a90d7f00280e177e1f009791db78701565d83c22506bf72b0469756238164d8ffa36c6712f4e95932eac languageName: node linkType: hard -"@redis/bloom@npm:1.2.0": - version: 1.2.0 - resolution: "@redis/bloom@npm:1.2.0" +"@react-types/button@npm:^3.12.2": + version: 3.12.2 + resolution: "@react-types/button@npm:3.12.2" + dependencies: + "@react-types/shared": "npm:^3.30.0" peerDependencies: - "@redis/client": ^1.0.0 - checksum: 10/a16408f729ddd032a52c9d998661dfa7beabc0e92760d30619c3166c7a53a98c037956d93d230b787005fd8a599a7456461ca7429c1916893c2d13d59a41e0e6 + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d00f7e23fd6cfa863c2d9f0f91ec47fc017666b55775842af3c001fabcf949dfa55b116674bcb841de568eacaf677c3a25e079b4810dcf857547bd9cf6d60917 languageName: node linkType: hard -"@redis/client@npm:1.6.0": - version: 1.6.0 - resolution: "@redis/client@npm:1.6.0" +"@react-types/calendar@npm:^3.7.2": + version: 3.7.2 + resolution: "@react-types/calendar@npm:3.7.2" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/40ad15a5f937844f6626028c8a6e7d36f09e946b4cbdfcd95ff4971f6681abd86260f64aa8298cf86fdde5a0ce2de17e327b8011bb435d0f3faf10fcd9c97f38 + languageName: node + linkType: hard + +"@react-types/checkbox@npm:^3.9.5": + version: 3.9.5 + resolution: "@react-types/checkbox@npm:3.9.5" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/25f6c155043f25f55379b613e3e7faa16191f34c04f13acfc75fc5bda5b83d76d3062b177aad2401f9dd1570215714d477ab1d1e3b92a33e24172aa44e9ff79e + languageName: node + linkType: hard + +"@react-types/color@npm:^3.0.6": + version: 3.0.6 + resolution: "@react-types/color@npm:3.0.6" + dependencies: + "@react-types/shared": "npm:^3.30.0" + "@react-types/slider": "npm:^3.7.12" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/33ddf5239ccfdada1ae276a69b020500dae557a0a43c4bb0fb55fcd6122331c7f2adb552ff3706753214964032ea459634708a02f147777dff948045877cc01b + languageName: node + linkType: hard + +"@react-types/combobox@npm:^3.13.6": + version: 3.13.6 + resolution: "@react-types/combobox@npm:3.13.6" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/8aaf970af426d084ba4b954aeca23cc7779de221a19465c1487d7098a5f637c13bcb6446bf695619e221d78d289cf629b94faa2c86de8bb3f949ff05a5448a13 + languageName: node + linkType: hard + +"@react-types/datepicker@npm:^3.12.2": + version: 3.12.2 + resolution: "@react-types/datepicker@npm:3.12.2" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@react-types/calendar": "npm:^3.7.2" + "@react-types/overlays": "npm:^3.8.16" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/21d6b72986b18b25b6380bf85a7a12319de05160a1dee5d0e7484378627dee955c6cda9fbb0b6909e9edcfc82181be1d9e744220fcf82d75357d7208ed2978a9 + languageName: node + linkType: hard + +"@react-types/dialog@npm:^3.5.19": + version: 3.5.19 + resolution: "@react-types/dialog@npm:3.5.19" + dependencies: + "@react-types/overlays": "npm:^3.8.16" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/07bd55d214d8162f1a700f1ac80cd51468d0defc0d9753309d5e58a0fffbc1eb65a588b6c0709593a41fa8d72cc1f8f8dba0a65b2a336968fdc2ff92afec3596 + languageName: node + linkType: hard + +"@react-types/form@npm:^3.7.13": + version: 3.7.13 + resolution: "@react-types/form@npm:3.7.13" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/2c55ceb9f5e52a08b60ae0ae95d1db3e57a67d3783eaee2fb37870602afd612153506c13effec98bf4c7b58728a148e9d966c8c3442db560543779b9ef8dd091 + languageName: node + linkType: hard + +"@react-types/grid@npm:^3.3.3": + version: 3.3.3 + resolution: "@react-types/grid@npm:3.3.3" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/cac232888139186110f7095cbb45ba2525522a296254f44290ad4c87ac2b1e72485d839e2820e7265cbcd6bc4cb8e49c170c52fd57defed16d06a4eb7f7cf047 + languageName: node + linkType: hard + +"@react-types/link@npm:^3.6.2": + version: 3.6.2 + resolution: "@react-types/link@npm:3.6.2" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/093a3011a98f36dd252d7675365effcba219b9a89e53619e176bdf322042520d5d24e7d9dca04bd63fa6785242f097301c3921d111c43ed888072b0938133435 + languageName: node + linkType: hard + +"@react-types/listbox@npm:^3.7.1": + version: 3.7.1 + resolution: "@react-types/listbox@npm:3.7.1" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/2040a31a6eae1a0d0fe44f2d6410dfd6e9f99849eaba4fea4b4819ed162f2642d5e023d0badb4f78b2e0d60c4480cacc2d0745f9881f77b83c20b7c2012adf0b + languageName: node + linkType: hard + +"@react-types/menu@npm:^3.10.2": + version: 3.10.2 + resolution: "@react-types/menu@npm:3.10.2" + dependencies: + "@react-types/overlays": "npm:^3.8.16" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/9b6cc0818f1d63225f7794ca89d0fa25cd771bad4440fa52c403d81d7027b1f83ba600603ede4ee05dee01b57d1240c9eaa7ed890150aa9dff0c8113dc0c85ab + languageName: node + linkType: hard + +"@react-types/meter@npm:^3.4.10": + version: 3.4.10 + resolution: "@react-types/meter@npm:3.4.10" + dependencies: + "@react-types/progress": "npm:^3.5.13" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/b55f4ca464fc2873662ee50d25cdc1c77995d34d39b6d2831c0b121e47c7b12051017576f51909881a3776003e03263ce899dcf572a9f083794b160320baa754 + languageName: node + linkType: hard + +"@react-types/numberfield@npm:^3.8.12": + version: 3.8.12 + resolution: "@react-types/numberfield@npm:3.8.12" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/98989cdcd7b7bef45129c54e9bdb919d06f09109c786f0783105c617304ee3ce15c9aad01507d4d9aa0e974ee3b50b230c057ad7a48f634247e374c9668080ae + languageName: node + linkType: hard + +"@react-types/overlays@npm:^3.8.16": + version: 3.8.16 + resolution: "@react-types/overlays@npm:3.8.16" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/12bedf8e79d31827cd9868dcb115d07b9fee7954af5e00c1a46fe87b9f2b08e971af4b1b238faa2ce7ed173600786a83c7b9cfcdee5c7464a42bc581d7dee3a6 + languageName: node + linkType: hard + +"@react-types/progress@npm:^3.5.13": + version: 3.5.13 + resolution: "@react-types/progress@npm:3.5.13" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/f666892cff510323e90b1e72e5351eef4f38eb752e568f46cb30250a7de8b9a4435f3506fc590d748d3a0ce30a5f64d449a88a8656369137d06a584ef667d0a8 + languageName: node + linkType: hard + +"@react-types/radio@npm:^3.8.10": + version: 3.8.10 + resolution: "@react-types/radio@npm:3.8.10" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/ba87e61b7a84547c417baa9553bdfebf2fcd03d45038f0d6a0031a5d4e6c94f9d9a81fe8a98579c6830dcffad68c71560ccac8fd842d5f2469a05ce275d92250 + languageName: node + linkType: hard + +"@react-types/searchfield@npm:^3.6.3": + version: 3.6.3 + resolution: "@react-types/searchfield@npm:3.6.3" + dependencies: + "@react-types/shared": "npm:^3.30.0" + "@react-types/textfield": "npm:^3.12.3" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/ffe5a909fb94a28c674c1ae798e511d93be5175c3fc8adcadc88e040c6e5aca4cf55565b321219311537c11c5ce70c005aa214d8dc708a4b1583a1f1a68a75f6 + languageName: node + linkType: hard + +"@react-types/select@npm:^3.9.13": + version: 3.9.13 + resolution: "@react-types/select@npm:3.9.13" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/9fba560d2060eb852289be76f0e0b691bddd48f744f969862da10d38c8963f6931190cb53cc8c5ea01eab3f447b8a680e6da378fe6287ec80d3f48a62d312759 + languageName: node + linkType: hard + +"@react-types/shared@npm:^3.30.0": + version: 3.30.0 + resolution: "@react-types/shared@npm:3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/58af4134275a347bf18ac5d15e3dcfd07dcf623e1d494f140303e06f2149b7aad86408979e163393346df613f6390a3a387f77a2937f0bf1908c6672a44f7b69 + languageName: node + linkType: hard + +"@react-types/slider@npm:^3.7.12": + version: 3.7.12 + resolution: "@react-types/slider@npm:3.7.12" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/38b8a556f87bb64c7f47af482a1a3ce688345a3d4b7a2c71e99522bdcb9668f8ed8e3ea0797b32d5ac1a95d6557c5f9e8eadab51e8eb780f9e66efa89ba50fa0 + languageName: node + linkType: hard + +"@react-types/switch@npm:^3.5.12": + version: 3.5.12 + resolution: "@react-types/switch@npm:3.5.12" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6552f113b0c3abf52f590b8cedf17f5901e7fa2d0ed94b30408fd2fbda907ee17e7792af6d86985ca742a3cb5b667bcbf6cec725e66265ccdfd201b77e7f524b + languageName: node + linkType: hard + +"@react-types/table@npm:^3.13.1": + version: 3.13.1 + resolution: "@react-types/table@npm:3.13.1" + dependencies: + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/be1bac716d13148b47016f2156ee0769c044d301b538df934f6b8aa5466265a750391e462f9fcdebff7dbb93cf9e373e2117f91eab528b3f5cef062174e547ee + languageName: node + linkType: hard + +"@react-types/tabs@npm:^3.3.16": + version: 3.3.16 + resolution: "@react-types/tabs@npm:3.3.16" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/fae8d98776408b0e9776d0217a545a1ed6035ac976321e413e4f442b525375c56854a328c33e77564efe525e66bc7b79dc89e38ff02373788b996c4b46bb6c5d + languageName: node + linkType: hard + +"@react-types/textfield@npm:^3.12.3": + version: 3.12.3 + resolution: "@react-types/textfield@npm:3.12.3" + dependencies: + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/d8bf58fd569b30fa13f98de834a025c9ef71d6de8c669912aa7083c83f88f7261852e0b1b30953e903d377b5b63cc849b848fd549a1466c82d6a74cb6c540cf5 + languageName: node + linkType: hard + +"@react-types/tooltip@npm:^3.4.18": + version: 3.4.18 + resolution: "@react-types/tooltip@npm:3.4.18" + dependencies: + "@react-types/overlays": "npm:^3.8.16" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/5eed69c83727496a6d06796617da86740ab2629b3bd6a106c6ddfb384aaa74725aed3c3eed44f329dcebe7cf87a261ce33e8aa8a5899549446321c1a86d27b87 + languageName: node + linkType: hard + +"@redis/client@npm:^1.6.0": + version: 1.6.1 + resolution: "@redis/client@npm:1.6.1" dependencies: cluster-key-slot: "npm:1.1.2" generic-pool: "npm:3.9.0" yallist: "npm:4.0.0" - checksum: 10/ad375bd685dc34163304872e611ad31dc09a20f4dc8416e5dd588e8a9d2a42a6882f5c8f83d6388ba04ada71513c461b211c3146bcd8ab4da20b3f7edf08fb63 - languageName: node - linkType: hard - -"@redis/graph@npm:1.1.1": - version: 1.1.1 - resolution: "@redis/graph@npm:1.1.1" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 10/96b8ee9bec124947465848b56a014805f9639e09704e03c75a92072a319599ac9dcd4f9ace22970a7f72131a241166ad31db4dc6931b34808d22a5ca94649ba5 - languageName: node - linkType: hard - -"@redis/json@npm:1.0.7": - version: 1.0.7 - resolution: "@redis/json@npm:1.0.7" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 10/df0ac5035608d2e82289237d1adbf541af3743a86692df9958a2c89281bce024eeecfc031db51774d8a46639c5ec34ce9f8b71ebec7bce21865920e36ca3db57 - languageName: node - linkType: hard - -"@redis/search@npm:1.2.0": - version: 1.2.0 - resolution: "@redis/search@npm:1.2.0" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 10/25bba222c0fb1ec1f2db08fe157d30d56a8ffe234214c72d3a7a991daefe77f18c5e6440ab3aa297aef88bafd5448b04ac9fcd84671f2dadc8989712c06b63b4 - languageName: node - linkType: hard - -"@redis/time-series@npm:1.1.0": - version: 1.1.0 - resolution: "@redis/time-series@npm:1.1.0" - peerDependencies: - "@redis/client": ^1.0.0 - checksum: 10/e1d000eef7f37645f7f4ff94f32dd998384360b12fed1e4d614d828c065b72d7627e27444fab781fac4fdfe301c45f893417550fb1011d3b75237566ff0954e0 + checksum: 10/3ef20235b9b0ecba728bbc7208eabbdfc2eebb50c6fb95b20486a0c14e9d6f3ce620ac0d3d14d7f682ea7cb953b13bf89bd94932b7ab3babeb12ba77136b4291 languageName: node linkType: hard @@ -16320,82 +18262,82 @@ __metadata: languageName: node linkType: hard -"@rspack/binding-darwin-arm64@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-darwin-arm64@npm:1.3.10" +"@rspack/binding-darwin-arm64@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-darwin-arm64@npm:1.3.15" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-darwin-x64@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-darwin-x64@npm:1.3.10" +"@rspack/binding-darwin-x64@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-darwin-x64@npm:1.3.15" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rspack/binding-linux-arm64-gnu@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-linux-arm64-gnu@npm:1.3.10" +"@rspack/binding-linux-arm64-gnu@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-linux-arm64-gnu@npm:1.3.15" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-arm64-musl@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-linux-arm64-musl@npm:1.3.10" +"@rspack/binding-linux-arm64-musl@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-linux-arm64-musl@npm:1.3.15" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rspack/binding-linux-x64-gnu@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-linux-x64-gnu@npm:1.3.10" +"@rspack/binding-linux-x64-gnu@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-linux-x64-gnu@npm:1.3.15" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rspack/binding-linux-x64-musl@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-linux-x64-musl@npm:1.3.10" +"@rspack/binding-linux-x64-musl@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-linux-x64-musl@npm:1.3.15" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rspack/binding-win32-arm64-msvc@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-win32-arm64-msvc@npm:1.3.10" +"@rspack/binding-win32-arm64-msvc@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-win32-arm64-msvc@npm:1.3.15" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rspack/binding-win32-ia32-msvc@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-win32-ia32-msvc@npm:1.3.10" +"@rspack/binding-win32-ia32-msvc@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-win32-ia32-msvc@npm:1.3.15" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@rspack/binding-win32-x64-msvc@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding-win32-x64-msvc@npm:1.3.10" +"@rspack/binding-win32-x64-msvc@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding-win32-x64-msvc@npm:1.3.15" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rspack/binding@npm:1.3.10": - version: 1.3.10 - resolution: "@rspack/binding@npm:1.3.10" +"@rspack/binding@npm:1.3.15": + version: 1.3.15 + resolution: "@rspack/binding@npm:1.3.15" dependencies: - "@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" + "@rspack/binding-darwin-arm64": "npm:1.3.15" + "@rspack/binding-darwin-x64": "npm:1.3.15" + "@rspack/binding-linux-arm64-gnu": "npm:1.3.15" + "@rspack/binding-linux-arm64-musl": "npm:1.3.15" + "@rspack/binding-linux-x64-gnu": "npm:1.3.15" + "@rspack/binding-linux-x64-musl": "npm:1.3.15" + "@rspack/binding-win32-arm64-msvc": "npm:1.3.15" + "@rspack/binding-win32-ia32-msvc": "npm:1.3.15" + "@rspack/binding-win32-x64-msvc": "npm:1.3.15" dependenciesMeta: "@rspack/binding-darwin-arm64": optional: true @@ -16415,42 +18357,38 @@ __metadata: optional: true "@rspack/binding-win32-x64-msvc": optional: true - checksum: 10/10328e405c6708f7d1eea7e8f83d7f4453bcc3e4ca77b99eb29819d9f66c51e85b8cf46daab797bea59fdafc266dab6091deb9b5e2ec3007482da11ab10dc62c + checksum: 10/d4918ee41eecbc6d27739d21b674fe64ea7d45433e0f038618475a7cfd91a68d470d6e05d68e109ae917b88c541059c368b7734b2dca3791d7be001a5804030e languageName: node linkType: hard "@rspack/core@npm:^1.3.9": - version: 1.3.10 - resolution: "@rspack/core@npm:1.3.10" + version: 1.3.15 + resolution: "@rspack/core@npm:1.3.15" dependencies: - "@module-federation/runtime-tools": "npm:0.13.1" - "@rspack/binding": "npm:1.3.10" + "@module-federation/runtime-tools": "npm:0.14.3" + "@rspack/binding": "npm:1.3.15" "@rspack/lite-tapable": "npm:1.0.1" - caniuse-lite: "npm:^1.0.30001717" peerDependencies: "@swc/helpers": ">=0.5.1" peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10/5471ced4f461936c723199006182ce9fc54a5840aa7ba46f56f137d46b1839b994a9f6e4539e1376eaaf2956cbbbe5a7846d06ab61246e38711a805f7dd56df4 + checksum: 10/9708d4b5112f9a1c23f691e544e9b690cdc7275145772932e6ea0683b6c71d3f304c0f21513b23a01f00cebe8607adc54c23e3ed4ac9dd566e8d9d02b35f5f4e languageName: node linkType: hard "@rspack/dev-server@npm:^1.1.1": - version: 1.1.1 - resolution: "@rspack/dev-server@npm:1.1.1" + version: 1.1.3 + resolution: "@rspack/dev-server@npm:1.1.3" dependencies: chokidar: "npm:^3.6.0" - express: "npm:^4.21.2" - http-proxy-middleware: "npm:^2.0.7" - mime-types: "npm:^2.1.35" + http-proxy-middleware: "npm:^2.0.9" p-retry: "npm:^6.2.0" - webpack-dev-middleware: "npm:^7.4.2" - webpack-dev-server: "npm:5.2.0" + webpack-dev-server: "npm:5.2.2" ws: "npm:^8.18.0" peerDependencies: "@rspack/core": "*" - checksum: 10/a712c1ed6819da26984d106e7fdede3ed720ef177bf8fa976e91eb95988b862b306c354829486dce8eabde420cb5a96cb25ecf397c9532f87f3b913e9a9da40c + checksum: 10/31cef80a602acf9468a94c31b1f09239cae9c47cf0ed25c6bcddd057bbaff5a220a1dead068a255d35d0addfea21d81a574069d63ea258c2807d84a02d4fe966 languageName: node linkType: hard @@ -16670,41 +18608,41 @@ __metadata: languageName: node linkType: hard -"@shikijs/engine-oniguruma@npm:^3.4.0": - version: 3.4.0 - resolution: "@shikijs/engine-oniguruma@npm:3.4.0" +"@shikijs/engine-oniguruma@npm:^3.7.0": + version: 3.7.0 + resolution: "@shikijs/engine-oniguruma@npm:3.7.0" dependencies: - "@shikijs/types": "npm:3.4.0" + "@shikijs/types": "npm:3.7.0" "@shikijs/vscode-textmate": "npm:^10.0.2" - checksum: 10/3f6c33a1f49e6a8235b79029e3e95f0bc95398f299e53b96a23942ceb4648be51071f48b8632e2f244b2b2477d152035fd18bc4b7e34abe885d84e94698f7e94 + checksum: 10/dee77bddb90efd2b164d46ed2b88793503d00cd2fb484b869cf7c78531b75fd024bbbd2d7b8be2eb3f063179d79aa441341478411403415156ad76672d751547 languageName: node linkType: hard -"@shikijs/langs@npm:^3.4.0": - version: 3.4.0 - resolution: "@shikijs/langs@npm:3.4.0" +"@shikijs/langs@npm:^3.7.0": + version: 3.7.0 + resolution: "@shikijs/langs@npm:3.7.0" dependencies: - "@shikijs/types": "npm:3.4.0" - checksum: 10/355065accab578a8a017ee238f13ee0b6adf007b514f78d7ea126f07ee89cd682f1f377f49d923688c6787b474b5b3a640aa3b083ac11ea11ea4edd7e8c50ac7 + "@shikijs/types": "npm:3.7.0" + checksum: 10/11bc671751c6cd82ae83575bdf84ecf51f54b13b3c8e3b51ff299a8c1288b8eac0fa2c99c6905753a4790b7af9160c6ed5a15be4c54851be3d339540a8d292ee languageName: node linkType: hard -"@shikijs/themes@npm:^3.4.0": - version: 3.4.0 - resolution: "@shikijs/themes@npm:3.4.0" +"@shikijs/themes@npm:^3.7.0": + version: 3.7.0 + resolution: "@shikijs/themes@npm:3.7.0" dependencies: - "@shikijs/types": "npm:3.4.0" - checksum: 10/74e5911548d77a63375125575db77dd62b44c40afd39f168bc3fc4d7fefb4d2922f49cf76d21139d850a02a5e207659a247a417be27b438d3d5ea80abe450e52 + "@shikijs/types": "npm:3.7.0" + checksum: 10/9fb5085bc9121124577823a28f9b2d1335875296232856e37c7cf9a9672958dff93b6d821896f6dbcbd44953043254acf28407c6e4cfba62d5fa8bf38142fd08 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" +"@shikijs/types@npm:3.7.0, @shikijs/types@npm:^3.7.0": + version: 3.7.0 + resolution: "@shikijs/types@npm:3.7.0" dependencies: "@shikijs/vscode-textmate": "npm:^10.0.2" "@types/hast": "npm:^3.0.4" - checksum: 10/bca759ac0972f9ab673b199d44fb4655eb75a1af68b024f05126ff6ef0ce1fd7d316ca16da8eb7c383de8b058f358f90e6172be3783a1f04a3871f518fb0fd72 + checksum: 10/f6f4c6166968a24620b390bc4dc7d2238f8cbcb209d0b55583dafe0fea2de742479795b2bdede2b3f070159550b2e36519d3933ac9f23ca02e036c87fed74db4 languageName: node linkType: hard @@ -16978,14 +18916,14 @@ __metadata: linkType: hard "@slack/web-api@npm:^7.5.0": - version: 7.8.0 - resolution: "@slack/web-api@npm:7.8.0" + version: 7.9.3 + resolution: "@slack/web-api@npm:7.9.3" dependencies: "@slack/logger": "npm:^4.0.0" "@slack/types": "npm:^2.9.0" "@types/node": "npm:>=18.0.0" "@types/retry": "npm:0.12.0" - axios: "npm:^1.7.8" + axios: "npm:^1.8.3" eventemitter3: "npm:^5.0.1" form-data: "npm:^4.0.0" is-electron: "npm:2.2.2" @@ -16993,7 +18931,7 @@ __metadata: p-queue: "npm:^6" p-retry: "npm:^4" retry: "npm:^0.13.1" - checksum: 10/f2a698f853d0aaab11a23e9ce659e2cf3e43792eeb6861ef05258bbd2bb5cfe3bad8fc7bff4ee6fe471e67ec9fb6b22ef16be4478ab68f139df730337ebe15bb + checksum: 10/dc8aa95c8127a1155f0c0756bc819185da0b471feb5953dc118301e577e67f45ad4ef64d9459ff430b44f769344a6347d1636ad59267483e9ac0104611263a05 languageName: node linkType: hard @@ -17800,8 +19738,8 @@ __metadata: linkType: hard "@stoplight/spectral-core@npm:^1.18.0, @stoplight/spectral-core@npm:^1.18.3, @stoplight/spectral-core@npm:^1.19.2, @stoplight/spectral-core@npm:^1.19.4, @stoplight/spectral-core@npm:^1.8.1": - version: 1.19.5 - resolution: "@stoplight/spectral-core@npm:1.19.5" + version: 1.20.0 + resolution: "@stoplight/spectral-core@npm:1.20.0" dependencies: "@stoplight/better-ajv-errors": "npm:1.0.3" "@stoplight/json": "npm:~3.21.0" @@ -17824,7 +19762,7 @@ __metadata: pony-cause: "npm:^1.1.1" simple-eval: "npm:1.0.1" tslib: "npm:^2.8.1" - checksum: 10/db0f6c1c0ca67bc4a49c480259d261283ccb72958cd3a2a761dccbbbb1fdd04911df9c0a05f7ce4453e303de737206fbf3e799a1ace5db49d433a2848abe13b2 + checksum: 10/63628dcf4b1556db166a020dd78d17033908fa7d1c67c62739e35848e5d0e095714524b4c142aa7d6524fe556152f4dbb8cb388b36bc6e37026bf07c31a0e69b languageName: node linkType: hard @@ -17841,8 +19779,8 @@ __metadata: linkType: hard "@stoplight/spectral-formatters@npm:^1.1.0": - version: 1.4.3 - resolution: "@stoplight/spectral-formatters@npm:1.4.3" + version: 1.5.0 + resolution: "@stoplight/spectral-formatters@npm:1.5.0" dependencies: "@stoplight/path": "npm:^1.3.2" "@stoplight/spectral-core": "npm:^1.19.4" @@ -17857,13 +19795,13 @@ __metadata: strip-ansi: "npm:6.0" text-table: "npm:^0.2.0" tslib: "npm:^2.8.1" - checksum: 10/38cdb6dd756a5b859f0768bd7a6730d9ff816a8e21880385e75d0b663a98a2ca62283b6ab50bcce92384e0117d354813591faec9d5da85a3d6ed491afd5b1830 + checksum: 10/565f939d4363d6de357cb6292fce3f656ea106097b675562a958a2fb0b351f2ebc8516602ebdad83f57a73d6da178ce3e18e8a484b565d5c46e24aa588e7f04b languageName: node linkType: hard "@stoplight/spectral-functions@npm:^1.6.1, @stoplight/spectral-functions@npm:^1.7.2, @stoplight/spectral-functions@npm:^1.9.1": - version: 1.9.4 - resolution: "@stoplight/spectral-functions@npm:1.9.4" + version: 1.10.1 + resolution: "@stoplight/spectral-functions@npm:1.10.1" dependencies: "@stoplight/better-ajv-errors": "npm:1.0.3" "@stoplight/json": "npm:^3.17.1" @@ -17876,7 +19814,7 @@ __metadata: ajv-formats: "npm:~2.1.1" lodash: "npm:~4.17.21" tslib: "npm:^2.8.1" - checksum: 10/8da84358ccfcf60afc2f61db3296eef5718e9f837cfaec99a006faf42f5e1f7a6f47fbbe02161e39ffe6a5fe9dc5cacf1270ff59b22a8ba6264d6381d6a94d65 + checksum: 10/dceb3aa64ef5342183a1175ccf6d1a81e6c44dd0dd128a2b0b00f7d8b0eb467a47faac96b2a3a42554e99116af0e5a1d8be3d51ae82c3a96fc683d53fb0127ca languageName: node linkType: hard @@ -17906,8 +19844,8 @@ __metadata: linkType: hard "@stoplight/spectral-rulesets@npm:^1.14.1, @stoplight/spectral-rulesets@npm:^1.18.0": - version: 1.21.4 - resolution: "@stoplight/spectral-rulesets@npm:1.21.4" + version: 1.22.0 + resolution: "@stoplight/spectral-rulesets@npm:1.22.0" dependencies: "@asyncapi/specs": "npm:^6.8.0" "@stoplight/better-ajv-errors": "npm:1.0.3" @@ -17924,7 +19862,7 @@ __metadata: leven: "npm:3.1.0" lodash: "npm:~4.17.21" tslib: "npm:^2.8.1" - checksum: 10/f5154e70285e3e7caac24a33fec0904d342923402f4bc2fb0a4d184d1e9f8a70d555d7ed9118dda004b8fd5ad51357e57abc68b657315934a03beb0e55d842d5 + checksum: 10/1e93a302436b62532324dadb5c0cd1f89606533a8fefb1437288b6dec6c28c350d41723718b978c0c4c806cffe9019a2b8b0df5f71f78349a23f98a2f847c036 languageName: node linkType: hard @@ -18131,17 +20069,6 @@ __metadata: languageName: node linkType: hard -"@storybook/addon-themes@npm:^8.6.12": - version: 8.6.12 - resolution: "@storybook/addon-themes@npm:8.6.12" - dependencies: - ts-dedent: "npm:^2.0.0" - peerDependencies: - storybook: ^8.6.12 - checksum: 10/2df945accd7c0546785f71e2adc4ed68d71a057bc54aaef53f16ce8fcbc5a494380d60f9dd58520fe7d93b021a1b09e3d73553766d4bf2a1d07a3d731132b939 - languageName: node - linkType: hard - "@storybook/addon-toolbars@npm:8.6.12": version: 8.6.12 resolution: "@storybook/addon-toolbars@npm:8.6.12" @@ -19484,8 +21411,8 @@ __metadata: linkType: hard "@testing-library/react@npm:^16.0.0": - version: 16.1.0 - resolution: "@testing-library/react@npm:16.1.0" + version: 16.3.0 + resolution: "@testing-library/react@npm:16.3.0" dependencies: "@babel/runtime": "npm:^7.12.5" peerDependencies: @@ -19499,7 +21426,7 @@ __metadata: optional: true "@types/react-dom": optional: true - checksum: 10/2a20e0dbfadbc93d45a84e82281ed47deed54a6a5fc1461a523172d7fbc0481e8502cf98a2080f38aba94290b3d745671a1c9e320e6f76ad6afcca67c580b963 + checksum: 10/0ee9e31dd0d2396a924682d0e61a4ecc6bfab8eaff23dbf8a72c3c2ce22c116fa578148baeb4de75b968ef99d22e6e6aa0a00dba40286f71184918bb6bb5b06a languageName: node linkType: hard @@ -19521,7 +21448,7 @@ __metadata: languageName: node linkType: hard -"@tokenizer/inflate@npm:^0.2.6": +"@tokenizer/inflate@npm:^0.2.7": version: 0.2.7 resolution: "@tokenizer/inflate@npm:0.2.7" dependencies: @@ -19595,6 +21522,41 @@ __metadata: languageName: node linkType: hard +"@ts-graphviz/adapter@npm:^2.0.6": + version: 2.0.6 + resolution: "@ts-graphviz/adapter@npm:2.0.6" + dependencies: + "@ts-graphviz/common": "npm:^2.1.5" + checksum: 10/6f15c14a166126e29dddb13f2ad012d8ac4d883e6fa789263b05ee50e0b47f2c09cf4c7f09b549e711563c30d6a6c2e45b52df0fd2701db2953dff112c0e9ea6 + languageName: node + linkType: hard + +"@ts-graphviz/ast@npm:^2.0.7": + version: 2.0.7 + resolution: "@ts-graphviz/ast@npm:2.0.7" + dependencies: + "@ts-graphviz/common": "npm:^2.1.5" + checksum: 10/ca2e0054cb97106e5993d642b8277b60b29a1d227ff75e576d56704a8364790d3fb535788243ac2ec7bc46f077cd760da61fa4a76ddfda5877d40d77b857c3c5 + languageName: node + linkType: hard + +"@ts-graphviz/common@npm:^2.1.5": + version: 2.1.5 + resolution: "@ts-graphviz/common@npm:2.1.5" + checksum: 10/421ea1a0d32917d6f26bceaf7b8e99db9f405afd3bbbb83bb9d2c759ee4bdcf15d5e611b3590becf55615f0ff35a9766fb19268c8c01c3dee0b02a74d0ef1a27 + languageName: node + linkType: hard + +"@ts-graphviz/core@npm:^2.0.7": + version: 2.0.7 + resolution: "@ts-graphviz/core@npm:2.0.7" + dependencies: + "@ts-graphviz/ast": "npm:^2.0.7" + "@ts-graphviz/common": "npm:^2.1.5" + checksum: 10/a2884db12a822ba0fa22845d382459332add43b8297c78d6e7223d89403aacf83508133a00bbf2122c6097b52351bfc5bcf856539379c6d35c9f1439d39cb89b + languageName: node + linkType: hard + "@ts-morph/common@npm:~0.25.0": version: 0.25.0 resolution: "@ts-morph/common@npm:0.25.0" @@ -19683,10 +21645,10 @@ __metadata: languageName: node linkType: hard -"@types/aws-lambda@npm:8.10.143, @types/aws-lambda@npm:^8.10.83": - version: 8.10.143 - resolution: "@types/aws-lambda@npm:8.10.143" - checksum: 10/326a6af5f26e6abab5ec0479829998714280656ea4f9e8623d1f18b159db7993116acbe2e5b0351e8e34601ff283c79b2e8c7c8c0c874f43d2649d9b00c50942 +"@types/aws-lambda@npm:8.10.150, @types/aws-lambda@npm:^8.10.83": + version: 8.10.150 + resolution: "@types/aws-lambda@npm:8.10.150" + checksum: 10/d837ae0b94fb16c7cfec3711126f0e7fb109fc3e6d14d994d083d2039c4f2bc52a75f256ee31a8010f023ce78ddcf3746ad43d552592a0f56720167f6d3111be languageName: node linkType: hard @@ -19775,12 +21737,12 @@ __metadata: languageName: node linkType: hard -"@types/bunyan@npm:1.8.9": - version: 1.8.9 - resolution: "@types/bunyan@npm:1.8.9" +"@types/bunyan@npm:1.8.11": + version: 1.8.11 + resolution: "@types/bunyan@npm:1.8.11" dependencies: "@types/node": "npm:*" - checksum: 10/5d6e818cc324eee60ad4658fc169bad2a7ec794de383de5333327951c7be74804cf70230ac4a93b4e6ce5d86106e81e6fcc8c617454617bad6a721011b619435 + checksum: 10/8906ec6c0573f563139681344c2a1d220c394e4aa5b88ff99b011894734f74aafc9d0b3a71758dda3b0ee30e9b6370a8aa48ac26b5585f3c8d7e0fe8db6ca844 languageName: node linkType: hard @@ -19883,28 +21845,28 @@ __metadata: languageName: node linkType: hard -"@types/connect@npm:*, @types/connect@npm:3.4.36": - version: 3.4.36 - resolution: "@types/connect@npm:3.4.36" +"@types/connect@npm:*, @types/connect@npm:3.4.38": + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" dependencies: "@types/node": "npm:*" - checksum: 10/4dee3d966fb527b98f0cbbdcf6977c9193fc3204ed539b7522fe5e64dfa45f9017bdda4ffb1f760062262fce7701a0ee1c2f6ce2e50af36c74d4e37052303172 + checksum: 10/7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 languageName: node linkType: hard "@types/content-type@npm:^1.1.8": - version: 1.1.8 - resolution: "@types/content-type@npm:1.1.8" - checksum: 10/2dd15e51925db7208b0d989c3a93d805a0e5e0942aa9edd70a1c3520896b772526d8280e344a674ae68a96a24aa8fce290843a07512460176f36a3020d99c792 + version: 1.1.9 + resolution: "@types/content-type@npm:1.1.9" + checksum: 10/ac2d419877661f25c6b2c174f42e142ee542cae7778af0a519d4787545ea190d2ff91a40acf68e5e8c52d22919be9ff0345e38a393866546ae9d45b8a79e94f6 languageName: node linkType: hard "@types/cookie-parser@npm:^1.4.2": - version: 1.4.8 - resolution: "@types/cookie-parser@npm:1.4.8" + version: 1.4.9 + resolution: "@types/cookie-parser@npm:1.4.9" peerDependencies: "@types/express": "*" - checksum: 10/b26ace5560adce1a746c059a75f18227ee3e3614a49c70dd29b8af204696079ced22eaa9d6cb084e3433ec433bff845cb47ffb890ae9af96a4aab2ff85e40da7 + checksum: 10/6192a4899b5412a4c3be0f47158321aef73a4cd7e7a4f7b2a37e2e1045f11a21209681cb1bc5335f250ee2a6ce64d8a3fefb851181a98e6415d3716ef9ed1f62 languageName: node linkType: hard @@ -19937,11 +21899,11 @@ __metadata: linkType: hard "@types/cors@npm:^2.8.6": - version: 2.8.18 - resolution: "@types/cors@npm:2.8.18" + version: 2.8.19 + resolution: "@types/cors@npm:2.8.19" dependencies: "@types/node": "npm:*" - checksum: 10/6e49b741345e67834cd19d766228509e4b37d6d5c272355bb059502b4787f5adf58776d9114ac5f0f407966e0347ae8d1f995d7ea41e6a24f716d36b3010401b + checksum: 10/9545cc532c9218754443f48a0c98c1a9ba4af1fe54a3425c95de75ff3158147bb39e666cb7c6bf98cc56a9c6dc7b4ce5b2cbdae6b55d5942e50c81b76ed6b825 languageName: node linkType: hard @@ -20093,9 +22055,9 @@ __metadata: linkType: hard "@types/estree@npm:*, @types/estree@npm:^1.0.0, @types/estree@npm:^1.0.5, @types/estree@npm:^1.0.6": - version: 1.0.7 - resolution: "@types/estree@npm:1.0.7" - checksum: 10/419c845ece767ad4b21171e6e5b63dabb2eb46b9c0d97361edcd9cabbf6a95fcadb91d89b5fa098d1336fa0b8fceaea82fca97a2ef3971f5c86e53031e157b21 + version: 1.0.8 + resolution: "@types/estree@npm:1.0.8" + checksum: 10/25a4c16a6752538ffde2826c2cc0c6491d90e69cd6187bef4a006dd2c3c45469f049e643d7e516c515f21484dc3d48fd5c870be158a5beb72f5baf3dc43e4099 languageName: node linkType: hard @@ -20120,19 +22082,7 @@ __metadata: languageName: node linkType: hard -"@types/express-serve-static-core@npm:*": - version: 5.0.0 - resolution: "@types/express-serve-static-core@npm:5.0.0" - dependencies: - "@types/node": "npm:*" - "@types/qs": "npm:*" - "@types/range-parser": "npm:*" - "@types/send": "npm:*" - checksum: 10/fc40cdeae61113d8b2335f4b0f9334a7a64388a0931f2e98f8fc9bdadd0b13b501a70da14c256ae4aa140db49bd2eff75a99a683266d561e62540784a61dc489 - languageName: node - linkType: hard - -"@types/express-serve-static-core@npm:^4.17.21, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5": +"@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.21, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5": version: 4.19.6 resolution: "@types/express-serve-static-core@npm:4.19.6" dependencies: @@ -20145,23 +22095,23 @@ __metadata: linkType: hard "@types/express-session@npm:^1.17.2": - version: 1.18.1 - resolution: "@types/express-session@npm:1.18.1" + version: 1.18.2 + resolution: "@types/express-session@npm:1.18.2" dependencies: "@types/express": "npm:*" - checksum: 10/54316f13e789b758f9ca8209c54aff0bbcf50c9ea50a208d4ba6c3f95d21a60ddaa98bcf459bef02641b38e7244bfbc43aa724a8b607bb13c1c1bab4a139351a + checksum: 10/c1711976d089fd6da58c0262e2b21b71ee64d508ee0f9f1b90e7658b712fc59874e03f799757843d2300c1dd127813cd16ce806c208bc4975f0f7ed700f2db94 languageName: node linkType: hard "@types/express@npm:*, @types/express@npm:^4.16.1, @types/express@npm:^4.17.21, @types/express@npm:^4.17.6": - version: 4.17.21 - resolution: "@types/express@npm:4.17.21" + version: 4.17.23 + resolution: "@types/express@npm:4.17.23" dependencies: "@types/body-parser": "npm:*" "@types/express-serve-static-core": "npm:^4.17.33" "@types/qs": "npm:*" "@types/serve-static": "npm:*" - checksum: 10/7a6d26cf6f43d3151caf4fec66ea11c9d23166e4f3102edfe45a94170654a54ea08cf3103d26b3928d7ebcc24162c90488e33986b7e3a5f8941225edd5eb18c7 + checksum: 10/cf4d540bbd90801cdc79a46107b8873404698a7fd0c3e8dd42989d52d3bd7f5b8768672e54c20835e41e27349c319bb47a404ad14c0f8db0e9d055ba1cb8a05b languageName: node linkType: hard @@ -20267,9 +22217,9 @@ __metadata: linkType: hard "@types/http-errors@npm:*, @types/http-errors@npm:^2.0.0": - version: 2.0.4 - resolution: "@types/http-errors@npm:2.0.4" - checksum: 10/1f3d7c3b32c7524811a45690881736b3ef741bf9849ae03d32ad1ab7062608454b150a4e7f1351f83d26a418b2d65af9bdc06198f1c079d75578282884c4e8e3 + version: 2.0.5 + resolution: "@types/http-errors@npm:2.0.5" + checksum: 10/a88da669366bc483e8f3b3eb3d34ada5f8d13eeeef851b1204d77e2ba6fc42aba4566d877cca5c095204a3f4349b87fe397e3e21288837bdd945dd514120755b languageName: node linkType: hard @@ -20495,9 +22445,9 @@ __metadata: linkType: hard "@types/lodash@npm:^4.14.151": - version: 4.17.16 - resolution: "@types/lodash@npm:4.17.16" - checksum: 10/9a8bb7471a7521bd65d528e1bd14f79819a3eeb6f8a35a8a44649a7d773775c0813e93fd93bd32ccf350bb076c0bf02c6d47877c4625f526f6dd4d283c746aec + version: 4.17.18 + resolution: "@types/lodash@npm:4.17.18" + checksum: 10/54ebb15b29925112dbe9da3abd99fb80d7202bc5ba20fc1b4fc8ea835d0012f00cbd9a3e7f367b70e7c3f2d5ee635964e3920a489625647b558f02994b3dd381 languageName: node linkType: hard @@ -20621,12 +22571,12 @@ __metadata: languageName: node linkType: hard -"@types/mysql@npm:2.15.26": - version: 2.15.26 - resolution: "@types/mysql@npm:2.15.26" +"@types/mysql@npm:2.15.27": + version: 2.15.27 + resolution: "@types/mysql@npm:2.15.27" dependencies: "@types/node": "npm:*" - checksum: 10/8f205eeaca8f94e998ce4707354bfd02b6ca0da5b7c22289f8f6ff864d549bfb95ca7ddc2f2ebe69eb8f7e3d1f5d8a5b9a2f98aee13824dbc48051bf53a1664d + checksum: 10/a8c743501036f494bb8b26ee04ce914c9cce88955d9ba48ff77d2b5b1bc403d4d99804dbf02238c1491fa93e2242983b1492ad8c2b39755dabd68683dada9e8f languageName: node linkType: hard @@ -20746,6 +22696,15 @@ __metadata: languageName: node linkType: hard +"@types/oracledb@npm:6.5.2": + version: 6.5.2 + resolution: "@types/oracledb@npm:6.5.2" + dependencies: + "@types/node": "npm:*" + checksum: 10/de4537f1718a03401d2726afb289cb9d3e421645b9024001458e8be626d6cdc01328c2c89f74c345e180506b334057915252221b15b253dcb0a60f337b5715fc + languageName: node + linkType: hard + "@types/parse-json@npm:^4.0.0": version: 4.0.0 resolution: "@types/parse-json@npm:4.0.0" @@ -20830,25 +22789,14 @@ __metadata: languageName: node linkType: hard -"@types/pg@npm:*": - version: 8.11.10 - resolution: "@types/pg@npm:8.11.10" - dependencies: - "@types/node": "npm:*" - pg-protocol: "npm:*" - pg-types: "npm:^4.0.1" - checksum: 10/65b7d7ca9c90b7cb94aa94ad94bb1883356845e1f64688bc824ecd499da25f63a2400f0a58500554637048a7cc8b91055bbfe14301c082ce9669afd0c18e414c - languageName: node - linkType: hard - -"@types/pg@npm:8.6.1": - version: 8.6.1 - resolution: "@types/pg@npm:8.6.1" +"@types/pg@npm:*, @types/pg@npm:8.15.4": + version: 8.15.4 + resolution: "@types/pg@npm:8.15.4" dependencies: "@types/node": "npm:*" pg-protocol: "npm:*" pg-types: "npm:^2.2.0" - checksum: 10/bf1134ea194ad9cb8bfe0aab7a532713c63bae1d95909fa45e8dc1945e44ede74f2d4c5b2cd2f9712c6b970896929e0d82480f9c9da79addf405c089b590e562 + checksum: 10/dd9203ae6732acad4892513fc99eb2bc699935a95b62e9fdbdcc6d1a90f63881b5fd89d4cac1729790ab3d0bb7c64130b144c65abef34e6bbed063ff43a739a6 languageName: node linkType: hard @@ -20881,9 +22829,9 @@ __metadata: linkType: hard "@types/prop-types@npm:*, @types/prop-types@npm:^15.0.0, @types/prop-types@npm:^15.7.12, @types/prop-types@npm:^15.7.3": - version: 15.7.14 - resolution: "@types/prop-types@npm:15.7.14" - checksum: 10/d0c5407b9ccc3dd5fae0ccf9b1007e7622ba5e6f1c18399b4f24dff33619d469da4b9fa918a374f19dc0d9fe6a013362aab0b844b606cfc10676efba3f5f736d + version: 15.7.15 + resolution: "@types/prop-types@npm:15.7.15" + checksum: 10/31aa2f59b28f24da6fb4f1d70807dae2aedfce090ec63eaf9ea01727a9533ef6eaf017de5bff99fbccad7d1c9e644f52c6c2ba30869465dd22b1a7221c29f356 languageName: node linkType: hard @@ -20897,9 +22845,9 @@ __metadata: linkType: hard "@types/qs@npm:*, @types/qs@npm:^6.9.6": - version: 6.9.18 - resolution: "@types/qs@npm:6.9.18" - checksum: 10/152fab96efd819cc82ae67c39f089df415da6deddb48f1680edaaaa4e86a2a597de7b2ff0ad391df66d11a07006a08d52c9405e86b8cb8f3d5ba15881fe56cc7 + version: 6.14.0 + resolution: "@types/qs@npm:6.14.0" + checksum: 10/1909205514d22b3cbc7c2314e2bd8056d5f05dfb21cf4377f0730ee5e338ea19957c41735d5e4806c746176563f50005bbab602d8358432e25d900bdf4970826 languageName: node linkType: hard @@ -21022,12 +22970,12 @@ __metadata: linkType: hard "@types/react@npm:^18.0.0": - version: 18.3.21 - resolution: "@types/react@npm:18.3.21" + version: 18.3.23 + resolution: "@types/react@npm:18.3.23" dependencies: "@types/prop-types": "npm:*" csstype: "npm:^3.0.2" - checksum: 10/89adc2fe391fd63b620db0aaa10b2be89fad338039415fd748e60c962e47bf2b0f1897cb5226f181eda963f37a1c8ac1bf0b3b29a0e35313bacd0355e5d6c736 + checksum: 10/4b965dffe34a1f8aac8e2d7e976f113373f38134f9e37239f7e75d7ac6b3c2e1333a8df21febf1fe7749640f8de5708f7668cdfc70bffebda1cc4d3346724fd5 languageName: node linkType: hard @@ -21561,6 +23509,19 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/project-service@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/project-service@npm:8.35.0" + dependencies: + "@typescript-eslint/tsconfig-utils": "npm:^8.35.0" + "@typescript-eslint/types": "npm:^8.35.0" + debug: "npm:^4.3.4" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/a9419da92231aa27f75078fcffab1d02398b50fdb7d5399775a414ba02570682b4b60cdfafb544a021b0dc2372f029c4195f5ae17c50deb11c25661b2ac18a74 + languageName: node + linkType: hard + "@typescript-eslint/scope-manager@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/scope-manager@npm:5.62.0" @@ -21591,6 +23552,15 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/tsconfig-utils@npm:8.35.0, @typescript-eslint/tsconfig-utils@npm:^8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.35.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/4160928313ccbe8b169a009b9c1220826c7df7aab427f960c31f3b838931bc7a121ebee8040118481e4528e2e3cf1b26da047c6ac1d802ecff2ef7206026ea6b + languageName: node + linkType: hard + "@typescript-eslint/type-utils@npm:8.29.1": version: 8.29.1 resolution: "@typescript-eslint/type-utils@npm:8.29.1" @@ -21627,6 +23597,13 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/types@npm:8.35.0, @typescript-eslint/types@npm:^8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/types@npm:8.35.0" + checksum: 10/34b5e6da2c59ea84cd528608fff0cc14b102fd23f5517dfee4ef38c9372861d80b5bf92445c9679674f0a4f8dc4ded5066c1bca2bc5569c47515f94568984f35 + languageName: node + linkType: hard + "@typescript-eslint/typescript-estree@npm:5.62.0": version: 5.62.0 resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" @@ -21682,6 +23659,26 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/typescript-estree@npm:^8.23.0": + version: 8.35.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.35.0" + dependencies: + "@typescript-eslint/project-service": "npm:8.35.0" + "@typescript-eslint/tsconfig-utils": "npm:8.35.0" + "@typescript-eslint/types": "npm:8.35.0" + "@typescript-eslint/visitor-keys": "npm:8.35.0" + debug: "npm:^4.3.4" + fast-glob: "npm:^3.3.2" + is-glob: "npm:^4.0.3" + minimatch: "npm:^9.0.4" + semver: "npm:^7.6.0" + ts-api-utils: "npm:^2.1.0" + peerDependencies: + typescript: ">=4.8.4 <5.9.0" + checksum: 10/4dff7c5a8853c8f4e30d35565c62d3ad5bf8445309bd465d94e9bca725853012bb9f58896a04207c30e10b6669511caac8c0f080ed781c93a3db81d5808195aa + languageName: node + linkType: hard + "@typescript-eslint/utils@npm:8.29.1, @typescript-eslint/utils@npm:^6.0.0 || ^7.0.0 || ^8.0.0, @typescript-eslint/utils@npm:^8.8.1": version: 8.29.1 resolution: "@typescript-eslint/utils@npm:8.29.1" @@ -21759,6 +23756,16 @@ __metadata: languageName: node linkType: hard +"@typescript-eslint/visitor-keys@npm:8.35.0": + version: 8.35.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.35.0" + dependencies: + "@typescript-eslint/types": "npm:8.35.0" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10/c0acb13aac3a2be5e82844f7d2e86137347efdd04661dbf9fa69ef04a19dd2f1eb2f1eb6bfbfbaada78a46884308d2c0e0b5d0d1a094c84f2dfb670b67ac2b3b + languageName: node + linkType: hard + "@uiw/codemirror-extensions-basic-setup@npm:4.23.7": version: 4.23.7 resolution: "@uiw/codemirror-extensions-basic-setup@npm:4.23.7" @@ -22127,6 +24134,63 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-core@npm:3.5.17": + version: 3.5.17 + resolution: "@vue/compiler-core@npm:3.5.17" + dependencies: + "@babel/parser": "npm:^7.27.5" + "@vue/shared": "npm:3.5.17" + entities: "npm:^4.5.0" + estree-walker: "npm:^2.0.2" + source-map-js: "npm:^1.2.1" + checksum: 10/b7900f0498cc1f0496e003e0e783ae09ad3fde25258ac635372b704533a14105a04d690b97d196ded5f2cf04ced078a59a53533242acfdaa4508432296372f54 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.5.17": + version: 3.5.17 + resolution: "@vue/compiler-dom@npm:3.5.17" + dependencies: + "@vue/compiler-core": "npm:3.5.17" + "@vue/shared": "npm:3.5.17" + checksum: 10/4370ce4c31c578ec864e25eddbeac23b9498036912a82ba26545346dde4cfc378624bda2865db5104fd6a57321de6f527525af77cd5f83f7d1cd67b00bf1c6de + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:^3.5.13": + version: 3.5.17 + resolution: "@vue/compiler-sfc@npm:3.5.17" + dependencies: + "@babel/parser": "npm:^7.27.5" + "@vue/compiler-core": "npm:3.5.17" + "@vue/compiler-dom": "npm:3.5.17" + "@vue/compiler-ssr": "npm:3.5.17" + "@vue/shared": "npm:3.5.17" + estree-walker: "npm:^2.0.2" + magic-string: "npm:^0.30.17" + postcss: "npm:^8.5.6" + source-map-js: "npm:^1.2.1" + checksum: 10/32a0e611615e1a9f553a3744084d15b388a48ec4218d1159ab5bf57897e38779fe408fc4e63ed43c134134e301d6a32a812f4e3f58a9c7e0fddba9d96f86a537 + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.5.17": + version: 3.5.17 + resolution: "@vue/compiler-ssr@npm:3.5.17" + dependencies: + "@vue/compiler-dom": "npm:3.5.17" + "@vue/shared": "npm:3.5.17" + checksum: 10/ed92e187d7fd29350c6a267a0ca8989f626b1b90e46ab2dbf9492a7164c4b32a7fd7fb5001dd43de0200c5f4cd3b3152d5cd4f55e39fe6f9837a30ce944876b1 + languageName: node + linkType: hard + +"@vue/shared@npm:3.5.17": + version: 3.5.17 + resolution: "@vue/shared@npm:3.5.17" + checksum: 10/d33b7435df4af16586679e713a0c15abb8c79a49067f558cbea6645d69f64ee2fd5b669a834abce2a084cb24a0d35a4a20164ba54625fbab70b3cc06b6913f72 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.12.1, @webassemblyjs/ast@npm:^1.12.1": version: 1.12.1 resolution: "@webassemblyjs/ast@npm:1.12.1" @@ -22338,6 +24402,31 @@ __metadata: languageName: node linkType: hard +"@xterm/addon-attach@npm:^0.11.0": + version: 0.11.0 + resolution: "@xterm/addon-attach@npm:0.11.0" + peerDependencies: + "@xterm/xterm": ^5.0.0 + checksum: 10/f11e7ba68b8b1064ce545ecbf2beeaf3ebc94622fa3ca77e0ca0b8e8834964f634ee3897c031376abc7242ddfd305a40b10aa7b3e8056bfa59055967251523b5 + languageName: node + linkType: hard + +"@xterm/addon-fit@npm:^0.10.0": + version: 0.10.0 + resolution: "@xterm/addon-fit@npm:0.10.0" + peerDependencies: + "@xterm/xterm": ^5.0.0 + checksum: 10/8edfad561c0d0316c5883cbe2ce56109f105a2b2bf53b71d5f8c788e656a3205c1093a659dddcf4025a459e4b7ff8e07b6c6a19815c8711deeded560de5f1893 + languageName: node + linkType: hard + +"@xterm/xterm@npm:^5.5.0": + version: 5.5.0 + resolution: "@xterm/xterm@npm:5.5.0" + checksum: 10/d4cdc402de81a83a3e0ef93f38072cb8f54abe4d65865f2e29b92cbc2593f86d052f6b993895c9e5dec97f47548f504e90bcea0aad6845917c09b03f2f3a4629 + languageName: node + linkType: hard + "@xtuc/ieee754@npm:^1.2.0": version: 1.2.0 resolution: "@xtuc/ieee754@npm:1.2.0" @@ -22453,7 +24542,7 @@ __metadata: languageName: node linkType: hard -"@yarnpkg/core@npm:^4.2.1, @yarnpkg/core@npm:^4.4.0": +"@yarnpkg/core@npm:^4.2.1, @yarnpkg/core@npm:^4.4.0, @yarnpkg/core@npm:^4.4.1": version: 4.4.1 resolution: "@yarnpkg/core@npm:4.4.1" dependencies: @@ -23073,13 +25162,13 @@ __metadata: languageName: node linkType: hard -"GendocuPublicApis@https://git.gendocu.com/gendocu/GendocuPublicApis.git#master": - version: 1.0.0 - resolution: "GendocuPublicApis@https://git.gendocu.com/gendocu/GendocuPublicApis.git#commit=6e8d4c1d2342556a2e8e719f19385b266001ebae" +"GendocuPublicApis@npm:gendocu-public-apis@^1.0.0": + version: 1.0.3 + resolution: "gendocu-public-apis@npm:1.0.3" dependencies: "@improbable-eng/grpc-web": "npm:^0.14.0" google-protobuf: "npm:^3.15.8" - checksum: 10/a34386a6137d92f392121305fb899f63ba1631a1b60c80781f6e56e160c1d462a8ce4b3ee93cb67c73c079b53c6de57ba19f514f29dd11c9783cd0d8227c6968 + checksum: 10/261a5cc97b599a7dfb492ac048e6bfce3f3bf64627f2b80ef195d5b6cefdbd4668d2b2ccd10676f01eb4f7862a5d19da567dd6ade9fc39a15268e81862308f7c languageName: node linkType: hard @@ -23137,6 +25226,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-globals@npm:^6.0.0": version: 6.0.0 resolution: "acorn-globals@npm:6.0.0" @@ -23352,7 +25451,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.5.5": +"ajv@npm:^6.12.2, ajv@npm:^6.12.4, ajv@npm:^6.12.5, ajv@npm:^6.12.6, ajv@npm:^6.5.5": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -23547,6 +25646,13 @@ __metadata: languageName: node linkType: hard +"app-module-path@npm:^2.2.0": + version: 2.2.0 + resolution: "app-module-path@npm:2.2.0" + checksum: 10/9ed8c6ce6247a6b5d556039f29b4610869237bbb5b8f3d905b22bd2d314c30efcc0fb70c2626d7461ecc52ec7edec9908f660d0938d2bea5b8cfc6868a28806f + languageName: node + linkType: hard + "app-next-example-plugin@workspace:packages/app-next-example-plugin": version: 0.0.0-use.local resolution: "app-next-example-plugin@workspace:packages/app-next-example-plugin" @@ -23911,6 +26017,13 @@ __metadata: languageName: node linkType: hard +"ast-module-types@npm:^6.0.1": + version: 6.0.1 + resolution: "ast-module-types@npm:6.0.1" + checksum: 10/d3a2e0ae998470c7c3d92e2bb12dfcf2797bca17d2548f60b57ed34638d2278f5f99140df009ab3cd73dbe15885b660fe780fc1c14c249f5c50b15a6e014740c + languageName: node + linkType: hard + "ast-types-flow@npm:^0.0.8": version: 0.0.8 resolution: "ast-types-flow@npm:0.0.8" @@ -24165,18 +26278,7 @@ __metadata: languageName: node linkType: hard -"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/a10f0dd836613924e48cf03dc2eff3fd21b14f764807aedaee4880a70c0f142aaebdb21da7ce27104d4c16ca00d0e452a20a20851f60e385a8d5bad1ae909d46 - languageName: node - linkType: hard - -"axios@npm:^1.0.0, axios@npm:^1.6.0, axios@npm:^1.7.4, axios@npm:^1.7.7, axios@npm:^1.7.8": +"axios@npm:1.9.0": version: 1.9.0 resolution: "axios@npm:1.9.0" dependencies: @@ -24187,6 +26289,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.0.0, axios@npm:^1.6.0, axios@npm:^1.7.4, axios@npm:^1.7.7, axios@npm:^1.8.3": + version: 1.10.0 + resolution: "axios@npm:1.10.0" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10/d43c80316a45611fd395743e15d16ea69a95f2b7f7095f2bb12cb78f9ca0a905194a02e52a3bf4e0db9f85fd1186d6c690410644c10ecd8bb0a468e57c2040e4 + languageName: node + linkType: hard + "axobject-query@npm:^4.1.0": version: 4.1.0 resolution: "axobject-query@npm:4.1.0" @@ -24695,6 +26808,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 + "bonjour-service@npm:^1.2.1": version: 1.2.1 resolution: "bonjour-service@npm:1.2.1" @@ -24750,12 +26880,12 @@ __metadata: linkType: hard "brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" dependencies: balanced-match: "npm:^1.0.0" concat-map: "npm:0.0.1" - checksum: 10/faf34a7bb0c3fcf4b59c7808bc5d2a96a40988addf2e7e09dfbb67a2251800e0d14cd2bfc1aa79174f2f5095c54ff27f46fb1289fe2d77dac755b5eb3434cc07 + checksum: 10/12cb6d6310629e3048cadb003e1aca4d8c9bb5c67c3c321bafdd7e7a50155de081f78ea3e0ed92ecc75a9015e784f301efc8132383132f4f7904ad1ac529c562 languageName: node linkType: hard @@ -25059,7 +27189,7 @@ __metadata: languageName: node linkType: hard -"bytes@npm:3.1.2": +"bytes@npm:3.1.2, bytes@npm:^3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" checksum: 10/a10abf2ba70c784471d6b4f58778c0beeb2b5d405148e66affa91f23a9f13d07603d0a0354667310ae1d6dc141474ffd44e2a074be0f6e2254edb8fc21445388 @@ -25323,7 +27453,7 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669, caniuse-lite@npm:^1.0.30001717": +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001669": version: 1.0.30001717 resolution: "caniuse-lite@npm:1.0.30001717" checksum: 10/e47dfd8707ea305baa177f3d3d531df614f5a9ac6335363fc8f86f0be4caf79f5734f3f68b601fee4edd9d79f1e5ffc0931466bb894bf955ed6b1dd5a1c34b1d @@ -25958,7 +28088,7 @@ __metadata: languageName: node linkType: hard -"color-name@npm:^1.0.0, color-name@npm:~1.1.4": +"color-name@npm:^1.0.0, color-name@npm:^1.1.4, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: 10/b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 @@ -26413,6 +28543,15 @@ __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.4, content-type@npm:^1.0.5, content-type@npm:~1.0.4, content-type@npm:~1.0.5": version: 1.0.5 resolution: "content-type@npm:1.0.5" @@ -26465,6 +28604,13 @@ __metadata: languageName: node linkType: hard +"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.1 resolution: "cookie@npm:0.7.1" @@ -26472,7 +28618,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:^0.7.2": +"cookie@npm:0.7.2, cookie@npm:^0.7.0, cookie@npm:^0.7.1, cookie@npm:^0.7.2": version: 0.7.2 resolution: "cookie@npm:0.7.2" checksum: 10/24b286c556420d4ba4e9bc09120c9d3db7d28ace2bd0f8ccee82422ce42322f73c8312441271e5eefafbead725980e5996cc02766dbb89a90ac7f5636ede608f @@ -26712,10 +28858,10 @@ __metadata: languageName: node linkType: hard -"crelt@npm:^1.0.5": - version: 1.0.5 - resolution: "crelt@npm:1.0.5" - checksum: 10/f692219f8550570b35d26f64a34945a016ec10c2714491d1306b95253e4b6bca609b3c73aee68e1c2035eab6cf38b6c853c4c16f80b26a808e688d25c6821b25 +"crelt@npm:^1.0.5, crelt@npm:^1.0.6": + version: 1.0.6 + resolution: "crelt@npm:1.0.6" + checksum: 10/5ed326ca6bd243b1dba6b943f665b21c2c04be03271824bc48f20dba324b0f8233e221f8c67312526d24af2b1243c023dc05a41bd8bd05d1a479fd2c72fb39c3 languageName: node linkType: hard @@ -26810,7 +28956,7 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5": +"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3, cross-spawn@npm:^7.0.5, cross-spawn@npm:^7.0.6": version: 7.0.6 resolution: "cross-spawn@npm:7.0.6" dependencies: @@ -27675,6 +29821,20 @@ __metadata: languageName: node linkType: hard +"dependency-tree@npm:^11.0.0": + version: 11.2.0 + resolution: "dependency-tree@npm:11.2.0" + dependencies: + commander: "npm:^12.1.0" + filing-cabinet: "npm:^5.0.3" + precinct: "npm:^12.2.0" + typescript: "npm:^5.8.3" + bin: + dependency-tree: bin/cli.js + checksum: 10/0dac9f86f1587ce7c3b0050ce64a7f0f9b86dd5b1c318feadfa1d440a3abb1eaa9cd615fc151c41d17301509461669983fd311368cb7113124ad5df17ba850e7 + languageName: node + linkType: hard + "deprecation@npm:^2.0.0, deprecation@npm:^2.3.1": version: 2.3.1 resolution: "deprecation@npm:2.3.1" @@ -27777,6 +29937,108 @@ __metadata: languageName: node linkType: hard +"detective-amd@npm:^6.0.1": + version: 6.0.1 + resolution: "detective-amd@npm:6.0.1" + dependencies: + ast-module-types: "npm:^6.0.1" + escodegen: "npm:^2.1.0" + get-amd-module-type: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + bin: + detective-amd: bin/cli.js + checksum: 10/e709bd512933f33e1f461045f8ac10295661f4cca7cd226d5d69e6972a6392e4dd3879f6c9411e6f22a12c3fac499205492cbe842cc2423aa116d1887d34d167 + languageName: node + linkType: hard + +"detective-cjs@npm:^6.0.1": + version: 6.0.1 + resolution: "detective-cjs@npm:6.0.1" + dependencies: + ast-module-types: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + checksum: 10/8261af91c98178c2580d8d3bbfc394ccf3eb161c15c88e119dd7ce3a1ff24ccf37b2faee12f6da8efd860396bca89dca4e76530d39a26207d710da93131f5db7 + languageName: node + linkType: hard + +"detective-es6@npm:^5.0.1": + version: 5.0.1 + resolution: "detective-es6@npm:5.0.1" + dependencies: + node-source-walk: "npm:^7.0.1" + checksum: 10/460751524871a11cf7c39e12b4316f745fff68f9e5f5fd85193c3ff651cb34cdd51a34d187a713735eb407b53c706645643ad8fd2a2ca098d551ffffe98935b1 + languageName: node + linkType: hard + +"detective-postcss@npm:^7.0.1": + version: 7.0.1 + resolution: "detective-postcss@npm:7.0.1" + dependencies: + is-url: "npm:^1.2.4" + postcss-values-parser: "npm:^6.0.2" + peerDependencies: + postcss: ^8.4.47 + checksum: 10/5cbe1ecc224beb3aa846477de83959c349764b6b912decc92e4c0e1dc22641b5d97b6cdee54dfa2706c71bed50b363c87072314e22730a0c66f82f4be145221c + languageName: node + linkType: hard + +"detective-sass@npm:^6.0.1": + version: 6.0.1 + resolution: "detective-sass@npm:6.0.1" + dependencies: + gonzales-pe: "npm:^4.3.0" + node-source-walk: "npm:^7.0.1" + checksum: 10/bf941665aa8fbdf02399f227d8a80ca059e22d2a2f8d34e90ca06fe3e2772afd63b75dc267712ac8a7b6dc580a9be91a7f965b790803488544472b36dd904c72 + languageName: node + linkType: hard + +"detective-scss@npm:^5.0.1": + version: 5.0.1 + resolution: "detective-scss@npm:5.0.1" + dependencies: + gonzales-pe: "npm:^4.3.0" + node-source-walk: "npm:^7.0.1" + checksum: 10/f5ebb927fd789f8e069f186439c60e8b9efe1e27c45d755c8dd00fdab4a390e28ed0e896e5ecc4275d1dac0b4afa1b1cb7bedf08bdc2155c2f779b69c882705d + languageName: node + linkType: hard + +"detective-stylus@npm:^5.0.1": + version: 5.0.1 + resolution: "detective-stylus@npm:5.0.1" + checksum: 10/28c4168a82e427ba51b24a4d5ef6ad0fb55183dc57317073a45801ff3e2617a86c3288a4b6cd2c84911371128bd841681d855276215926bc1d176da4ebeb5ae2 + languageName: node + linkType: hard + +"detective-typescript@npm:^14.0.0": + version: 14.0.0 + resolution: "detective-typescript@npm:14.0.0" + dependencies: + "@typescript-eslint/typescript-estree": "npm:^8.23.0" + ast-module-types: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + peerDependencies: + typescript: ^5.4.4 + checksum: 10/8dcf16e6bdc9faf0318553f3a2e1e198782cf21edde264d29efd33d88551a79ff1fe3d2d1ef7b2c00a8a88da0323ead4fddbc7ed92270e425da42c4608bec97b + languageName: node + linkType: hard + +"detective-vue2@npm:^2.2.0": + version: 2.2.0 + resolution: "detective-vue2@npm:2.2.0" + dependencies: + "@dependents/detective-less": "npm:^5.0.1" + "@vue/compiler-sfc": "npm:^3.5.13" + detective-es6: "npm:^5.0.1" + detective-sass: "npm:^6.0.1" + detective-scss: "npm:^5.0.1" + detective-stylus: "npm:^5.0.1" + detective-typescript: "npm:^14.0.0" + peerDependencies: + typescript: ^5.4.4 + checksum: 10/896226e5993e7b87948db0aaca4d8980b015828e797a1e46ae102fa079abcbb9713ef170df65735a69644f2e2bf7eb4ee8c2036727b22a65616e1fb5823039ab + languageName: node + linkType: hard + "dezalgo@npm:^1.0.0, dezalgo@npm:^1.0.4": version: 1.0.4 resolution: "dezalgo@npm:1.0.4" @@ -27896,8 +30158,8 @@ __metadata: linkType: hard "dockerode@npm:^4.0.0": - version: 4.0.6 - resolution: "dockerode@npm:4.0.6" + version: 4.0.7 + resolution: "dockerode@npm:4.0.7" dependencies: "@balena/dockerignore": "npm:^1.0.2" "@grpc/grpc-js": "npm:^1.11.1" @@ -27906,7 +30168,7 @@ __metadata: protobufjs: "npm:^7.3.2" tar-fs: "npm:~2.1.2" uuid: "npm:^10.0.0" - checksum: 10/75bd706f20f01742d22913b72e2a5215a4d9f79772c29079772f84fc41a2b1890a704a1aa3d1d764405367090e93c33197d6add19fde3546bac919d98eebc8e3 + checksum: 10/d7cd174cf4489f41335ec8aaaa7c98c164a624f9a793544aa5280d85254ce276e7797de896042ce47d87aca6f8d2653acc37a0d18807d4ce8ea31892faef40a8 languageName: node linkType: hard @@ -28366,7 +30628,7 @@ __metadata: languageName: node linkType: hard -"encodeurl@npm:~2.0.0": +"encodeurl@npm:^2.0.0, encodeurl@npm:~2.0.0": version: 2.0.0 resolution: "encodeurl@npm:2.0.0" checksum: 10/abf5cd51b78082cf8af7be6785813c33b6df2068ce5191a40ca8b1afe6a86f9230af9a9ce694a5ce4665955e5c1120871826df9c128a642e09c58d592e2807fe @@ -28755,34 +31017,34 @@ __metadata: linkType: hard "esbuild@npm:^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0, esbuild@npm:^0.25.0": - version: 0.25.4 - resolution: "esbuild@npm:0.25.4" + version: 0.25.5 + resolution: "esbuild@npm:0.25.5" dependencies: - "@esbuild/aix-ppc64": "npm:0.25.4" - "@esbuild/android-arm": "npm:0.25.4" - "@esbuild/android-arm64": "npm:0.25.4" - "@esbuild/android-x64": "npm:0.25.4" - "@esbuild/darwin-arm64": "npm:0.25.4" - "@esbuild/darwin-x64": "npm:0.25.4" - "@esbuild/freebsd-arm64": "npm:0.25.4" - "@esbuild/freebsd-x64": "npm:0.25.4" - "@esbuild/linux-arm": "npm:0.25.4" - "@esbuild/linux-arm64": "npm:0.25.4" - "@esbuild/linux-ia32": "npm:0.25.4" - "@esbuild/linux-loong64": "npm:0.25.4" - "@esbuild/linux-mips64el": "npm:0.25.4" - "@esbuild/linux-ppc64": "npm:0.25.4" - "@esbuild/linux-riscv64": "npm:0.25.4" - "@esbuild/linux-s390x": "npm:0.25.4" - "@esbuild/linux-x64": "npm:0.25.4" - "@esbuild/netbsd-arm64": "npm:0.25.4" - "@esbuild/netbsd-x64": "npm:0.25.4" - "@esbuild/openbsd-arm64": "npm:0.25.4" - "@esbuild/openbsd-x64": "npm:0.25.4" - "@esbuild/sunos-x64": "npm:0.25.4" - "@esbuild/win32-arm64": "npm:0.25.4" - "@esbuild/win32-ia32": "npm:0.25.4" - "@esbuild/win32-x64": "npm:0.25.4" + "@esbuild/aix-ppc64": "npm:0.25.5" + "@esbuild/android-arm": "npm:0.25.5" + "@esbuild/android-arm64": "npm:0.25.5" + "@esbuild/android-x64": "npm:0.25.5" + "@esbuild/darwin-arm64": "npm:0.25.5" + "@esbuild/darwin-x64": "npm:0.25.5" + "@esbuild/freebsd-arm64": "npm:0.25.5" + "@esbuild/freebsd-x64": "npm:0.25.5" + "@esbuild/linux-arm": "npm:0.25.5" + "@esbuild/linux-arm64": "npm:0.25.5" + "@esbuild/linux-ia32": "npm:0.25.5" + "@esbuild/linux-loong64": "npm:0.25.5" + "@esbuild/linux-mips64el": "npm:0.25.5" + "@esbuild/linux-ppc64": "npm:0.25.5" + "@esbuild/linux-riscv64": "npm:0.25.5" + "@esbuild/linux-s390x": "npm:0.25.5" + "@esbuild/linux-x64": "npm:0.25.5" + "@esbuild/netbsd-arm64": "npm:0.25.5" + "@esbuild/netbsd-x64": "npm:0.25.5" + "@esbuild/openbsd-arm64": "npm:0.25.5" + "@esbuild/openbsd-x64": "npm:0.25.5" + "@esbuild/sunos-x64": "npm:0.25.5" + "@esbuild/win32-arm64": "npm:0.25.5" + "@esbuild/win32-ia32": "npm:0.25.5" + "@esbuild/win32-x64": "npm:0.25.5" dependenciesMeta: "@esbuild/aix-ppc64": optional: true @@ -28836,7 +31098,7 @@ __metadata: optional: true bin: esbuild: bin/esbuild - checksum: 10/227ffe9b31f0b184a0b0a0210bb9d32b2b115b8c5c9b09f08db2c3928cb470fc55a22dbba3c2894365d3abcc62c2089b85638be96a20691d1234d31990ea01b2 + checksum: 10/0fa4c3b42c6ddf1a008e75a4bb3dcab08ce22ac0b31dd59dc01f7fe8e21380bfaec07a2fe3730a7cf430da5a30142d016714b358666325a4733547afa42be405 languageName: node linkType: hard @@ -29262,10 +31524,10 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.2.0": - version: 4.2.0 - resolution: "eslint-visitor-keys@npm:4.2.0" - checksum: 10/9651b3356b01760e586b4c631c5268c0e1a85236e3292bf754f0472f465bf9a856c0ddc261fceace155334118c0151778effafbab981413dbf9288349343fa25 +"eslint-visitor-keys@npm:^4.2.0, eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10/3ee00fc6a7002d4b0ffd9dc99e13a6a7882c557329e6c25ab254220d71e5c9c4f89dca4695352949ea678eb1f3ba912a18ef8aac0a7fe094196fd92f441bfce2 languageName: node linkType: hard @@ -29445,7 +31707,7 @@ __metadata: languageName: node linkType: hard -"etag@npm:~1.8.1": +"etag@npm:^1.8.1, etag@npm:~1.8.1": version: 1.8.1 resolution: "etag@npm:1.8.1" checksum: 10/571aeb3dbe0f2bbd4e4fadbdb44f325fc75335cd5f6f6b6a091e6a06a9f25ed5392f0863c5442acb0646787446e816f13cbfc6edce5b07658541dff573cab1ff @@ -29513,7 +31775,7 @@ __metadata: languageName: node linkType: hard -"eventsource@npm:^3.0.6": +"eventsource@npm:^3.0.2, eventsource@npm:^3.0.6": version: 3.0.7 resolution: "eventsource@npm:3.0.7" dependencies: @@ -29696,6 +31958,7 @@ __metadata: "@backstage/plugin-events-backend": "workspace:^" "@backstage/plugin-events-backend-module-google-pubsub": "workspace:^" "@backstage/plugin-kubernetes-backend": "workspace:^" + "@backstage/plugin-mcp-actions-backend": "workspace:^" "@backstage/plugin-notifications-backend": "workspace:^" "@backstage/plugin-permission-backend": "workspace:^" "@backstage/plugin-permission-backend-module-allow-all-policy": "workspace:^" @@ -29712,7 +31975,7 @@ __metadata: "@backstage/plugin-search-backend-node": "workspace:^" "@backstage/plugin-signals-backend": "workspace:^" "@backstage/plugin-techdocs-backend": "workspace:^" - "@opentelemetry/auto-instrumentations-node": "npm:^0.54.0" + "@opentelemetry/auto-instrumentations-node": "npm:^0.61.0" "@opentelemetry/exporter-prometheus": "npm:^0.54.0" "@opentelemetry/sdk-node": "npm:^0.54.0" example-app: "link:../app" @@ -29865,6 +32128,15 @@ __metadata: languageName: node linkType: hard +"express-rate-limit@npm:^7.5.0": + version: 7.5.1 + resolution: "express-rate-limit@npm:7.5.1" + peerDependencies: + express: ">= 4.11" + checksum: 10/357c3398450144ab7bbce2841d0bf4f93a0f3fd9d1d5ed9a0ee331b557af969cc790941dc37b47f8d9b5672964aa0e31666f770e1f48b334dc7d1e69f6433040 + languageName: node + linkType: hard + "express-session@npm:^1.17.1, express-session@npm:^1.17.3": version: 1.18.1 resolution: "express-session@npm:1.18.1" @@ -29920,6 +32192,41 @@ __metadata: 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 + "ext@npm:^1.1.2": version: 1.7.0 resolution: "ext@npm:1.7.0" @@ -30233,15 +32540,15 @@ __metadata: languageName: node linkType: hard -"file-type@npm:20.4.1": - version: 20.4.1 - resolution: "file-type@npm:20.4.1" +"file-type@npm:21.0.0": + version: 21.0.0 + resolution: "file-type@npm:21.0.0" dependencies: - "@tokenizer/inflate": "npm:^0.2.6" - strtok3: "npm:^10.2.0" + "@tokenizer/inflate": "npm:^0.2.7" + strtok3: "npm:^10.2.2" token-types: "npm:^6.0.0" uint8array-extras: "npm:^1.4.0" - checksum: 10/efbb81c69c84ea4d83ea86dc1c95a45bc6830d5455deb7666e0dbefde2dbfe2bcee4e5398bfc5a8fc1ac55a80a27a3ec4f2bff4b53d4af20be799db4fae324b6 + checksum: 10/6980e8b0ef870a98b51ab2eac5db94a1884de8476fe49dc02d2f7e0c1d1d7d44d42b6c59e67867ae90f321ddf4edd00fcfda01821591e2fa05385d0e438a9dc1 languageName: node linkType: hard @@ -30286,6 +32593,27 @@ __metadata: languageName: node linkType: hard +"filing-cabinet@npm:^5.0.3": + version: 5.0.3 + resolution: "filing-cabinet@npm:5.0.3" + dependencies: + app-module-path: "npm:^2.2.0" + commander: "npm:^12.1.0" + enhanced-resolve: "npm:^5.18.0" + module-definition: "npm:^6.0.1" + module-lookup-amd: "npm:^9.0.3" + resolve: "npm:^1.22.10" + resolve-dependency-path: "npm:^4.0.1" + sass-lookup: "npm:^6.1.0" + stylus-lookup: "npm:^6.1.0" + tsconfig-paths: "npm:^4.2.0" + typescript: "npm:^5.7.3" + bin: + filing-cabinet: bin/cli.js + checksum: 10/0434ee0aa745c4da8f5e112563ee712a73c0582144ed3a37afad404cf42f700f7770ab7c0d00a9e062f14b5d52f9ab2b04e867f6f4b5c4bbde633242ee5020ac + languageName: node + linkType: hard + "fill-range@npm:^7.1.1": version: 7.1.1 resolution: "fill-range@npm:7.1.1" @@ -30325,6 +32653,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-cache-dir@npm:^2.0.0": version: 2.1.0 resolution: "find-cache-dir@npm:2.1.0" @@ -30539,13 +32881,13 @@ __metadata: languageName: node linkType: hard -"foreground-child@npm:^3.1.0": - version: 3.1.1 - resolution: "foreground-child@npm:3.1.1" +"foreground-child@npm:^3.1.0, foreground-child@npm:^3.3.1": + version: 3.3.1 + resolution: "foreground-child@npm:3.3.1" dependencies: - cross-spawn: "npm:^7.0.0" + cross-spawn: "npm:^7.0.6" signal-exit: "npm:^4.0.1" - checksum: 10/087edd44857d258c4f73ad84cb8df980826569656f2550c341b27adf5335354393eec24ea2fabd43a253233fb27cee177ebe46bd0b7ea129c77e87cb1e9936fb + checksum: 10/427b33f997a98073c0424e5c07169264a62cda806d8d2ded159b5b903fdfc8f0a1457e06b5fc35506497acb3f1e353f025edee796300209ac6231e80edece835 languageName: node linkType: hard @@ -30715,6 +33057,28 @@ __metadata: languageName: node linkType: hard +"framer-motion@npm:^12.20.1": + version: 12.20.1 + resolution: "framer-motion@npm:12.20.1" + dependencies: + motion-dom: "npm:^12.20.1" + motion-utils: "npm:^12.19.0" + tslib: "npm:^2.4.0" + peerDependencies: + "@emotion/is-prop-valid": "*" + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/is-prop-valid": + optional: true + react: + optional: true + react-dom: + optional: true + checksum: 10/ba36e76dbf66c856f0c5d3cfb80973bb0ef95ca9169fc16b22560a7bc55fbedcf63205266dba7beecac5152c4cd140097ff504c5d529beffca86cd85f97bf5e3 + languageName: node + linkType: hard + "framer-motion@npm:^6.5.1": version: 6.5.1 resolution: "framer-motion@npm:6.5.1" @@ -30752,6 +33116,13 @@ __metadata: 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 + "from2@npm:^2.3.0": version: 2.3.0 resolution: "from2@npm:2.3.0" @@ -31026,6 +33397,16 @@ __metadata: languageName: node linkType: hard +"get-amd-module-type@npm:^6.0.1": + version: 6.0.1 + resolution: "get-amd-module-type@npm:6.0.1" + dependencies: + ast-module-types: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + checksum: 10/775e60d53f3c0aa4747c98ae2055957b688ffcd47944bc6d8af8ab7c1ab0b337e3d4a5624f29d815fe91e3da1830b8f9bad07d0da7025368e6e144205712b536 + languageName: node + linkType: hard + "get-caller-file@npm:^2.0.5": version: 2.0.5 resolution: "get-caller-file@npm:2.0.5" @@ -31065,6 +33446,13 @@ __metadata: languageName: node linkType: hard +"get-own-enumerable-property-symbols@npm:^3.0.0": + version: 3.0.2 + resolution: "get-own-enumerable-property-symbols@npm:3.0.2" + checksum: 10/8f0331f14159f939830884799f937343c8c0a2c330506094bc12cbee3665d88337fe97a4ea35c002cc2bdba0f5d9975ad7ec3abb925015cdf2a93e76d4759ede + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -31262,15 +33650,19 @@ __metadata: languageName: node linkType: hard -"glob@npm:9.3.5": - version: 9.3.5 - resolution: "glob@npm:9.3.5" +"glob@npm:11.0.3, glob@npm:^11.0.1": + version: 11.0.3 + resolution: "glob@npm:11.0.3" dependencies: - fs.realpath: "npm:^1.0.0" - minimatch: "npm:^8.0.2" - minipass: "npm:^4.2.4" - path-scurry: "npm:^1.6.1" - checksum: 10/e5fa8a58adf53525bca42d82a1fad9e6800032b7e4d372209b80cfdca524dd9a7dbe7d01a92d7ed20d89c572457f12c250092bc8817cb4f1c63efefdf9b658c0 + foreground-child: "npm:^3.3.1" + jackspeak: "npm:^4.1.1" + minimatch: "npm:^10.0.3" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^2.0.0" + bin: + glob: dist/esm/bin.mjs + checksum: 10/2ae536c1360c0266b523b2bfa6aadc10144a8b7e08869b088e37ac3c27cd30774f82e4bfb291cde796776e878f9e13200c7ff44010eb7054e00f46f649397893 languageName: node linkType: hard @@ -31290,23 +33682,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^11.0.1": - version: 11.0.2 - resolution: "glob@npm:11.0.2" - dependencies: - foreground-child: "npm:^3.1.0" - jackspeak: "npm:^4.0.1" - minimatch: "npm:^10.0.0" - minipass: "npm:^7.1.2" - package-json-from-dist: "npm:^1.0.0" - path-scurry: "npm:^2.0.0" - bin: - glob: dist/esm/bin.mjs - checksum: 10/53501530240150fdceb9ace47ab856acd1e0d598f8101b0760b665fc11dae2160d366563b89232ae4f5df7ddba8f7c92294719268fe932bd3a32d16cc58c3d02 - languageName: node - linkType: hard - -"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, glob@npm:^7.2.3": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -31475,6 +33851,17 @@ __metadata: languageName: node linkType: hard +"gonzales-pe@npm:^4.3.0": + version: 4.3.0 + resolution: "gonzales-pe@npm:4.3.0" + dependencies: + minimist: "npm:^1.2.5" + bin: + gonzales: bin/gonzales.js + checksum: 10/d1676546bcaa4cb1c6c1fc5de5d62e85960665a13a4c489b02baeb58a10c53a249beef05ceaf21ea801813a559ff17d7b61158aa417211c135bcb8bdcb1701ca + languageName: node + linkType: hard + "goober@npm:^2.0.33": version: 2.1.14 resolution: "goober@npm:2.1.14" @@ -32305,7 +34692,7 @@ __metadata: languageName: node linkType: hard -"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.7": +"http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.6, http-proxy-middleware@npm:^2.0.9": version: 2.0.9 resolution: "http-proxy-middleware@npm:2.0.9" dependencies: @@ -32862,9 +35249,9 @@ __metadata: languageName: node linkType: hard -"iovalkey@npm:^0.3.1": - version: 0.3.1 - resolution: "iovalkey@npm:0.3.1" +"iovalkey@npm:^0.3.3": + version: 0.3.3 + resolution: "iovalkey@npm:0.3.3" dependencies: "@iovalkey/commands": "npm:^0.1.0" cluster-key-slot: "npm:^1.1.0" @@ -32875,7 +35262,7 @@ __metadata: redis-errors: "npm:^1.2.0" redis-parser: "npm:^3.0.0" standard-as-callback: "npm:^2.1.0" - checksum: 10/afe5e0218810d902263dca2b22dd4501fb74698111f1850804d0948bd6a97793a7f5006757f9b6e8c8131bac6bd532d07ad971e7776bed7f6dc1f6e471706c53 + checksum: 10/39368842e6a9dbc85e92dfcd4c1c917a76240bac5c4210e19a0bd3bc7984af9b567dce5de7c560c4517d5d4cb03af229f38582c1dd055c6fd452060774318e8c languageName: node linkType: hard @@ -33327,6 +35714,13 @@ __metadata: languageName: node linkType: hard +"is-obj@npm:^1.0.1": + version: 1.0.1 + resolution: "is-obj@npm:1.0.1" + checksum: 10/3ccf0efdea12951e0b9c784e2b00e77e87b2f8bd30b42a498548a8afcc11b3287342a2030c308e473e93a7a19c9ea7854c99a8832a476591c727df2a9c79796c + languageName: node + linkType: hard + "is-obj@npm:^2.0.0": version: 2.0.0 resolution: "is-obj@npm:2.0.0" @@ -33448,6 +35842,13 @@ __metadata: languageName: node linkType: hard +"is-regexp@npm:^1.0.0": + version: 1.0.0 + resolution: "is-regexp@npm:1.0.0" + checksum: 10/be692828e24cba479ec33644326fa98959ec68ba77965e0291088c1a741feaea4919d79f8031708f85fd25e39de002b4520622b55460660b9c369e6f7187faef + languageName: node + linkType: hard + "is-resolvable@npm:^1.1.0": version: 1.1.0 resolution: "is-resolvable@npm:1.1.0" @@ -33584,6 +35985,13 @@ __metadata: languageName: node linkType: hard +"is-url-superb@npm:^4.0.0": + version: 4.0.0 + resolution: "is-url-superb@npm:4.0.0" + checksum: 10/fd55e91c96349acb0d688f95fcb1ac67450e5db934976e3a8ff13ef446841e779a6f4d18b15f02331f05a3429c8fdaba2382ac1ab444059e86e9ffcde1ec8db0 + languageName: node + linkType: hard + "is-url@npm:^1.2.4": version: 1.2.4 resolution: "is-url@npm:1.2.4" @@ -33928,12 +36336,12 @@ __metadata: languageName: node linkType: hard -"jackspeak@npm:^4.0.1": - version: 4.0.2 - resolution: "jackspeak@npm:4.0.2" +"jackspeak@npm:^4.1.1": + version: 4.1.1 + resolution: "jackspeak@npm:4.1.1" dependencies: "@isaacs/cliui": "npm:^8.0.2" - checksum: 10/d9722f0e55f6c322c57aedf094c405f4201b834204629817187953988075521cfddb23df83e2a7b845723ca7eb0555068c5ce1556732e9c275d32a531881efa8 + checksum: 10/ffceb270ec286841f48413bfb4a50b188662dfd599378ce142b6540f3f0a66821dc9dcb1e9ebc55c6c3b24dc2226c96e5819ba9bd7a241bd29031b61911718c7 languageName: node linkType: hard @@ -35266,6 +37674,13 @@ __metadata: languageName: node linkType: hard +"kafkajs@npm:^2.2.4": + version: 2.2.4 + resolution: "kafkajs@npm:2.2.4" + checksum: 10/75eb0d221397085f90e51f8a2d752495c9fa9c1b3a1a6db610cd7074fa8c52777f295832fd0a7c49cded5e574337a09fafa8c3f7cf1caa38f4dc9aa20fcfb7df + languageName: node + linkType: hard + "keygrip@npm:~1.1.0": version: 1.1.0 resolution: "keygrip@npm:1.1.0" @@ -35276,11 +37691,11 @@ __metadata: linkType: hard "keyv@npm:*, keyv@npm:^5.2.1": - version: 5.2.3 - resolution: "keyv@npm:5.2.3" + version: 5.3.4 + resolution: "keyv@npm:5.3.4" dependencies: - "@keyv/serialize": "npm:^1.0.2" - checksum: 10/47b4e9deb33e6a80e5ea79f3022ed3a14bc9fe553b7527ffff0a70b10c7a6c1a5d7e49b9bcfdbd8e8b9fb4632d68baa19d09e82628bcf853103e750e56d49a9e + "@keyv/serialize": "npm:^1.0.3" + checksum: 10/3e294eb1168af78ad3430d0cc47b6839fd3e70593238d35226a0a1e4094abe397ea378b7bce35cfcb314e55b5d1b4fcdae3c19bee5610e78d283b3cb5b279c8b languageName: node linkType: hard @@ -36481,6 +38896,33 @@ __metadata: languageName: node linkType: hard +"madge@npm:^8.0.0": + version: 8.0.0 + resolution: "madge@npm:8.0.0" + dependencies: + chalk: "npm:^4.1.2" + commander: "npm:^7.2.0" + commondir: "npm:^1.0.1" + debug: "npm:^4.3.4" + dependency-tree: "npm:^11.0.0" + ora: "npm:^5.4.1" + pluralize: "npm:^8.0.0" + pretty-ms: "npm:^7.0.1" + rc: "npm:^1.2.8" + stream-to-array: "npm:^2.3.0" + ts-graphviz: "npm:^2.1.2" + walkdir: "npm:^0.4.1" + peerDependencies: + typescript: ^5.4.4 + peerDependenciesMeta: + typescript: + optional: true + bin: + madge: bin/cli.js + checksum: 10/9adee480224aadb1d24d36f583396053c3cea84359316b601f5bc423c67c87e81ba842425acaa6c764ccc8c00241e2e78700b16f9af546111d1106b70c336174 + languageName: node + linkType: hard + "magic-string@npm:^0.25.7": version: 0.25.7 resolution: "magic-string@npm:0.25.7" @@ -36490,12 +38932,12 @@ __metadata: languageName: node linkType: hard -"magic-string@npm:^0.30.10, magic-string@npm:^0.30.3, magic-string@npm:^0.30.5": - version: 0.30.14 - resolution: "magic-string@npm:0.30.14" +"magic-string@npm:^0.30.10, magic-string@npm:^0.30.17, magic-string@npm:^0.30.3, magic-string@npm:^0.30.5": + version: 0.30.17 + resolution: "magic-string@npm:0.30.17" dependencies: "@jridgewell/sourcemap-codec": "npm:^1.5.0" - checksum: 10/8ca0f8937c2824e48ebc70e7e065a193c467713639cc6e5972aaba0fa5417b375a6f62c383410a19a66e618c386bb7253fbd3ccbfb0144bb310f0ba772121f12 + checksum: 10/2f71af2b0afd78c2e9012a29b066d2c8ba45a9cd0c8070f7fd72de982fb1c403b4e3afdb1dae00691d56885ede66b772ef6bedf765e02e3a7066208fe2fec4aa languageName: node linkType: hard @@ -36998,7 +39440,7 @@ __metadata: languageName: node linkType: hard -"memjs@npm:^1.3.1, memjs@npm:^1.3.2": +"memjs@npm:^1.3.2": version: 1.3.2 resolution: "memjs@npm:1.3.2" checksum: 10/705bb9be88180685fa3bc71375dc8b3a188356ab8f5c6c5ea1761e5e0912a78520cb964d2afc24e65686d53b45d3a7d8b5e770b2f3cf071a6eaea7f8d0d6bbfd @@ -37044,6 +39486,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 + "merge-stream@npm:^2.0.0": version: 2.0.0 resolution: "merge-stream@npm:2.0.0" @@ -37427,13 +39876,20 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0, mime-db@npm:>= 1.43.0 < 2": +"mime-db@npm:1.52.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 10/54bb60bf39e6f8689f6622784e668a3d7f8bed6b0d886f5c3c446cb3284be28b30bf707ed05d0fe44a036f8469976b2629bbea182684977b084de9da274694d7 languageName: node linkType: hard +"mime-db@npm:>= 1.43.0 < 2, 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-db@npm:~1.33.0": version: 1.33.0 resolution: "mime-db@npm:1.33.0" @@ -37459,7 +39915,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.18, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:^2.1.35, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:2.1.35, mime-types@npm:^2.1.12, mime-types@npm:^2.1.18, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -37468,6 +39924,15 @@ __metadata: 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 + "mime@npm:1.6.0, mime@npm:^1.3.4": version: 1.6.0 resolution: "mime@npm:1.6.0" @@ -37604,12 +40069,12 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^10.0.0": - version: 10.0.1 - resolution: "minimatch@npm:10.0.1" +"minimatch@npm:^10.0.3": + version: 10.0.3 + resolution: "minimatch@npm:10.0.3" dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/082e7ccbc090d5f8c4e4e029255d5a1d1e3af37bda837da2b8b0085b1503a1210c91ac90d9ebfe741d8a5f286ece820a1abb4f61dc1f82ce602a055d461d93f3 + "@isaacs/brace-expansion": "npm:^5.0.0" + checksum: 10/d5b8b2538b367f2cfd4aeef27539fddeee58d1efb692102b848e4a968a09780a302c530eb5aacfa8c57f7299155fb4b4e85219ad82664dcef5c66f657111d9b8 languageName: node linkType: hard @@ -37631,15 +40096,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^8.0.2": - version: 8.0.4 - resolution: "minimatch@npm:8.0.4" - dependencies: - brace-expansion: "npm:^2.0.1" - checksum: 10/aef05598ee565e1013bc8a10f53410ac681561f901c1a084b8ecfd016c9ed919f58f4bbd5b63e05643189dfb26e8106a84f0e1ff12e4a263aa37e1cae7ce9828 - languageName: node - linkType: hard - "minimatch@npm:^9.0.0, minimatch@npm:^9.0.4, minimatch@npm:^9.0.5": version: 9.0.5 resolution: "minimatch@npm:9.0.5" @@ -37798,7 +40254,7 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^4.0.0, minipass@npm:^4.2.4": +"minipass@npm:^4.0.0": version: 4.2.8 resolution: "minipass@npm:4.2.8" checksum: 10/e148eb6dcb85c980234cad889139ef8ddf9d5bdac534f4f0268446c8792dd4c74f4502479be48de3c1cce2f6450f6da4d0d4a86405a8a12be04c1c36b339569a @@ -37994,6 +40450,18 @@ __metadata: languageName: node linkType: hard +"module-definition@npm:^6.0.1": + version: 6.0.1 + resolution: "module-definition@npm:6.0.1" + dependencies: + ast-module-types: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + bin: + module-definition: bin/cli.js + checksum: 10/a61280434289b9c2209a0075e3754731c797dce1816f164995e8a94ba57d3b0cffd6ca5d719b26cdef31c5fb41176676d54f2dc83cc62f7e61576369a05bef7c + languageName: node + linkType: hard + "module-details-from-path@npm:^1.0.3": version: 1.0.3 resolution: "module-details-from-path@npm:1.0.3" @@ -38001,6 +40469,20 @@ __metadata: languageName: node linkType: hard +"module-lookup-amd@npm:^9.0.3": + version: 9.0.5 + resolution: "module-lookup-amd@npm:9.0.5" + dependencies: + commander: "npm:^12.1.0" + glob: "npm:^7.2.3" + requirejs: "npm:^2.3.7" + requirejs-config-file: "npm:^4.0.0" + bin: + lookup-amd: bin/cli.js + checksum: 10/9d1e001a0d59a9169437eb595d26ccf6c0d2905ab46d26bf819495afdea3c796252c0417a4af62b42ce5ae519cc6d66968419a3c7b70b0671bc158a4bc57375a + languageName: node + linkType: hard + "moo@npm:^0.5.0": version: 0.5.2 resolution: "moo@npm:0.5.2" @@ -38021,6 +40503,43 @@ __metadata: languageName: node linkType: hard +"motion-dom@npm:^12.20.1": + version: 12.20.1 + resolution: "motion-dom@npm:12.20.1" + dependencies: + motion-utils: "npm:^12.19.0" + checksum: 10/b92d733fdfe3f66511f73c7f84f9483a0529eadf6ad2ab633cef584d161bcd1af786d257776e1fe7cd2b4e2c9c10912773f96ce9e2bd5b45c6c33ba6d9edfe86 + languageName: node + linkType: hard + +"motion-utils@npm:^12.19.0": + version: 12.19.0 + resolution: "motion-utils@npm:12.19.0" + checksum: 10/a6fe6b329bf4e2070bb95788218f36f0d7ea6cc3098e6ea2646ed68d5be88d25fa30e55284463ace78d19a5e19714875a7b70055bd86ae6e52a5fdee08438470 + languageName: node + linkType: hard + +"motion@npm:^12.20.1": + version: 12.20.1 + resolution: "motion@npm:12.20.1" + dependencies: + framer-motion: "npm:^12.20.1" + tslib: "npm:^2.4.0" + peerDependencies: + "@emotion/is-prop-valid": "*" + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@emotion/is-prop-valid": + optional: true + react: + optional: true + react-dom: + optional: true + checksum: 10/49057c8d5812207209ca078a67ee7467d2dca7abacdcf2db486cd28c3ca105a7fe2504c34407df201c85035f6df1c806954af94914417ee89d721133fc4e92ce + languageName: node + linkType: hard + "mri@npm:1.1.4": version: 1.1.4 resolution: "mri@npm:1.1.4" @@ -38091,14 +40610,14 @@ __metadata: linkType: hard "msw@npm:^2.0.0, msw@npm:^2.0.8": - version: 2.8.2 - resolution: "msw@npm:2.8.2" + version: 2.10.2 + resolution: "msw@npm:2.10.2" dependencies: "@bundled-es-modules/cookie": "npm:^2.0.1" "@bundled-es-modules/statuses": "npm:^1.0.1" "@bundled-es-modules/tough-cookie": "npm:^0.1.6" "@inquirer/confirm": "npm:^5.0.0" - "@mswjs/interceptors": "npm:^0.37.0" + "@mswjs/interceptors": "npm:^0.39.1" "@open-draft/deferred-promise": "npm:^2.2.0" "@open-draft/until": "npm:^2.1.0" "@types/cookie": "npm:^0.6.0" @@ -38119,7 +40638,7 @@ __metadata: optional: true bin: msw: cli/index.js - checksum: 10/7579a8dccb8cc8eb0f13d0bf3a232a3d50a478511d95bc2a4b70778e78ffa28fd0949a855fbceb6ec381bbf76f6331a6d36fcb4a3197ff5bf55d0f14a7f3b35c + checksum: 10/bc90bc34a0b9e978e662f33fa630a0de66c4b6eff3a92b41efa08bf67d79b43e4a961a17b0d274580393c912f4ac6226c76a8bc33148799b1ee43477df712c26 languageName: node linkType: hard @@ -38242,12 +40761,12 @@ __metadata: languageName: node linkType: hard -"nanoid@npm:^3.3.7": - version: 3.3.8 - resolution: "nanoid@npm:3.3.8" +"nanoid@npm:^3.3.11, nanoid@npm:^3.3.7": + version: 3.3.11 + resolution: "nanoid@npm:3.3.11" bin: nanoid: bin/nanoid.cjs - checksum: 10/2d1766606cf0d6f47b6f0fdab91761bb81609b2e3d367027aff45e6ee7006f660fb7e7781f4a34799fe6734f1268eeed2e37a5fdee809ade0c2d4eb11b0f9c40 + checksum: 10/73b5afe5975a307aaa3c95dfe3334c52cdf9ae71518176895229b8d65ab0d1c0417dd081426134eb7571c055720428ea5d57c645138161e7d10df80815527c48 languageName: node linkType: hard @@ -38655,6 +41174,15 @@ __metadata: languageName: node linkType: hard +"node-source-walk@npm:^7.0.1": + version: 7.0.1 + resolution: "node-source-walk@npm:7.0.1" + dependencies: + "@babel/parser": "npm:^7.26.7" + checksum: 10/cd71a136b095aa8055d2a670e7820d467eaad3c62d343dacb706d3ac651991ae73032ce5f82a9ac0373ec8186d98c3c7e9b4311f1cd0887d1ec035c734b5f5ed + languageName: node + linkType: hard + "node-stdlib-browser@npm:^1.3.1": version: 1.3.1 resolution: "node-stdlib-browser@npm:1.3.1" @@ -39167,7 +41695,7 @@ __metadata: languageName: node linkType: hard -"obuf@npm:^1.0.0, obuf@npm:^1.1.2, obuf@npm:~1.1.2": +"obuf@npm:^1.0.0, obuf@npm:^1.1.2": version: 1.1.2 resolution: "obuf@npm:1.1.2" checksum: 10/53ff4ab3a13cc33ba6c856cf281f2965c0aec9720967af450e8fd06cfd50aceeefc791986a16bcefa14e7898b3ca9acdfcf15b9d9a1b9c7e1366581a8ad6e65e @@ -39184,20 +41712,21 @@ __metadata: linkType: hard "octokit@npm:^3.0.0": - version: 3.2.1 - resolution: "octokit@npm:3.2.1" + version: 3.2.2 + resolution: "octokit@npm:3.2.2" dependencies: "@octokit/app": "npm:^14.0.2" "@octokit/core": "npm:^5.0.0" "@octokit/oauth-app": "npm:^6.0.0" "@octokit/plugin-paginate-graphql": "npm:^4.0.0" - "@octokit/plugin-paginate-rest": "npm:11.3.1" - "@octokit/plugin-rest-endpoint-methods": "npm:13.2.2" + "@octokit/plugin-paginate-rest": "npm:11.4.4-cjs.2" + "@octokit/plugin-rest-endpoint-methods": "npm:13.3.2-cjs.1" "@octokit/plugin-retry": "npm:^6.0.0" "@octokit/plugin-throttling": "npm:^8.0.0" "@octokit/request-error": "npm:^5.0.0" "@octokit/types": "npm:^13.0.0" - checksum: 10/a3539831c9c0828b1e37dc94a3668f5fb9aa873fc396c0464275d152e4065b6b20300cfba766871cb52ed2e6d537febda7d4be872894c4c62024393a3578fde0 + "@octokit/webhooks": "npm:^12.3.1" + checksum: 10/a258cc62767552fcf9d9a2dfb2aac44b326a2a235fb6444c1f37bc018bda4ac4bf0203704d8b7b3955d3caee6f9256c200287f7e99843c8be01febe4d9fe86d8 languageName: node linkType: hard @@ -39876,6 +42405,13 @@ __metadata: languageName: node linkType: hard +"parse-ms@npm:^2.1.0": + version: 2.1.0 + resolution: "parse-ms@npm:2.1.0" + checksum: 10/517eab80cdb9df6ae22a8fad944bfb4289482699bcde5211a1c127091dfea33c3dcb217246b188865fc32e998bcee815bfa4a863f41e3b2d0bcc69f34ef1a543 + languageName: node + linkType: hard + "parse-ms@npm:^4.0.0": version: 4.0.0 resolution: "parse-ms@npm:4.0.0" @@ -40147,7 +42683,7 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.11.1, path-scurry@npm:^1.6.1": +"path-scurry@npm:^1.11.1": version: 1.11.1 resolution: "path-scurry@npm:1.11.1" dependencies: @@ -40250,13 +42786,6 @@ __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" @@ -40306,13 +42835,6 @@ __metadata: languageName: node linkType: hard -"pg-numeric@npm:1.0.2": - version: 1.0.2 - resolution: "pg-numeric@npm:1.0.2" - checksum: 10/8899f8200caa1744439a8778a9eb3ceefb599d893e40a09eef84ee0d4c151319fd416634a6c0fc7b7db4ac268710042da5be700b80ef0de716fe089b8652c84f - languageName: node - linkType: hard - "pg-pool@npm:^3.8.0": version: 3.8.0 resolution: "pg-pool@npm:3.8.0" @@ -40342,21 +42864,6 @@ __metadata: languageName: node linkType: hard -"pg-types@npm:^4.0.1": - version: 4.0.1 - resolution: "pg-types@npm:4.0.1" - dependencies: - pg-int8: "npm:1.0.1" - pg-numeric: "npm:1.0.2" - postgres-array: "npm:~3.0.1" - postgres-bytea: "npm:~3.0.0" - postgres-date: "npm:~2.0.1" - postgres-interval: "npm:^3.0.0" - postgres-range: "npm:^1.1.1" - checksum: 10/2c686ef361856ff9695a40cf34d7bb59bcf2218bd1f121d7fc8b77a75cc4106c09519e9520a82aac8fce4540dec81f695458b8e43bfc420ba74eb9b5005b5619 - languageName: node - linkType: hard - "pg@npm:^8.11.3, pg@npm:^8.9.0": version: 8.14.1 resolution: "pg@npm:8.14.1" @@ -40523,6 +43030,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 + "pkg-dir@npm:^3.0.0": version: 3.0.0 resolution: "pkg-dir@npm:3.0.0" @@ -40573,27 +43087,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.52.0": - version: 1.52.0 - resolution: "playwright-core@npm:1.52.0" +"playwright-core@npm:1.53.2": + version: 1.53.2 + resolution: "playwright-core@npm:1.53.2" bin: playwright-core: cli.js - checksum: 10/42e13f5f98dc25ebc95525fb338a215b9097b2ba39d41e99972a190bf75d79979f163f5bc07b1ca06847ee07acb2c9b487d070fab67e9cd55e33310fc05aca3c + checksum: 10/575c37b66aa62e1300d8b42d4b021c9304a35266ea47b6f0c36b429c40674082700b7172ee3641de7451cd2a4c736c42e0e39fd5f6a7ba4c81a6eefd8c104efd languageName: node linkType: hard -"playwright@npm:1.52.0": - version: 1.52.0 - resolution: "playwright@npm:1.52.0" +"playwright@npm:1.53.2": + version: 1.53.2 + resolution: "playwright@npm:1.53.2" dependencies: fsevents: "npm:2.3.2" - playwright-core: "npm:1.52.0" + playwright-core: "npm:1.53.2" dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: 10/214175446089000c2ac997b925063b95f7d86d129c5d7c74caa5ddcb05bcad598dfd569d2133a10dc82d288bf67e7858877dcd099274b0b928b9c63db7d6ecec + checksum: 10/1c860c52a77f32793d7b1b5f5b15da9b809b8a09a82e5c89f79442fe9708ff6314b6ee6d13aa0961a6c9fddc9209f0a109ac524bdf586e0df52b641e96fa759d languageName: node linkType: hard @@ -41075,14 +43589,27 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.33": - version: 8.4.49 - resolution: "postcss@npm:8.4.49" +"postcss-values-parser@npm:^6.0.2": + version: 6.0.2 + resolution: "postcss-values-parser@npm:6.0.2" dependencies: - nanoid: "npm:^3.3.7" + color-name: "npm:^1.1.4" + is-url-superb: "npm:^4.0.0" + quote-unquote: "npm:^1.0.0" + peerDependencies: + postcss: ^8.2.9 + checksum: 10/ff2fa096896f1c33f7531e814b8d01e785bd99d672c1597d5c5d8c2409b30b8146be6565f6269c952d1f03d626f00ae3f1afb8308cc772c08b323abee23c9a42 + languageName: node + linkType: hard + +"postcss@npm:^8.1.0, postcss@npm:^8.4.33, postcss@npm:^8.5.1, postcss@npm:^8.5.6": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" + dependencies: + nanoid: "npm:^3.3.11" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10/28fe1005b1339870e0a5006375ba5ac1213fd69800f79e7db09c398e074421ba6e162898e94f64942fed554037fd292db3811d87835d25ab5ef7f3c9daacb6ca + checksum: 10/9e4fbe97574091e9736d0e82a591e29aa100a0bf60276a926308f8c57249698935f35c5d2f4e80de778d0cbb8dcffab4f383d85fd50c5649aca421c3df729b86 languageName: node linkType: hard @@ -41093,13 +43620,6 @@ __metadata: languageName: node linkType: hard -"postgres-array@npm:~3.0.1": - version: 3.0.2 - resolution: "postgres-array@npm:3.0.2" - checksum: 10/0159517e4e5f263bf9e324f0c4d3c10244a294021f2b5980abc8c23afdb965370a7fc0c82012fce4d28e83186ad089b6476b05fcef6c88f8e43e37a3a2fa0ad5 - languageName: node - linkType: hard - "postgres-bytea@npm:~1.0.0": version: 1.0.0 resolution: "postgres-bytea@npm:1.0.0" @@ -41107,15 +43627,6 @@ __metadata: languageName: node linkType: hard -"postgres-bytea@npm:~3.0.0": - version: 3.0.0 - resolution: "postgres-bytea@npm:3.0.0" - dependencies: - obuf: "npm:~1.1.2" - checksum: 10/f5c01758fd2fa807afbd34e1ba2146f683818ebc2d23f4a62f0fd627c0b1126fc543cab1b63925f97ce6c7d8f5f316043218619c447445210ea82f10411efb1b - languageName: node - linkType: hard - "postgres-date@npm:~1.0.4": version: 1.0.5 resolution: "postgres-date@npm:1.0.5" @@ -41123,13 +43634,6 @@ __metadata: languageName: node linkType: hard -"postgres-date@npm:~2.0.1": - version: 2.0.1 - resolution: "postgres-date@npm:2.0.1" - checksum: 10/908eacec35faf9b6fa4248074e7c8db0fc4ac6a2e0a00eacef9fb9cf44a25fc6f100af7cda09f5d89d076c87923b59e940a309560d86aded4753985c97255be1 - languageName: node - linkType: hard - "postgres-interval@npm:^1.1.0": version: 1.2.0 resolution: "postgres-interval@npm:1.2.0" @@ -41139,20 +43643,6 @@ __metadata: languageName: node linkType: hard -"postgres-interval@npm:^3.0.0": - version: 3.0.0 - resolution: "postgres-interval@npm:3.0.0" - checksum: 10/c7a1cf006de97de663b6b8c4d2b167aa9909a238c4866a94b15d303762f5ac884ff4796cd6e2111b7f0a91302b83c570453aa8506fd005b5a5d5dfa87441bebc - languageName: node - linkType: hard - -"postgres-range@npm:^1.1.1": - version: 1.1.3 - resolution: "postgres-range@npm:1.1.3" - checksum: 10/356a46d97eab301cca0dbf7a65be6f68c4a7e33d304909e5ca840038b1210390e1d33fcc3b566c42020f7b97e9de189bc5286d1ec8d7ba2445ed3377c576ce9e - languageName: node - linkType: hard - "postman-collection@npm:^4.1.7": version: 4.2.1 resolution: "postman-collection@npm:4.2.1" @@ -41203,6 +43693,31 @@ __metadata: languageName: node linkType: hard +"precinct@npm:^12.2.0": + version: 12.2.0 + resolution: "precinct@npm:12.2.0" + dependencies: + "@dependents/detective-less": "npm:^5.0.1" + commander: "npm:^12.1.0" + detective-amd: "npm:^6.0.1" + detective-cjs: "npm:^6.0.1" + detective-es6: "npm:^5.0.1" + detective-postcss: "npm:^7.0.1" + detective-sass: "npm:^6.0.1" + detective-scss: "npm:^5.0.1" + detective-stylus: "npm:^5.0.1" + detective-typescript: "npm:^14.0.0" + detective-vue2: "npm:^2.2.0" + module-definition: "npm:^6.0.1" + node-source-walk: "npm:^7.0.1" + postcss: "npm:^8.5.1" + typescript: "npm:^5.7.3" + bin: + precinct: bin/cli.js + checksum: 10/fb0cd96b234aa70ede2326971dff039ceee99c4bab68ee8c3f5c3cd09ca67f7a7ae7537121ac49698ef0f9d90f3454e9d3bf2b22bf5ceb745fe4049ec31c9ef0 + languageName: node + linkType: hard + "precond@npm:0.2": version: 0.2.3 resolution: "precond@npm:0.2.3" @@ -41291,6 +43806,15 @@ __metadata: languageName: node linkType: hard +"pretty-ms@npm:^7.0.1": + version: 7.0.1 + resolution: "pretty-ms@npm:7.0.1" + dependencies: + parse-ms: "npm:^2.1.0" + checksum: 10/a39aac23cc7dae7a94c70518ab8b6c6db0894a7b84c81ee7abc8778c5ec8bae2d1e71ba991ff641732b38433724bfbdbb37bd3a00418637f797c072e06fe8b4c + languageName: node + linkType: hard + "pretty-ms@npm:^9.0.0": version: 9.2.0 resolution: "pretty-ms@npm:9.2.0" @@ -41543,7 +44067,7 @@ __metadata: languageName: node linkType: hard -"proxy-addr@npm:~2.0.7": +"proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" dependencies: @@ -41667,7 +44191,7 @@ __metadata: languageName: node linkType: hard -"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.7.0, qs@npm:^6.9.4": +"qs@npm:^6.10.1, qs@npm:^6.10.3, qs@npm:^6.11.0, qs@npm:^6.12.2, qs@npm:^6.12.3, qs@npm:^6.14.0, qs@npm:^6.7.0, qs@npm:^6.9.4": version: 6.14.0 resolution: "qs@npm:6.14.0" dependencies: @@ -41738,6 +44262,13 @@ __metadata: languageName: node linkType: hard +"quote-unquote@npm:^1.0.0": + version: 1.0.0 + resolution: "quote-unquote@npm:1.0.0" + checksum: 10/955a2ead534f5b6a3f8d4dc5a4b95ac6468213d3fb11f8c1592a0a56345c45a3d14d5ca04d3de2bc9891493fcac38c03dfa91c48a6159aef50124e9c5afcea49 + languageName: node + linkType: hard + "raf-schd@npm:^4.0.2": version: 4.0.2 resolution: "raf-schd@npm:4.0.2" @@ -41835,6 +44366,15 @@ __metadata: languageName: node linkType: hard +"rate-limit-redis@npm:^4.2.0": + version: 4.2.1 + resolution: "rate-limit-redis@npm:4.2.1" + peerDependencies: + express-rate-limit: ">= 6" + checksum: 10/c36c50cfca992cbd14c97c08bb01c7d1d340d27b5fa1a2c1154275fec0b59c187eb6fb207da3d035c7c87e7629c92324cd53074f854dbb16548faf94f5a41351 + languageName: node + linkType: hard + "rate-limiter-flexible@npm:^4.0.1": version: 4.0.1 resolution: "rate-limiter-flexible@npm:4.0.1" @@ -41854,6 +44394,18 @@ __metadata: 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 + "raw-loader@npm:^4.0.2": version: 4.0.2 resolution: "raw-loader@npm:4.0.2" @@ -41915,6 +44467,98 @@ __metadata: languageName: node linkType: hard +"react-aria-components@npm:^1.10.1": + version: 1.10.1 + resolution: "react-aria-components@npm:1.10.1" + dependencies: + "@internationalized/date": "npm:^3.8.2" + "@internationalized/string": "npm:^3.2.7" + "@react-aria/autocomplete": "npm:3.0.0-beta.5" + "@react-aria/collections": "npm:3.0.0-rc.3" + "@react-aria/dnd": "npm:^3.10.1" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/live-announcer": "npm:^3.4.3" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/toolbar": "npm:3.0.0-beta.18" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/virtualizer": "npm:^4.1.7" + "@react-stately/autocomplete": "npm:3.0.0-beta.2" + "@react-stately/layout": "npm:^4.3.1" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/table": "npm:^3.14.3" + "@react-stately/utils": "npm:^3.10.7" + "@react-stately/virtualizer": "npm:^4.4.1" + "@react-types/form": "npm:^3.7.13" + "@react-types/grid": "npm:^3.3.3" + "@react-types/shared": "npm:^3.30.0" + "@react-types/table": "npm:^3.13.1" + "@swc/helpers": "npm:^0.5.0" + client-only: "npm:^0.0.1" + react-aria: "npm:^3.41.1" + react-stately: "npm:^3.39.0" + use-sync-external-store: "npm:^1.4.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6fcc35404bdd8718bbfb05263bd0c67157d2ba665e0446c47f42f78262ef92231bb0512fc7734c2114bb20c28fff8eb13694c9719c741d2c7a59181fd8fdf115 + languageName: node + linkType: hard + +"react-aria@npm:^3.41.1": + version: 3.41.1 + resolution: "react-aria@npm:3.41.1" + dependencies: + "@internationalized/string": "npm:^3.2.7" + "@react-aria/breadcrumbs": "npm:^3.5.26" + "@react-aria/button": "npm:^3.13.3" + "@react-aria/calendar": "npm:^3.8.3" + "@react-aria/checkbox": "npm:^3.15.7" + "@react-aria/color": "npm:^3.0.9" + "@react-aria/combobox": "npm:^3.12.5" + "@react-aria/datepicker": "npm:^3.14.5" + "@react-aria/dialog": "npm:^3.5.27" + "@react-aria/disclosure": "npm:^3.0.6" + "@react-aria/dnd": "npm:^3.10.1" + "@react-aria/focus": "npm:^3.20.5" + "@react-aria/gridlist": "npm:^3.13.2" + "@react-aria/i18n": "npm:^3.12.10" + "@react-aria/interactions": "npm:^3.25.3" + "@react-aria/label": "npm:^3.7.19" + "@react-aria/landmark": "npm:^3.0.4" + "@react-aria/link": "npm:^3.8.3" + "@react-aria/listbox": "npm:^3.14.6" + "@react-aria/menu": "npm:^3.18.5" + "@react-aria/meter": "npm:^3.4.24" + "@react-aria/numberfield": "npm:^3.11.16" + "@react-aria/overlays": "npm:^3.27.3" + "@react-aria/progress": "npm:^3.4.24" + "@react-aria/radio": "npm:^3.11.5" + "@react-aria/searchfield": "npm:^3.8.6" + "@react-aria/select": "npm:^3.15.7" + "@react-aria/selection": "npm:^3.24.3" + "@react-aria/separator": "npm:^3.4.10" + "@react-aria/slider": "npm:^3.7.21" + "@react-aria/ssr": "npm:^3.9.9" + "@react-aria/switch": "npm:^3.7.5" + "@react-aria/table": "npm:^3.17.5" + "@react-aria/tabs": "npm:^3.10.5" + "@react-aria/tag": "npm:^3.6.2" + "@react-aria/textfield": "npm:^3.17.5" + "@react-aria/toast": "npm:^3.0.5" + "@react-aria/tooltip": "npm:^3.8.5" + "@react-aria/tree": "npm:^3.1.1" + "@react-aria/utils": "npm:^3.29.1" + "@react-aria/visually-hidden": "npm:^3.8.25" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/6ec619bc9b26800516e22054a428ea29ebc7bcfabf762918893a5a1a73d1576dd4b1c99d85aae02ae69eb16a49d1a765ab34cb840a500134a76e145a4e995f42 + languageName: node + linkType: hard + "react-base16-styling@npm:^0.6.0": version: 0.6.0 resolution: "react-base16-styling@npm:0.6.0" @@ -42510,6 +45154,42 @@ __metadata: languageName: node linkType: hard +"react-stately@npm:^3.39.0": + version: 3.39.0 + resolution: "react-stately@npm:3.39.0" + dependencies: + "@react-stately/calendar": "npm:^3.8.2" + "@react-stately/checkbox": "npm:^3.6.15" + "@react-stately/collections": "npm:^3.12.5" + "@react-stately/color": "npm:^3.8.6" + "@react-stately/combobox": "npm:^3.10.6" + "@react-stately/data": "npm:^3.13.1" + "@react-stately/datepicker": "npm:^3.14.2" + "@react-stately/disclosure": "npm:^3.0.5" + "@react-stately/dnd": "npm:^3.6.0" + "@react-stately/form": "npm:^3.1.5" + "@react-stately/list": "npm:^3.12.3" + "@react-stately/menu": "npm:^3.9.5" + "@react-stately/numberfield": "npm:^3.9.13" + "@react-stately/overlays": "npm:^3.6.17" + "@react-stately/radio": "npm:^3.10.14" + "@react-stately/searchfield": "npm:^3.5.13" + "@react-stately/select": "npm:^3.6.14" + "@react-stately/selection": "npm:^3.20.3" + "@react-stately/slider": "npm:^3.6.5" + "@react-stately/table": "npm:^3.14.3" + "@react-stately/tabs": "npm:^3.8.3" + "@react-stately/toast": "npm:^3.1.1" + "@react-stately/toggle": "npm:^3.8.5" + "@react-stately/tooltip": "npm:^3.5.5" + "@react-stately/tree": "npm:^3.9.0" + "@react-types/shared": "npm:^3.30.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + checksum: 10/f59f86aca0797e389698fc20a7e88a26748e00e56e011dd6d9f7a4fa1ba7fda32083bc8e5f2af000957d20ad9f4c74dd151cb667b958dcac8f3959d860905fd4 + languageName: node + linkType: hard + "react-style-singleton@npm:^2.2.1": version: 2.2.1 resolution: "react-style-singleton@npm:2.2.1" @@ -42882,20 +45562,6 @@ __metadata: languageName: node linkType: hard -"redis@npm:^4.7.0": - version: 4.7.0 - resolution: "redis@npm:4.7.0" - dependencies: - "@redis/bloom": "npm:1.2.0" - "@redis/client": "npm:1.6.0" - "@redis/graph": "npm:1.1.1" - "@redis/json": "npm:1.0.7" - "@redis/search": "npm:1.2.0" - "@redis/time-series": "npm:1.1.0" - checksum: 10/d927a0b1516e2845b7eab67b1466b6f2d0d0695be7a3d4a0a1ffa2f2c60dace98fb9ad01ec0db07519fb9d4d078b99d95b6809508c2a72afe6f814fc8f693188 - languageName: node - linkType: hard - "redux-immutable@npm:^4.0.0": version: 4.0.0 resolution: "redux-immutable@npm:4.0.0" @@ -43210,6 +45876,26 @@ __metadata: languageName: node linkType: hard +"requirejs-config-file@npm:^4.0.0": + version: 4.0.0 + resolution: "requirejs-config-file@npm:4.0.0" + dependencies: + esprima: "npm:^4.0.0" + stringify-object: "npm:^3.2.1" + checksum: 10/9fc55813ca8627cc4d3704622702b6b119a222c97825fc61459727d5f9ef9435e4fd34ee03335dc119c66e834d7963fdfe43d9a4df99448d43219587c624ba4e + languageName: node + linkType: hard + +"requirejs@npm:^2.3.7": + version: 2.3.7 + resolution: "requirejs@npm:2.3.7" + bin: + r.js: bin/r.js + r_js: bin/r.js + checksum: 10/7f42af10d8af210a2769501004b34f9dad4c39716f008f91faeef36c33b60a5f372ca1025c8f40b262f2522fd1ea0f36fd11e9587cba14311fac0df51a8571b0 + languageName: node + linkType: hard + "requires-port@npm:^1.0.0": version: 1.0.0 resolution: "requires-port@npm:1.0.0" @@ -43247,6 +45933,13 @@ __metadata: languageName: node linkType: hard +"resolve-dependency-path@npm:^4.0.1": + version: 4.0.1 + resolution: "resolve-dependency-path@npm:4.0.1" + checksum: 10/db52fc3b970c6784fa5a3695dfeccce0a9dd26d1ea7a4e83a91c30c7be11aed21d27c27c59311c5362fc1c6d2f7f5c40913304a544301c524bc18dbd98837622 + languageName: node + linkType: hard + "resolve-dir@npm:^1.0.0, resolve-dir@npm:^1.0.1": version: 1.0.1 resolution: "resolve-dir@npm:1.0.1" @@ -43298,7 +45991,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:~1.22.1, resolve@npm:~1.22.2": +"resolve@npm:^1.1.6, resolve@npm:^1.14.2, resolve@npm:^1.17.0, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:^1.22.10, resolve@npm:^1.22.4, resolve@npm:^1.22.8, resolve@npm:~1.22.1, resolve@npm:~1.22.2": version: 1.22.10 resolution: "resolve@npm:1.22.10" dependencies: @@ -43337,7 +46030,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin<compat/resolve>": +"resolve@patch:resolve@npm%3A^1.1.6#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.17.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.19.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.20.0#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.10#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.4#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A^1.22.8#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.1#optional!builtin<compat/resolve>, resolve@patch:resolve@npm%3A~1.22.2#optional!builtin<compat/resolve>": version: 1.22.10 resolution: "resolve@patch:resolve@npm%3A1.22.10#optional!builtin<compat/resolve>::version=1.22.10&hash=c3c19d" dependencies: @@ -43726,6 +46419,7 @@ __metadata: "@backstage/create-app": "workspace:*" "@backstage/e2e-test-utils": "workspace:*" "@backstage/errors": "workspace:^" + "@backstage/eslint-plugin": "workspace:*" "@backstage/repo-tools": "workspace:*" "@changesets/cli": "npm:^2.14.0" "@manypkg/get-packages": "npm:^1.1.3" @@ -43750,6 +46444,7 @@ __metadata: fs-extra: "npm:^11.2.0" husky: "npm:^9.0.0" lint-staged: "npm:^15.0.0" + madge: "npm:^8.0.0" minimist: "npm:^1.2.5" node-gyp: "npm:^10.0.0" prettier: "npm:^2.2.1" @@ -43763,6 +46458,19 @@ __metadata: languageName: unknown linkType: soft +"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 + "rrweb-cssom@npm:^0.7.1": version: 0.7.1 resolution: "rrweb-cssom@npm:0.7.1" @@ -43922,6 +46630,18 @@ __metadata: languageName: node linkType: hard +"sass-lookup@npm:^6.1.0": + version: 6.1.0 + resolution: "sass-lookup@npm:6.1.0" + dependencies: + commander: "npm:^12.1.0" + enhanced-resolve: "npm:^5.18.0" + bin: + sass-lookup: bin/cli.js + checksum: 10/90a06d5b0545615919f619608019a731eee81939cd0f1f88ed006060222f57de797ad20fc0eb103e13e6a9ff3e620af773e9f3e2ddf69a99fb2ccf4513f47fa4 + languageName: node + linkType: hard + "saxes@npm:^6.0.0": version: 6.0.0 resolution: "saxes@npm:6.0.0" @@ -44146,6 +46866,25 @@ __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 + "seq-queue@npm:^0.0.5": version: 0.0.5 resolution: "seq-queue@npm:0.0.5" @@ -44222,6 +46961,18 @@ __metadata: 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-blocking@npm:^2.0.0": version: 2.0.0 resolution: "set-blocking@npm:2.0.0" @@ -44230,9 +46981,9 @@ __metadata: linkType: hard "set-cookie-parser@npm:^2.4.6": - version: 2.6.0 - resolution: "set-cookie-parser@npm:2.6.0" - checksum: 10/8d451ebadb760989f93b634942c79de3c925ca7a986d133d08a80c40b5ae713ce12e354f0d5245c49f288c52daa7bd6554d5dc52f8a4eecaaf5e192881cf2b1f + version: 2.7.1 + resolution: "set-cookie-parser@npm:2.7.1" + checksum: 10/c92b1130032693342bca13ea1b1bc93967ab37deec4387fcd8c2a843c0ef2fd9a9f3df25aea5bb3976cd05a91c2cf4632dd6164d6e1814208fb7d7e14edd42b4 languageName: node linkType: hard @@ -45233,7 +47984,7 @@ __metadata: languageName: node linkType: hard -"stream-to-array@npm:~2.3.0": +"stream-to-array@npm:^2.3.0, stream-to-array@npm:~2.3.0": version: 2.3.0 resolution: "stream-to-array@npm:2.3.0" dependencies: @@ -45470,6 +48221,17 @@ __metadata: languageName: node linkType: hard +"stringify-object@npm:^3.2.1": + version: 3.3.0 + resolution: "stringify-object@npm:3.3.0" + dependencies: + get-own-enumerable-property-symbols: "npm:^3.0.0" + is-obj: "npm:^1.0.1" + is-regexp: "npm:^1.0.0" + checksum: 10/973782f09a3df3f39a2cf07dbf43fb9ba6cb32976f3616cd0f6c10e0a5c5415dd72b7b700e72920e8da2bf57c3001b8e37b5af7174bab9a748ce0416989e19b1 + languageName: node + linkType: hard + "strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:6.0, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": version: 6.0.1 resolution: "strip-ansi@npm:6.0.1" @@ -45606,13 +48368,12 @@ __metadata: languageName: node linkType: hard -"strtok3@npm:^10.2.0": - version: 10.2.2 - resolution: "strtok3@npm:10.2.2" +"strtok3@npm:^10.2.2": + version: 10.3.1 + resolution: "strtok3@npm:10.3.1" dependencies: "@tokenizer/token": "npm:^0.3.0" - peek-readable: "npm:^7.0.0" - checksum: 10/964e60d76576232803fc75572adfb8778b55a7ef021ecfa3a507dcdb426f1e2c42580e545ab18f882da8f2e9bfca05fb0659f56f71b18723e95c31c0512301fc + checksum: 10/bb7950cc9ce98ec742a5db360630f0b004f16197959ae28d8c8dad4f8f0e405d71cfdc992483038ba29a0b4cbd7227618ad2492005b510d84a3fc5903df0c13f languageName: node linkType: hard @@ -45723,6 +48484,17 @@ __metadata: languageName: node linkType: hard +"stylus-lookup@npm:^6.1.0": + version: 6.1.0 + resolution: "stylus-lookup@npm:6.1.0" + dependencies: + commander: "npm:^12.1.0" + bin: + stylus-lookup: bin/cli.js + checksum: 10/0d998a648025b5e4ad07b65c9f0ee3a0b67cfdbecc352d56e6b2806f9f0ce966ffa287922d3dde7e7e245510a10d6435169dbaa015bdda44cccc60907a2bbce7 + languageName: node + linkType: hard + "sucrase@npm:^3.20.2": version: 3.35.0 resolution: "sucrase@npm:3.35.0" @@ -46663,12 +49435,12 @@ __metadata: languageName: node linkType: hard -"ts-api-utils@npm:^2.0.1": - version: 2.0.1 - resolution: "ts-api-utils@npm:2.0.1" +"ts-api-utils@npm:^2.0.1, ts-api-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "ts-api-utils@npm:2.1.0" peerDependencies: typescript: ">=4.8.4" - checksum: 10/2e68938cd5acad6b5157744215ce10cd097f9f667fd36b5fdd5efdd4b0c51063e855459d835f94f6777bb8a0f334916b6eb5c1eedab8c325feb34baa39238898 + checksum: 10/02e55b49d9617c6eebf8aadfa08d3ca03ca0cd2f0586ad34117fdfc7aa3cd25d95051843fde9df86665ad907f99baed179e7a117b11021417f379e4d2614eacd languageName: node linkType: hard @@ -46686,6 +49458,18 @@ __metadata: languageName: node linkType: hard +"ts-graphviz@npm:^2.1.2": + version: 2.1.6 + resolution: "ts-graphviz@npm:2.1.6" + dependencies: + "@ts-graphviz/adapter": "npm:^2.0.6" + "@ts-graphviz/ast": "npm:^2.0.7" + "@ts-graphviz/common": "npm:^2.1.5" + "@ts-graphviz/core": "npm:^2.0.7" + checksum: 10/7490978263c2365b80b687964c13ad622a05b00e307c1ce448317a551fa1ee7fc03f37cd88d606ba23a030dd3baeb5b147d06d5af087b73133ac9276a7d65449 + languageName: node + linkType: hard + "ts-interface-checker@npm:^0.1.9": version: 0.1.13 resolution: "ts-interface-checker@npm:0.1.13" @@ -46965,6 +49749,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 + "type@npm:^1.0.1": version: 1.2.0 resolution: "type@npm:1.2.0" @@ -47069,19 +49864,19 @@ __metadata: linkType: hard "typedoc@npm:^0.28.0": - version: 0.28.4 - resolution: "typedoc@npm:0.28.4" + version: 0.28.7 + resolution: "typedoc@npm:0.28.7" dependencies: - "@gerrit0/mini-shiki": "npm:^3.2.2" + "@gerrit0/mini-shiki": "npm:^3.7.0" lunr: "npm:^2.3.9" markdown-it: "npm:^14.1.0" minimatch: "npm:^9.0.5" - yaml: "npm:^2.7.1" + yaml: "npm:^2.8.0" 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/0ccab46519e4be1aa9bd4defd55e920cbefe8825e0ba7ca93b8d6fd3867300e8b60260d2182033b46121f9040251366f6a496a3500da458c3031b224d7bdadd7 + checksum: 10/afe4868d9288c8456227d745955eeb37a0485b56657d4b690d32e2c99580983cf27109e197b5c652c747cfcdab81588c9ff3d1572e9d7d87fd068e8630715b18 languageName: node linkType: hard @@ -47122,6 +49917,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.7.3, typescript@npm:^5.8.3": + version: 5.8.3 + resolution: "typescript@npm:5.8.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10/65c40944c51b513b0172c6710ee62e951b70af6f75d5a5da745cb7fab132c09ae27ffdf7838996e3ed603bb015dadd099006658046941bd0ba30340cc563ae92 + languageName: node + linkType: hard + "typescript@npm:~5.5.0": version: 5.5.4 resolution: "typescript@npm:5.5.4" @@ -47152,6 +49957,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@npm%3A^5.7.3#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.8.3#optional!builtin<compat/typescript>": + version: 5.8.3 + resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>::version=5.8.3&hash=5786d5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10/b9b1e73dabac5dc730c041325dbd9c99467c1b0d239f1b74ec3b90d831384af3e2ba973946232df670519147eb51a2c20f6f96163cea2b359f03de1e2091cc4f + languageName: node + linkType: hard + "typescript@patch:typescript@npm%3A~5.5.0#optional!builtin<compat/typescript>": version: 5.5.4 resolution: "typescript@patch:typescript@npm%3A5.5.4#optional!builtin<compat/typescript>::version=5.5.4&hash=379a07" @@ -48152,6 +50967,13 @@ __metadata: languageName: node linkType: hard +"walkdir@npm:^0.4.1": + version: 0.4.1 + resolution: "walkdir@npm:0.4.1" + checksum: 10/54cbe7afc5fb811a55748b0bfa077a9a4aa43f568eb5857db9785af9728e1ad8b1ecf6b9ce6f14b405c6124939a92522e36aaa0397f3f52a9a7a08496f2eebe1 + languageName: node + linkType: hard + "walker@npm:^1.0.8": version: 1.0.8 resolution: "walker@npm:1.0.8" @@ -48274,53 +51096,9 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:5.2.0": - version: 5.2.0 - resolution: "webpack-dev-server@npm:5.2.0" - dependencies: - "@types/bonjour": "npm:^3.5.13" - "@types/connect-history-api-fallback": "npm:^1.5.4" - "@types/express": "npm:^4.17.21" - "@types/serve-index": "npm:^1.9.4" - "@types/serve-static": "npm:^1.15.5" - "@types/sockjs": "npm:^0.3.36" - "@types/ws": "npm:^8.5.10" - ansi-html-community: "npm:^0.0.8" - bonjour-service: "npm:^1.2.1" - chokidar: "npm:^3.6.0" - colorette: "npm:^2.0.10" - compression: "npm:^1.7.4" - connect-history-api-fallback: "npm:^2.0.0" - express: "npm:^4.21.2" - graceful-fs: "npm:^4.2.6" - http-proxy-middleware: "npm:^2.0.7" - ipaddr.js: "npm:^2.1.0" - launch-editor: "npm:^2.6.1" - open: "npm:^10.0.3" - p-retry: "npm:^6.2.0" - schema-utils: "npm:^4.2.0" - selfsigned: "npm:^2.4.1" - serve-index: "npm:^1.9.1" - sockjs: "npm:^0.3.24" - spdy: "npm:^4.0.2" - webpack-dev-middleware: "npm:^7.4.2" - ws: "npm:^8.18.0" - peerDependencies: - webpack: ^5.0.0 - peerDependenciesMeta: - webpack: - optional: true - webpack-cli: - optional: true - bin: - webpack-dev-server: bin/webpack-dev-server.js - checksum: 10/f93ca46b037e547a9db157db72ef98ab177659ad13a6e63302d87bd77b32e524dd7133f1ad18f5a51ec68712911c59be8d4e06aa7bcbe6f56a9e9ce3774cf7f6 - languageName: node - linkType: hard - -"webpack-dev-server@npm:^5.0.0": - version: 5.2.1 - resolution: "webpack-dev-server@npm:5.2.1" +"webpack-dev-server@npm:5.2.2, webpack-dev-server@npm:^5.0.0": + version: 5.2.2 + resolution: "webpack-dev-server@npm:5.2.2" dependencies: "@types/bonjour": "npm:^3.5.13" "@types/connect-history-api-fallback": "npm:^1.5.4" @@ -48338,7 +51116,7 @@ __metadata: connect-history-api-fallback: "npm:^2.0.0" express: "npm:^4.21.2" graceful-fs: "npm:^4.2.6" - http-proxy-middleware: "npm:^2.0.7" + http-proxy-middleware: "npm:^2.0.9" ipaddr.js: "npm:^2.1.0" launch-editor: "npm:^2.6.1" open: "npm:^10.0.3" @@ -48359,7 +51137,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: 10/424edfe22b7bbe2301a38b8b519dfeb7643e0ca643be01af3fa48ec18512955c1952246741d7577bdb911ee09dcd6c521ade7d65e0059448ee69ab02bfac4624 + checksum: 10/59517409cd38c01a875a03b9658f3d20d492b5b8bead9ded4a0f3d33e6857daf2d352fe89f0181dcaea6d0fbe84b0494cb4750a87120fe81cdbb3c32b499451c languageName: node linkType: hard @@ -48876,31 +51654,6 @@ __metadata: languageName: node linkType: hard -"xterm-addon-attach@npm:^0.9.0": - version: 0.9.0 - resolution: "xterm-addon-attach@npm:0.9.0" - peerDependencies: - xterm: ^5.0.0 - checksum: 10/70e5d3ecf139c04fae13c644b79c33858ef1a6e28dfe78f91dad3e34f5a155579029b87e91d1d016575acaf17f74e6c59402bde4bcff03461595bea0870f1ec1 - languageName: node - linkType: hard - -"xterm-addon-fit@npm:^0.8.0": - version: 0.8.0 - resolution: "xterm-addon-fit@npm:0.8.0" - peerDependencies: - xterm: ^5.0.0 - checksum: 10/5af2041b442f7c804eda2e6f62e3b68b5159b0ae6bd96e2aa8d85b26441df57291cbfed653d1196d4af5d9b94bfc39993df8b409a25c35e0d36bdaf6f5cdfe5f - languageName: node - linkType: hard - -"xterm@npm:^5.2.1, xterm@npm:^5.3.0": - version: 5.3.0 - resolution: "xterm@npm:5.3.0" - checksum: 10/3690b6a6d744f1d2932279975bb8e6c786e70c675531045016ecfe0f9b7957e2fc6811b815335f3e0e84b40ffae654f6ee4afe55a5768534744497e62252dd50 - languageName: node - linkType: hard - "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8" @@ -48958,12 +51711,12 @@ __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.7.0, yaml@npm:^2.7.1": - version: 2.7.1 - resolution: "yaml@npm:2.7.1" +"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.8.0": + version: 2.8.0 + resolution: "yaml@npm:2.8.0" bin: yaml: bin.mjs - checksum: 10/af57658d37c5efae4bac7204589b742ae01878a278554d632f01012868cf7fa66cba09b39140f12e7f6ceecc693ae52bcfb737596c4827e6e233338cb3a9528e + checksum: 10/7d4bd9c10d0e467601f496193f2ac254140f8e36f96f5ff7f852b9ce37974168eb7354f4c36dc8837dde527a2043d004b6aff48818ec24a69ab2dd3c6b6c381c languageName: node linkType: hard @@ -49020,8 +51773,9 @@ __metadata: "@backstage/cli-common": "workspace:^" "@backstage/release-manifests": "workspace:^" "@yarnpkg/builder": "npm:^4.2.1" - "@yarnpkg/core": "npm:^4.4.0" + "@yarnpkg/core": "npm:^4.4.1" "@yarnpkg/fslib": "npm:^3.1.2" + "@yarnpkg/plugin-essentials": "npm:^4.4.0" "@yarnpkg/plugin-npm": "patch:@yarnpkg/plugin-npm@npm%3A3.1.0#~/.yarn/patches/@yarnpkg-plugin-npm-npm-3.1.0-6533d0f5a1.patch" "@yarnpkg/plugin-pack": "npm:^4.0.1" fs-extra: "npm:^11.2.0" @@ -49168,12 +51922,12 @@ __metadata: languageName: node linkType: hard -"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4": - version: 3.23.5 - resolution: "zod-to-json-schema@npm:3.23.5" +"zod-to-json-schema@npm:^3.20.4, zod-to-json-schema@npm:^3.21.4, zod-to-json-schema@npm:^3.24.1": + version: 3.24.5 + resolution: "zod-to-json-schema@npm:3.24.5" peerDependencies: - zod: ^3.23.3 - checksum: 10/53d07a419f0f194e0b96711dc11e7e6fa52a366b0ed5fceb405dc55f13252a1bf433712e4fb496c2a5fdc851018ee1acba7b39c2265c43d6fbb180e12c110c3b + zod: ^3.24.1 + checksum: 10/1af291b4c429945c9568c2e924bdb7c66ab8d139cbeb9a99b6e9fc9e1b02863f85d07759b9303714f07ceda3993dcaf0ebcb80d2c18bb2aaf5502b2c1016affd languageName: node linkType: hard @@ -49186,10 +51940,10 @@ __metadata: languageName: node linkType: hard -"zod@npm:^3.22.4": - version: 3.24.4 - resolution: "zod@npm:3.24.4" - checksum: 10/3d545792fa54bb27ee5dbc34a5709e81f603185fcc94c8204b5d95c20dc4c81d870ff9c51f3884a30ef05cdc601449f4c4df254ac4783f0827b1faed7c1cdb48 +"zod@npm:^3.22.4, zod@npm:^3.23.8": + version: 3.25.67 + resolution: "zod@npm:3.25.67" + checksum: 10/0e35432dcca7f053e63f5dd491a87c78abe0d981817547252c3b6d05f0f58788695d1a69724759c6501dff3fd62929be24c9f314a3625179bee889150f7a61fa languageName: node linkType: hard