diff --git a/.changeset/blue-bags-warn.md b/.changeset/blue-bags-warn.md new file mode 100644 index 0000000000..17fa5e89d6 --- /dev/null +++ b/.changeset/blue-bags-warn.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-catalog-node': minor +--- + +Support adding location analyzers in new catalog analysis extension point and move `AnalyzeOptions` and `ScmLocationAnalyzer` types to `@backstage/plugin-catalog-node` diff --git a/.changeset/chilly-books-sneeze.md b/.changeset/chilly-books-sneeze.md new file mode 100644 index 0000000000..a2d3ca1e04 --- /dev/null +++ b/.changeset/chilly-books-sneeze.md @@ -0,0 +1,6 @@ +--- +'@backstage/cli-node': minor +'@backstage/cli': minor +--- + +Removed support for the `publishConfig.alphaTypes` and `.betaTypes` fields that were used together with `--experimental-type-build` to generate `/alpha` and `/beta` entry points. Use the `exports` field to achieve this instead. diff --git a/.changeset/clever-houses-scream.md b/.changeset/clever-houses-scream.md new file mode 100644 index 0000000000..14a2427dea --- /dev/null +++ b/.changeset/clever-houses-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-node': patch +--- + +Switch to `@smithy/node-http-handler` instead of the `@aws-sdk/node-http-handler` diff --git a/.changeset/curvy-carrots-thank.md b/.changeset/curvy-carrots-thank.md new file mode 100644 index 0000000000..6303125314 --- /dev/null +++ b/.changeset/curvy-carrots-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Fix bug where `retrieveAll` method wasn't fetching visits diff --git a/.changeset/dry-days-invite.md b/.changeset/dry-days-invite.md new file mode 100644 index 0000000000..dcf052acd2 --- /dev/null +++ b/.changeset/dry-days-invite.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Switch from using deprecated `@esbuild-kit/*` packages to using `tsx`. This also switches to using the new module loader `register` API when available, avoiding the experimental warning when starting backends. diff --git a/.changeset/fast-bears-lick.md b/.changeset/fast-bears-lick.md new file mode 100644 index 0000000000..8e0dc8e60b --- /dev/null +++ b/.changeset/fast-bears-lick.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Migrate catalog entity cards to new frontend system extension format. diff --git a/.changeset/four-files-behave.md b/.changeset/four-files-behave.md new file mode 100644 index 0000000000..0430bd98d8 --- /dev/null +++ b/.changeset/four-files-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-graphiql': minor +--- + +Upgrade to GraphiQL to 3.0.6 diff --git a/.changeset/four-pears-swim.md b/.changeset/four-pears-swim.md new file mode 100644 index 0000000000..b091dad598 --- /dev/null +++ b/.changeset/four-pears-swim.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-vault-node': minor +--- + +Initial version of the `plugin-vault-node`` package. It contains the extension point definitions +for the vault backend, as well as some types that will be deprecated in the backend plugin. diff --git a/.changeset/fresh-crews-promise.md b/.changeset/fresh-crews-promise.md new file mode 100644 index 0000000000..fc9859086a --- /dev/null +++ b/.changeset/fresh-crews-promise.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +'@backstage/plugin-techdocs': patch +--- + +Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. diff --git a/.changeset/fresh-schools-thank.md b/.changeset/fresh-schools-thank.md new file mode 100644 index 0000000000..1e32f82051 --- /dev/null +++ b/.changeset/fresh-schools-thank.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-gitlab': patch +--- + +Added try catch around fetching gitlab group users to prevent refresh from failing completely while only a select number of groups might not be able to load correctly. diff --git a/.changeset/gentle-pears-camp.md b/.changeset/gentle-pears-camp.md new file mode 100644 index 0000000000..6d96958a76 --- /dev/null +++ b/.changeset/gentle-pears-camp.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-backstage-openapi': minor +--- + +Adds a new catalog module for ingesting Backstage plugin OpenAPI specs into the catalog for display as an API entity. diff --git a/.changeset/gentle-pumpkins-carry.md b/.changeset/gentle-pumpkins-carry.md new file mode 100644 index 0000000000..721fbe8195 --- /dev/null +++ b/.changeset/gentle-pumpkins-carry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +--- + +Added support for specifying a domain hint on the Microsoft authentication provider configuration. diff --git a/.changeset/good-plums-confess.md b/.changeset/good-plums-confess.md new file mode 100644 index 0000000000..941adec8b3 --- /dev/null +++ b/.changeset/good-plums-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Refactor internal extension instance system into an app graph. diff --git a/.changeset/great-baboons-allow.md b/.changeset/great-baboons-allow.md new file mode 100644 index 0000000000..47e410d8a4 --- /dev/null +++ b/.changeset/great-baboons-allow.md @@ -0,0 +1,33 @@ +--- +'@backstage/plugin-vault-backend': minor +--- + +Added support for the [new backend system](https://backstage.io/docs/backend-system/). + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + import { createBackend } from '@backstage/backend-defaults'; + const backend = createBackend(); + // ... other feature additions ++ backend.add(import('@backstage/plugin-vault-backend'); + backend.start(); +``` + +If you use the new backend system, the token renewal task can be defined via configuration file: + +```diff +vault: + baseUrl: + token: + schedule: ++ frequency: ... ++ timeout: ... ++ # Other schedule options, such as scope or initialDelay +``` + +If the `schedule` is omitted or set to `false` no token renewal task will be scheduled. +If the value of `schedule` is set to `true` the renew will be scheduled hourly (the default). +In other cases (like in the diff above), the defined schedule will be used. + +**DEPRECATIONS**: The interface `VaultApi` and the type `VaultSecret` are now deprecated. Import them from `@backstage/plugin-vault-node`. diff --git a/.changeset/heavy-experts-accept.md b/.changeset/heavy-experts-accept.md new file mode 100644 index 0000000000..e6f2f435b8 --- /dev/null +++ b/.changeset/heavy-experts-accept.md @@ -0,0 +1,17 @@ +--- +'@backstage/plugin-catalog-react': minor +--- + +Added an `EntityPresentationApi` and associated `entityPresentationApiRef`. This +API lets you control how references to entities (e.g. in links, headings, +iconography etc) are represented in the user interface. + +Usage of this API is initially added to the `EntityRefLink` and `EntityRefLinks` +components, so that they can render richer, more correct representation of +entity refs. There's also a new `EntityDisplayName` component, which works just like +the `EntityRefLink` but without the link. + +Along with that change, the `fetchEntities` and `getTitle` props of +`EntityRefLinksProps` are deprecated and no longer used, since the same need +instead is fulfilled (and by default always enabled) by the +`entityPresentationApiRef`. diff --git a/.changeset/heavy-rings-play.md b/.changeset/heavy-rings-play.md new file mode 100644 index 0000000000..bbed4c3fa3 --- /dev/null +++ b/.changeset/heavy-rings-play.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-bazaar': patch +--- + +Adding descending sort in a bazaar plugin diff --git a/.changeset/hip-moose-boil.md b/.changeset/hip-moose-boil.md new file mode 100644 index 0000000000..e14c557c62 --- /dev/null +++ b/.changeset/hip-moose-boil.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-kubernetes-react': patch +--- + +Internal refactor to avoid a null pointer problem diff --git a/.changeset/large-forks-arrive.md b/.changeset/large-forks-arrive.md new file mode 100644 index 0000000000..3490a87128 --- /dev/null +++ b/.changeset/large-forks-arrive.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage-backend': patch +--- + +Added support for new backend system diff --git a/.changeset/late-forks-fetch.md b/.changeset/late-forks-fetch.md new file mode 100644 index 0000000000..3ba15885e2 --- /dev/null +++ b/.changeset/late-forks-fetch.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-backend': patch +--- + +Fix potential memory leak by not creating a build log transport if not given via `RouterOptions`. diff --git a/.changeset/long-turkeys-argue.md b/.changeset/long-turkeys-argue.md new file mode 100644 index 0000000000..f051d42478 --- /dev/null +++ b/.changeset/long-turkeys-argue.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add examples for `github:environment:create` scaffolder action & improve related tests diff --git a/.changeset/loud-ghosts-deny.md b/.changeset/loud-ghosts-deny.md new file mode 100644 index 0000000000..e66b052c4e --- /dev/null +++ b/.changeset/loud-ghosts-deny.md @@ -0,0 +1,16 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Introduce a new optional config parameter `catalog.stitchingStrategy.mode`, +which can have the values `'immediate'` (default) and `'deferred'`. The default +is for stitching to work as it did before this change, which means that it +happens "in-band" (blocking) immediately when each processing task finishes. +When set to `'deferred'`, stitching is instead deferred to happen on a separate +asynchronous worker queue just like processing. + +Deferred stitching should make performance smoother when ingesting large amounts +of entities, and reduce p99 processing times and repeated over-stitching of +hot spot entities when fan-out/fan-in in terms of relations is very large. It +does however also come with some performance cost due to the queuing with how +much wall-clock time some types of task take. diff --git a/.changeset/many-masks-smoke.md b/.changeset/many-masks-smoke.md new file mode 100644 index 0000000000..6d249eeb15 --- /dev/null +++ b/.changeset/many-masks-smoke.md @@ -0,0 +1,6 @@ +--- +'@backstage/repo-tools': minor +'@backstage/cli': minor +--- + +Remove support for the deprecated `--experimental-type-build` option for `package build`. diff --git a/.changeset/mighty-crews-attack.md b/.changeset/mighty-crews-attack.md new file mode 100644 index 0000000000..c0b8587fe0 --- /dev/null +++ b/.changeset/mighty-crews-attack.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Initial entity page implementation for new frontend system at `/alpha`, with an overview page enabled by default and the about card available as an optional card. diff --git a/.changeset/nasty-colts-tickle.md b/.changeset/nasty-colts-tickle.md new file mode 100644 index 0000000000..5a6194c14d --- /dev/null +++ b/.changeset/nasty-colts-tickle.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-search-backend': patch +--- + +Update the OpenAPI spec with more complete error responses and request bodies using Optic. Also, updates the test cases to use the new `supertest` pass through from `@backstage/backend-openapi-utils`. diff --git a/.changeset/nice-apes-kneel.md b/.changeset/nice-apes-kneel.md new file mode 100644 index 0000000000..1ecff9d223 --- /dev/null +++ b/.changeset/nice-apes-kneel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +Added entity page content for the new plugin exported via `/alpha`. diff --git a/.changeset/old-apricots-taste.md b/.changeset/old-apricots-taste.md new file mode 100644 index 0000000000..544ba11536 --- /dev/null +++ b/.changeset/old-apricots-taste.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Added new APIs at the `/alpha` subpath for creating entity page cards and content for the new frontend system. diff --git a/.changeset/old-chairs-switch.md b/.changeset/old-chairs-switch.md new file mode 100644 index 0000000000..2e9251e06f --- /dev/null +++ b/.changeset/old-chairs-switch.md @@ -0,0 +1,7 @@ +--- +'@backstage/repo-tools': minor +--- + +Adds a new command `schema openapi test` that performs runtime validation of your OpenAPI specs using your test data. Under the hood, we're using Optic to perform this check, really cool work by them! + +To use this new command, you will have to run `yarn add @useoptic/optic` in the root of your repo. diff --git a/.changeset/orange-planes-crash.md b/.changeset/orange-planes-crash.md new file mode 100644 index 0000000000..a8e828e912 --- /dev/null +++ b/.changeset/orange-planes-crash.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-bazaar': patch +--- + +Added alert popup in the bazaar plugin diff --git a/.changeset/plenty-buckets-float.md b/.changeset/plenty-buckets-float.md new file mode 100644 index 0000000000..c1dbb54e87 --- /dev/null +++ b/.changeset/plenty-buckets-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-bazaar': patch +--- + +Removed unnecessary dependency on `@backstage/cli`. diff --git a/.changeset/poor-seahorses-rush.md b/.changeset/poor-seahorses-rush.md new file mode 100644 index 0000000000..55313f706d --- /dev/null +++ b/.changeset/poor-seahorses-rush.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Consolidated getting the annotation values into a single function to help with future changes diff --git a/.changeset/pre.json b/.changeset/pre.json index 4a9428d0cd..eb4c8454ac 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -250,7 +250,96 @@ "@backstage/plugin-user-settings-backend": "0.2.4", "@backstage/plugin-vault": "0.1.20", "@backstage/plugin-vault-backend": "0.3.11", - "@backstage/plugin-xcmetrics": "0.2.44" + "@backstage/plugin-xcmetrics": "0.2.44", + "@backstage/core-compat-api": "0.0.0", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "0.0.0", + "@backstage/plugin-search-backend-module-stack-overflow-collator": "0.0.0", + "@backstage/plugin-vault-node": "0.0.0" }, - "changesets": [] + "changesets": [ + "chatty-cobras-cheer", + "chilled-knives-rule", + "chilly-books-sneeze", + "chilly-terms-behave", + "dirty-ducks-behave", + "dry-days-invite", + "eighty-chairs-camp", + "fair-parrots-lick", + "fast-bears-lick", + "fifty-taxis-allow", + "flat-ducks-buy", + "fluffy-years-shake", + "four-files-behave", + "four-pears-swim", + "fresh-crews-promise", + "fresh-schools-thank", + "gentle-pears-camp", + "giant-cars-walk", + "giant-cycles-end", + "good-plums-confess", + "great-baboons-allow", + "healthy-shirts-fold", + "heavy-experts-accept", + "hip-mugs-camp", + "hungry-paws-dress", + "hungry-radios-play", + "large-forks-arrive", + "lazy-hotels-wait", + "long-turkeys-argue", + "loud-ghosts-deny", + "many-masks-smoke", + "mighty-crews-attack", + "modern-ducks-battle", + "nasty-colts-tickle", + "new-beers-drive", + "nice-apes-kneel", + "nice-pillows-poke", + "ninety-numbers-study", + "old-apricots-taste", + "old-chairs-switch", + "olive-paws-divide", + "plenty-tigers-argue", + "poor-seahorses-rush", + "popular-bikes-do", + "pretty-swans-worry", + "quick-pumpkins-shave", + "quick-roses-move", + "real-carrots-brake", + "real-jars-yawn", + "red-beers-roll", + "red-yaks-press", + "rich-pugs-chew", + "rude-tomatoes-itch", + "shaggy-beers-collect", + "shaggy-buses-beg", + "sharp-falcons-clean", + "shiny-geese-watch", + "sixty-tips-argue", + "small-buckets-roll", + "smart-dancers-watch", + "sour-toes-joke", + "stale-rice-count", + "strange-queens-deliver", + "strong-taxis-wait", + "sweet-buckets-fry", + "sweet-countries-share", + "swift-mice-care", + "tall-colts-roll", + "tender-lies-wonder", + "tender-maps-type", + "thick-dolphins-boil", + "thirty-stingrays-grin", + "three-moles-mix", + "tidy-camels-boil", + "tricky-cups-hammer", + "tricky-vans-behave", + "violet-lamps-appear", + "wet-cows-brake", + "wicked-ties-knock", + "wild-cows-watch", + "wild-geese-occur", + "wise-waves-approve", + "wise-weeks-design", + "young-days-talk" + ] } diff --git a/.changeset/real-apes-build.md b/.changeset/real-apes-build.md new file mode 100644 index 0000000000..5481bebf3a --- /dev/null +++ b/.changeset/real-apes-build.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-microsoft-provider': patch +--- + +Correctly mark the client secret in configuration as secret diff --git a/.changeset/real-carrots-brake.md b/.changeset/real-carrots-brake.md new file mode 100644 index 0000000000..e9ae1f0c27 --- /dev/null +++ b/.changeset/real-carrots-brake.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Fixed a rare occurrence where a race in the search bar could throw away user input or cause the clear button not to work. diff --git a/.changeset/real-jars-yawn.md b/.changeset/real-jars-yawn.md new file mode 100644 index 0000000000..a89a687971 --- /dev/null +++ b/.changeset/real-jars-yawn.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +If create app installs dependencies, don't suggest to user that they also need to do it. diff --git a/.changeset/red-beers-roll.md b/.changeset/red-beers-roll.md new file mode 100644 index 0000000000..3c061ce377 --- /dev/null +++ b/.changeset/red-beers-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-techdocs': patch +--- + +The process of adding or modifying fields in the techdocs search index has been simplified. For more details, see [How to customize fields in the Software Catalog or TechDocs index](https://backstage.io/docs/features/search/how-to-guides.md#how-to-customize-fields-in-the-software-catalog-or-techdocs-index). diff --git a/.changeset/red-yaks-press.md b/.changeset/red-yaks-press.md new file mode 100644 index 0000000000..7166ac345e --- /dev/null +++ b/.changeset/red-yaks-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-app-api': patch +--- + +Add current and default scopes when refreshing session diff --git a/.changeset/rich-pugs-chew.md b/.changeset/rich-pugs-chew.md new file mode 100644 index 0000000000..6c5aa0af62 --- /dev/null +++ b/.changeset/rich-pugs-chew.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +The experimental package detection will now ignore packages that don't make `package.json` available. diff --git a/.changeset/rude-penguins-press.md b/.changeset/rude-penguins-press.md new file mode 100644 index 0000000000..967b04bc19 --- /dev/null +++ b/.changeset/rude-penguins-press.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Title and description in RepoUrlPicker are now correctly displayed. diff --git a/.changeset/shaggy-buses-beg.md b/.changeset/shaggy-buses-beg.md new file mode 100644 index 0000000000..d2c9351b5e --- /dev/null +++ b/.changeset/shaggy-buses-beg.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-react': minor +--- + +The `UserListPicker` component has undergone improvements to enhance its performance. + +The previous implementation inferred the number of owned and starred entities based on the entities available in the `EntityListContext`. The updated version no longer relies on the `EntityListContext` for inference, allowing for better decoupling. + +The component now loads the entities' count asynchronously, resulting in improved performance and responsiveness. For this purpose, some of the exported filters such as `EntityTagFilter`, `EntityOwnerFilter`, `EntityLifecycleFilter` and `EntityNamespaceFilter` have now the `getCatalogFilters` method implemented. diff --git a/.changeset/sharp-chefs-attend.md b/.changeset/sharp-chefs-attend.md new file mode 100644 index 0000000000..2334344928 --- /dev/null +++ b/.changeset/sharp-chefs-attend.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-test-utils': patch +--- + +Ensure recursive deletion of temporary directories in tests diff --git a/.changeset/silver-kiwis-float.md b/.changeset/silver-kiwis-float.md new file mode 100644 index 0000000000..6a45dfb1a6 --- /dev/null +++ b/.changeset/silver-kiwis-float.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +Extensions now return their output from the factory function rather than calling `bind(...)`. diff --git a/.changeset/six-books-arrive.md b/.changeset/six-books-arrive.md new file mode 100644 index 0000000000..785424f635 --- /dev/null +++ b/.changeset/six-books-arrive.md @@ -0,0 +1,23 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +'@backstage/plugin-microsoft-calendar': patch +'@backstage/plugin-scaffolder-react': patch +'@backstage/plugin-catalog-graph': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-tech-insights': patch +'@backstage/plugin-code-climate': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-gcalendar': patch +'@backstage/plugin-org-react': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-xcmetrics': patch +'@backstage/plugin-api-docs': patch +'@backstage/plugin-playlist': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-explore': patch +'@backstage/plugin-bazaar': patch +'@backstage/plugin-search': patch +'@backstage/plugin-org': patch +--- + +Internal theme type updates diff --git a/.changeset/small-buckets-roll.md b/.changeset/small-buckets-roll.md new file mode 100644 index 0000000000..5cd9b23678 --- /dev/null +++ b/.changeset/small-buckets-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Temporarily pin the `react-grid-layout` sub-dependency to version `1.3.4` while the horizontal resizing of the latest version is not fixed. For more details, see [#20712](https://github.com/backstage/backstage/issues/20712). diff --git a/.changeset/smart-dancers-watch.md b/.changeset/smart-dancers-watch.md new file mode 100644 index 0000000000..b47a6ae5c2 --- /dev/null +++ b/.changeset/smart-dancers-watch.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-plugin-api': minor +--- + +`IconComponent` can now have a `fontSize` of `inherit`, which is useful for in-line icons. diff --git a/.changeset/sour-toes-joke.md b/.changeset/sour-toes-joke.md new file mode 100644 index 0000000000..4e380f4ded --- /dev/null +++ b/.changeset/sour-toes-joke.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-user-settings-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-periskop-backend': patch +'@backstage/plugin-scaffolder-node': patch +'@backstage/plugin-bazaar-backend': patch +'@backstage/plugin-kafka-backend': patch +'@backstage/plugin-proxy-backend': patch +'@backstage/plugin-sonarqube': patch +'@backstage/plugin-todo': patch +--- + +Switched to using `"exports"` field for `/alpha` subpath export. diff --git a/.changeset/strange-gifts-try.md b/.changeset/strange-gifts-try.md new file mode 100644 index 0000000000..7f9aa89ad4 --- /dev/null +++ b/.changeset/strange-gifts-try.md @@ -0,0 +1,9 @@ +--- +'@backstage/frontend-app-api': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-catalog-react': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-search-react': patch +--- + +Internal updates to match changes in the experimental `@backstage/frontend-plugin-api`. diff --git a/.changeset/strange-queens-deliver.md b/.changeset/strange-queens-deliver.md new file mode 100644 index 0000000000..ed1d4d0b5b --- /dev/null +++ b/.changeset/strange-queens-deliver.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Reverted the Microsoft auth provider to the previous implementation. diff --git a/.changeset/strong-taxis-wait.md b/.changeset/strong-taxis-wait.md new file mode 100644 index 0000000000..13cfa9a1ba --- /dev/null +++ b/.changeset/strong-taxis-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +Added a workaround for double `default` wrapping when dynamically importing CommonJS modules with default exports. diff --git a/.changeset/swift-mice-care.md b/.changeset/swift-mice-care.md new file mode 100644 index 0000000000..1d0d7d8703 --- /dev/null +++ b/.changeset/swift-mice-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Added `EXPERIMENTAL_VITE` flag for using [vite](https://vitejs.dev) as dev server instead of Webpack diff --git a/.changeset/tall-colts-roll.md b/.changeset/tall-colts-roll.md new file mode 100644 index 0000000000..914a09cdac --- /dev/null +++ b/.changeset/tall-colts-roll.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Allow setting `update: true` in `publish:github:pull-request` scaffolder action diff --git a/.changeset/tame-spies-hunt.md b/.changeset/tame-spies-hunt.md new file mode 100644 index 0000000000..5a45defb7e --- /dev/null +++ b/.changeset/tame-spies-hunt.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add examples for `github:deployKey:create` scaffolder action & improve related tests diff --git a/.changeset/tender-lies-wonder.md b/.changeset/tender-lies-wonder.md new file mode 100644 index 0000000000..43124bb41c --- /dev/null +++ b/.changeset/tender-lies-wonder.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': minor +--- + +Adds the StaticTokenIssuer and StaticKeyStore, an alternative token issuer that can be used to sign the Authorization header using a predefined public/private key pair. diff --git a/.changeset/thirty-stingrays-grin.md b/.changeset/thirty-stingrays-grin.md new file mode 100644 index 0000000000..6dcb4bbd50 --- /dev/null +++ b/.changeset/thirty-stingrays-grin.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-openapi-utils': minor +--- + +Adds a new route, `/openapi.json` to validated routers for displaying their full OpenAPI spec in a standard endpoint. diff --git a/.changeset/tidy-planets-trade.md b/.changeset/tidy-planets-trade.md new file mode 100644 index 0000000000..49c1643e55 --- /dev/null +++ b/.changeset/tidy-planets-trade.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-scaffolder-common': patch +'@backstage/plugin-scaffolder-react': patch +--- + +Make it possible to define control buttons text (Back, Create, Review) per template diff --git a/.changeset/tricky-vans-behave.md b/.changeset/tricky-vans-behave.md new file mode 100644 index 0000000000..89592e5e8f --- /dev/null +++ b/.changeset/tricky-vans-behave.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-openapi-utils': patch +--- + +Adds a new function `wrapInOpenApiTestServer` that allows for proxied requests at runtime. This will support the new `yarn backstage-repo-tools schema openapi test` command. diff --git a/.changeset/two-jars-melt.md b/.changeset/two-jars-melt.md new file mode 100644 index 0000000000..2789157065 --- /dev/null +++ b/.changeset/two-jars-melt.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-techdocs-backend': minor +'@backstage/plugin-techdocs-node': minor +--- + +Expose an extension point to set a custom build strategy. Also move `DocsBuildStrategy` type to `@backstage/plugin-techdocs-node` and deprecate `ShouldBuildParameters` type. diff --git a/.changeset/unlucky-houses-end.md b/.changeset/unlucky-houses-end.md new file mode 100644 index 0000000000..48f5849002 --- /dev/null +++ b/.changeset/unlucky-houses-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-github': patch +--- + +Import `AnalyzeOptions` and `ScmLocationAnalyzer` types from `@backstage/plugin-catalog-node` diff --git a/.changeset/weak-zebras-cover.md b/.changeset/weak-zebras-cover.md new file mode 100644 index 0000000000..f3290683e2 --- /dev/null +++ b/.changeset/weak-zebras-cover.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-react': patch +--- + +Minor refactor of search bar analytics capture diff --git a/.changeset/wicked-ties-knock.md b/.changeset/wicked-ties-knock.md new file mode 100644 index 0000000000..582cb905d0 --- /dev/null +++ b/.changeset/wicked-ties-knock.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-gitlab': patch +--- + +Add examples for `gitlab:projectAccessToken:create` scaffolder action & improve related tests diff --git a/.changeset/wild-cows-watch.md b/.changeset/wild-cows-watch.md new file mode 100644 index 0000000000..770fb1a692 --- /dev/null +++ b/.changeset/wild-cows-watch.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-catalog': minor +--- + +Added the `DefaultEntityPresentationApi`, which is an implementation of the +`EntityPresentationApi` that `@backstage/plugin-catalog-react` exposes through +its `entityPresentationApiRef`. This implementation is also by default made +available automatically by the catalog plugin, unless you replace it with a +custom one. It batch fetches and caches data from the catalog as needed for +display, and is customizable by adopters to add their own rendering functions. diff --git a/.changeset/wise-waves-approve.md b/.changeset/wise-waves-approve.md new file mode 100644 index 0000000000..f543f8918c --- /dev/null +++ b/.changeset/wise-waves-approve.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Ignore `stdin` when spawning backend child process for the `start` command. Fixing an issue where backend startup would hang. diff --git a/.changeset/wise-weeks-design.md b/.changeset/wise-weeks-design.md new file mode 100644 index 0000000000..657a8e23d5 --- /dev/null +++ b/.changeset/wise-weeks-design.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add examples for `publish:github:pull-request` scaffolder action & improve related tests diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 92f2d57885..d10106e47f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -29,6 +29,7 @@ yarn.lock @backstage/maintainers @backst /plugins/catalog-* @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers /plugins/catalog-backend-module-aws @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann /plugins/catalog-backend-module-bitbucket-cloud @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann +/plugins/catalog-backend-module-backstage-openapi @backstage/maintainers @backstage/reviewers @backstage/openapi-tooling-maintainers /plugins/catalog-backend-module-msgraph @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @pjungermann /plugins/catalog-backend-module-puppetdb @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers /plugins/catalog-graph @backstage/maintainers @backstage/reviewers @backstage/catalog-maintainers @backstage/sda-se-reviewers diff --git a/.github/vale/Vocab/Backstage/accept.txt b/.github/vale/Vocab/Backstage/accept.txt index 0e86ef5fc4..9020dc2097 100644 --- a/.github/vale/Vocab/Backstage/accept.txt +++ b/.github/vale/Vocab/Backstage/accept.txt @@ -258,6 +258,7 @@ onboarding Onboarding OpenSearch OpenShift +openssl orgs padding paddings @@ -291,6 +292,7 @@ Preprarer productional Protobuf proxying +proxied Proxying pseudonymized pubsub @@ -430,6 +432,7 @@ Valentina validator validators varchar +vite VMware Vodafone VPCs diff --git a/.github/workflows/automate_changeset_feedback.yml b/.github/workflows/automate_changeset_feedback.yml index 483d59dc8c..56aa3f58f8 100644 --- a/.github/workflows/automate_changeset_feedback.yml +++ b/.github/workflows/automate_changeset_feedback.yml @@ -27,14 +27,14 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 with: # Fetch the commit that's merged into the base rather than the target ref # This will let us diff only the contents of the PR, without fetching more history ref: 'refs/pull/${{ github.event.pull_request.number }}/merge' - name: fetch base run: git fetch --depth 1 origin ${{ github.base_ref }} - - uses: backstage/actions/changeset-feedback@v0.6.4 + - uses: backstage/actions/changeset-feedback@v0.6.5 name: Generate feedback with: diff-ref: 'origin/master' diff --git a/.github/workflows/automate_merge_message.yml b/.github/workflows/automate_merge_message.yml index 4cbf6c69a2..c7a02db354 100644 --- a/.github/workflows/automate_merge_message.yml +++ b/.github/workflows/automate_merge_message.yml @@ -28,7 +28,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 with: ref: '${{ github.event.pull_request.merge_commit_sha }}' diff --git a/.github/workflows/automate_stale.yml b/.github/workflows/automate_stale.yml index 40ebdf8a3e..48ae943f69 100644 --- a/.github/workflows/automate_stale.yml +++ b/.github/workflows/automate_stale.yml @@ -19,7 +19,7 @@ jobs: with: egress-policy: audit - - uses: actions/stale@v7.0.0 + - uses: actions/stale@v8.0.0 id: stale with: stale-issue-message: > diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81ea6f96d2..b9be09a9ab 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,16 +31,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -67,16 +67,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -196,18 +196,18 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: fetch branch master run: git fetch origin master - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -222,6 +222,10 @@ jobs: BACKSTAGE_TEST_DATABASE_POSTGRES9_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres9.ports[5432] }} BACKSTAGE_TEST_DATABASE_MYSQL8_CONNECTION_STRING: mysql://root:root@localhost:${{ job.services.mysql8.ports[3306] }}/ignored + # We run the test cases before verifying the specs to prevent any failing tests from causing errors. + - name: verify openapi specs against test cases + run: yarn backstage-repo-tools schema openapi test + - name: ensure clean working directory run: | if files=$(git ls-files --exclude-standard --others --modified) && [[ -z "$files" ]]; then diff --git a/.github/workflows/cron.yml b/.github/workflows/cron.yml index 2874be512c..79cfe9d790 100644 --- a/.github/workflows/cron.yml +++ b/.github/workflows/cron.yml @@ -13,7 +13,7 @@ jobs: with: egress-policy: audit - - uses: backstage/actions/cron@v0.6.4 + - uses: backstage/actions/cron@v0.6.5 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/deploy_docker-image.yml b/.github/workflows/deploy_docker-image.yml index 30e7badcd0..57ed668627 100644 --- a/.github/workflows/deploy_docker-image.yml +++ b/.github/workflows/deploy_docker-image.yml @@ -19,19 +19,19 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: path: backstage ref: v${{ github.event.client_payload.version }} - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/deploy_microsite.yml b/.github/workflows/deploy_microsite.yml index f614db31ca..baa59dc977 100644 --- a/.github/workflows/deploy_microsite.yml +++ b/.github/workflows/deploy_microsite.yml @@ -28,10 +28,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/.github/workflows/deploy_nightly.yml b/.github/workflows/deploy_nightly.yml index d5fe9f5a64..e64fbe000a 100644 --- a/.github/workflows/deploy_nightly.yml +++ b/.github/workflows/deploy_nightly.yml @@ -19,15 +19,15 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@5e21ff4d9bc1a8cf6de233a3057d20ec6b3fb69d # v3.8.1 + uses: actions/setup-node@1a4442cacd436585916779262731d5b162bc6ec7 # v3.8.2 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@497722d45fb5a35c14c721f63ed74e6ad5f629dd # v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index f5ebfd7ee2..a9beee405f 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -60,15 +60,15 @@ jobs: INTEGRATION_TEST_AZURE_TOKEN: ${{ secrets.INTEGRATION_TEST_AZURE_TOKEN }} steps: - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} @@ -142,15 +142,15 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/issue.yaml b/.github/workflows/issue.yaml index 1c1c028f20..f5150a3a01 100644 --- a/.github/workflows/issue.yaml +++ b/.github/workflows/issue.yaml @@ -15,4 +15,4 @@ jobs: egress-policy: audit - name: Issue sync - uses: backstage/actions/issue-sync@v0.6.4 + uses: backstage/actions/issue-sync@v0.6.5 diff --git a/.github/workflows/pr-review-comment.yaml b/.github/workflows/pr-review-comment.yaml index e557c85d51..8d66567ff4 100644 --- a/.github/workflows/pr-review-comment.yaml +++ b/.github/workflows/pr-review-comment.yaml @@ -40,7 +40,7 @@ jobs: const prNumber = artifact.name.slice('pr_number-'.length) core.setOutput('pr-number', prNumber); - - uses: backstage/actions/re-review@v0.6.4 + - uses: backstage/actions/re-review@v0.6.5 with: app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} private-key: ${{ secrets.BACKSTAGE_GOALIE_PRIVATE_KEY }} diff --git a/.github/workflows/pr.yaml b/.github/workflows/pr.yaml index 8c99f105a9..89193c30ab 100644 --- a/.github/workflows/pr.yaml +++ b/.github/workflows/pr.yaml @@ -23,7 +23,7 @@ jobs: egress-policy: audit - name: PR sync - uses: backstage/actions/pr-sync@v0.6.4 + uses: backstage/actions/pr-sync@v0.6.5 with: github-token: ${{ secrets.GH_SERVICE_ACCOUNT_TOKEN }} app-id: ${{ secrets.BACKSTAGE_GOALIE_APPLICATION_ID }} diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index c9078f0086..3b017e759c 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -34,7 +34,7 @@ jobs: egress-policy: audit - name: 'Checkout code' - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: persist-credentials: false @@ -66,6 +66,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: 'Upload to code-scanning' - uses: github/codeql-action/upload-sarif@fdcae64e1484d349b3366718cdfef3d404390e85 # v2.22.1 + uses: github/codeql-action/upload-sarif@49abf0ba24d0b7953cb586944e918a0b92074c80 # v2.22.4 with: sarif_file: results.sarif diff --git a/.github/workflows/sync_code-formatting.yml b/.github/workflows/sync_code-formatting.yml index fa8d91f25a..bbb5ab702b 100644 --- a/.github/workflows/sync_code-formatting.yml +++ b/.github/workflows/sync_code-formatting.yml @@ -14,18 +14,18 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 with: # Fetch changes to previous commit - required for 'only_changed' in Prettier action fetch-depth: 0 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/sync_dependabot-changesets.yml b/.github/workflows/sync_dependabot-changesets.yml index 2ac6d7f0d0..40886015ca 100644 --- a/.github/workflows/sync_dependabot-changesets.yml +++ b/.github/workflows/sync_dependabot-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_release-manifest.yml b/.github/workflows/sync_release-manifest.yml index 46d5d5382a..1e6a2e4912 100644 --- a/.github/workflows/sync_release-manifest.yml +++ b/.github/workflows/sync_release-manifest.yml @@ -13,7 +13,7 @@ jobs: egress-policy: audit # Setup node & install deps before checkout, keeping install quick - - uses: actions/setup-node@v3.8.1 + - uses: actions/setup-node@v3.8.2 with: node-version: 18.x - name: Install dependencies @@ -21,7 +21,7 @@ jobs: run: npm install semver@7.3.5 fs-extra@10.0.0 @manypkg/get-packages@1.1.1 - name: Checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: path: backstage # 'v' prefix is added here for the tag, we keep it out of the manifest logic @@ -29,7 +29,7 @@ jobs: # Checkout backstage/versions into /backstage/versions, which is where store the output - name: Checkout versions - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: repository: backstage/versions path: backstage/versions diff --git a/.github/workflows/sync_renovate-changesets.yml b/.github/workflows/sync_renovate-changesets.yml index 738abcf773..88b1430784 100644 --- a/.github/workflows/sync_renovate-changesets.yml +++ b/.github/workflows/sync_renovate-changesets.yml @@ -16,7 +16,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: fetch-depth: 2 ref: ${{ github.head_ref }} diff --git a/.github/workflows/sync_snyk-github-issues.yml b/.github/workflows/sync_snyk-github-issues.yml index 690e2fb76e..80e59f048c 100644 --- a/.github/workflows/sync_snyk-github-issues.yml +++ b/.github/workflows/sync_snyk-github-issues.yml @@ -16,20 +16,20 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: 18.x registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x - name: Create Snyk report - uses: snyk/actions/node@299cde98a08ff8b1c2bfde1e5a067bce67a6d2b8 # master + uses: snyk/actions/node@39091e69b560da335383b404e50d65b408f4f812 # master continue-on-error: true # Snyk CLI exits with error when vulnerabilities are found with: args: > diff --git a/.github/workflows/sync_snyk-monitor.yml b/.github/workflows/sync_snyk-monitor.yml index 6450b1bd51..9268fc4975 100644 --- a/.github/workflows/sync_snyk-monitor.yml +++ b/.github/workflows/sync_snyk-monitor.yml @@ -29,9 +29,9 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: Monitor and Synchronize Snyk Policies - uses: snyk/actions/node@299cde98a08ff8b1c2bfde1e5a067bce67a6d2b8 # master + uses: snyk/actions/node@39091e69b560da335383b404e50d65b408f4f812 # master with: command: monitor args: > @@ -46,7 +46,7 @@ jobs: # Above we run the `monitor` command, this runs the `test` command which is # the one that generates the SARIF report that we can upload to GitHub. - name: Create Snyk report - uses: snyk/actions/node@299cde98a08ff8b1c2bfde1e5a067bce67a6d2b8 # master + uses: snyk/actions/node@39091e69b560da335383b404e50d65b408f4f812 # master continue-on-error: true # To make sure that SARIF upload gets called with: args: > @@ -58,6 +58,6 @@ jobs: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} NODE_OPTIONS: --max-old-space-size=7168 - name: Upload Snyk report - uses: github/codeql-action/upload-sarif@v2.21.8 + uses: github/codeql-action/upload-sarif@v2.22.4 with: sarif_file: snyk.sarif diff --git a/.github/workflows/sync_version-packages.yml b/.github/workflows/sync_version-packages.yml index 929fb9cc89..4ff6ce94f3 100644 --- a/.github/workflows/sync_version-packages.yml +++ b/.github/workflows/sync_version-packages.yml @@ -18,7 +18,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 with: fetch-depth: 20000 fetch-tags: true diff --git a/.github/workflows/uffizzi-build.yml b/.github/workflows/uffizzi-build.yml index 3107b87b8f..910cff0873 100644 --- a/.github/workflows/uffizzi-build.yml +++ b/.github/workflows/uffizzi-build.yml @@ -25,16 +25,16 @@ jobs: egress-policy: audit - name: checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 - name: setup-node - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: 18.x registry-url: https://registry.npmjs.org/ - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: linux-v18 @@ -87,7 +87,7 @@ jobs: egress-policy: audit - name: Checkout git repo - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 - name: Render Compose File run: | BACKSTAGE_IMAGE=$(echo ${{ needs.build-backstage.outputs.tags }}) diff --git a/.github/workflows/verify_accessibility.yml b/.github/workflows/verify_accessibility.yml index d53f3834d2..b367f4afb3 100644 --- a/.github/workflows/verify_accessibility.yml +++ b/.github/workflows/verify_accessibility.yml @@ -24,13 +24,13 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: Use Node.js 18.x - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: 18.x - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v18.x - name: run Lighthouse CI diff --git a/.github/workflows/verify_codeql.yml b/.github/workflows/verify_codeql.yml index 666bd5f0f4..59aa41ff55 100644 --- a/.github/workflows/verify_codeql.yml +++ b/.github/workflows/verify_codeql.yml @@ -47,7 +47,7 @@ jobs: egress-policy: audit - name: Checkout repository - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 with: # We must fetch at least the immediate parents so that if this is # a pull request then we can checkout the head. @@ -55,7 +55,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v2.21.8 + uses: github/codeql-action/init@v2.22.4 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. @@ -66,7 +66,7 @@ jobs: # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild - uses: github/codeql-action/autobuild@v2.21.8 + uses: github/codeql-action/autobuild@v2.22.4 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -80,4 +80,4 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2.21.8 + uses: github/codeql-action/analyze@v2.22.4 diff --git a/.github/workflows/verify_docs-quality.yml b/.github/workflows/verify_docs-quality.yml index ce457a234f..f1932d5afa 100644 --- a/.github/workflows/verify_docs-quality.yml +++ b/.github/workflows/verify_docs-quality.yml @@ -16,7 +16,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 # Vale does not support file excludes, so we use the script to generate a list of files instead # The action also does not allow args or a local config file to be passed in, so the files array diff --git a/.github/workflows/verify_e2e-kubernetes.yml b/.github/workflows/verify_e2e-kubernetes.yml index cddd6589d7..bd7eeb2ea7 100644 --- a/.github/workflows/verify_e2e-kubernetes.yml +++ b/.github/workflows/verify_e2e-kubernetes.yml @@ -26,16 +26,16 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-linux.yml b/.github/workflows/verify_e2e-linux.yml index 744a5d7e74..cf61463dd8 100644 --- a/.github/workflows/verify_e2e-linux.yml +++ b/.github/workflows/verify_e2e-linux.yml @@ -44,7 +44,7 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: Configure Git run: | @@ -52,12 +52,12 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_e2e-techdocs.yml b/.github/workflows/verify_e2e-techdocs.yml index 94f08a491f..96645c9b0b 100644 --- a/.github/workflows/verify_e2e-techdocs.yml +++ b/.github/workflows/verify_e2e-techdocs.yml @@ -34,8 +34,8 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 - - uses: actions/setup-python@v4.7.0 + - uses: actions/checkout@v4.1.1 + - uses: actions/setup-python@v4.7.1 with: python-version: '3.9' diff --git a/.github/workflows/verify_e2e-windows.yml b/.github/workflows/verify_e2e-windows.yml index 1385983c24..f0b1611fe1 100644 --- a/.github/workflows/verify_e2e-windows.yml +++ b/.github/workflows/verify_e2e-windows.yml @@ -42,7 +42,7 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: Configure Git run: | @@ -50,13 +50,13 @@ jobs: git config --global user.name 'GitHub e2e user' - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: setup python - uses: actions/setup-python@v4.7.0 + uses: actions/setup-python@v4.7.1 with: python-version: '3.10' @@ -78,7 +78,7 @@ jobs: uses: browser-actions/setup-chrome@803ef6dfb4fdf22089c9563225d95e4a515820a0 # latest - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} diff --git a/.github/workflows/verify_fossa.yml b/.github/workflows/verify_fossa.yml index 393bcf9631..7918efe333 100644 --- a/.github/workflows/verify_fossa.yml +++ b/.github/workflows/verify_fossa.yml @@ -19,7 +19,7 @@ jobs: egress-policy: audit - name: Checkout - uses: actions/checkout@v3.6.0 + uses: actions/checkout@v4.1.1 - name: Install Fossa run: "curl -H 'Cache-Control: no-cache' https://raw.githubusercontent.com/fossas/fossa-cli/master/install.sh | bash" diff --git a/.github/workflows/verify_microsite.yml b/.github/workflows/verify_microsite.yml index f0705bedc7..0a8d766717 100644 --- a/.github/workflows/verify_microsite.yml +++ b/.github/workflows/verify_microsite.yml @@ -28,10 +28,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js 18.x - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: 18.x diff --git a/.github/workflows/verify_storybook.yml b/.github/workflows/verify_storybook.yml index f0354df296..c428375a40 100644 --- a/.github/workflows/verify_storybook.yml +++ b/.github/workflows/verify_storybook.yml @@ -32,17 +32,17 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 with: fetch-depth: 0 # Required to retrieve git history - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth - name: yarn install - uses: backstage/actions/yarn-install@v0.6.4 + uses: backstage/actions/yarn-install@v0.6.5 with: cache-prefix: ${{ runner.os }}-v${{ matrix.node-version }} - name: storybook yarn install @@ -51,7 +51,7 @@ jobs: - run: yarn build-storybook - - uses: chromaui/action@807600692d28833b717c155e15ed20905cdc865c # v1 + - uses: chromaui/action@c7e9d129ad2b8e728e10522270e14596473a7958 # v1 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 f2c61938d8..59a06773b9 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -33,10 +33,10 @@ jobs: with: egress-policy: audit - - uses: actions/checkout@v3.6.0 + - uses: actions/checkout@v4.1.1 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3.8.1 + uses: actions/setup-node@v3.8.2 with: node-version: ${{ matrix.node-version }} registry-url: https://registry.npmjs.org/ # Needed for auth diff --git a/README.md b/README.md index 83f7512a07..1175b4320c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,10 @@ # [Backstage](https://backstage.io) +> đŸ–ī¸ From October 30th - November 5th, employees at Spotify will have a week to recharge, which means all core maintainers & some project area maintainers are out of office. Right after that, we will go into the week of BackstageCon + KubeCon in Chicago, hoping to see some of you there! Expect slower responses & reviews during the 2 weeks, but we're on it for urgent issues. We will be up to speed again on November 13th! đŸ–ī¸ + [![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) [![CNCF Status](https://img.shields.io/badge/cncf%20status-incubation-blue.svg)](https://www.cncf.io/projects) -[![Main CI Build](https://github.com/backstage/backstage/workflows/Main%20Master%20Build/badge.svg)](https://github.com/backstage/backstage/actions?query=workflow%3A%22Main+Master+Build%22) [![Discord](https://img.shields.io/discord/687207715902193673)](https://discord.gg/backstage-687207715902193673) ![Code style](https://img.shields.io/badge/code_style-prettier-ff69b4.svg) [![Codecov](https://img.shields.io/codecov/c/github/backstage/backstage)](https://codecov.io/gh/backstage/backstage) diff --git a/REVIEWING.md b/REVIEWING.md index 43085090a8..7ed3fcda57 100644 --- a/REVIEWING.md +++ b/REVIEWING.md @@ -172,13 +172,7 @@ We generate API Reports using the [API Extractor](https://api-extractor.com/) to Each API report contains a list of all the exported types of each package. As long as the API report does not have any warnings it will contain the full publicly facing API of the package, meaning you do not need to consider any other changes to the package from the point of view of TypeScript API stability. -Exported types can be marked with either `@public`, `@alpha` or `@beta` release tags. It is only the `@public` exports that we consider to be part of the stable API. The `@alpha` and `@beta` exports are considered unstable and can be changed at any time without needing a breaking package versions bump. However, this **ONLY** applies if the package has been configured to use experimental type builds, which looks like this in `package.json`: - -```json - "build": "backstage-cli package build --experimental-type-build" -``` - -If a package does not have this configuration, then all exported types are considered stable, even if they are marked as `@alpha` or `@beta`. +Exported types can be marked with either `@public`, `@alpha` or `@beta` release tags. It is only the `@public` exports that we consider to be part of the stable API. The `@alpha` and `@beta` exports are considered unstable and can be changed at any time without needing a breaking package versions bump. #### Changes that are Not Considered Breaking diff --git a/app-config.yaml b/app-config.yaml index a2ee232516..c53a6475f4 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -235,6 +235,12 @@ catalog: - System - Domain - Location + providers: + openapi: + plugins: + - catalog + - search + - todo processors: ldapOrg: diff --git a/docs/auth/index.md b/docs/auth/index.md index a670e69baa..091c8b01a0 100644 --- a/docs/auth/index.md +++ b/docs/auth/index.md @@ -406,3 +406,50 @@ providerFactories: { ghe: providers.github.create(), }, ``` + +## Configuring token issuers + +By default, the Backstage authentication backend generates and manages its own signing keys automatically for any issued +Backstage tokens. However, these keys have a short lifetime and do not persist after instance restarts. + +Alternatively, users can provide their own public and private key files to sign issued tokens. This is beneficial in +scenarios where the token verification implementation aggressively caches the list of keys, and doesn't attempt to fetch +new ones even if they encounter an unknown key id. To enable this feature add the following configuration to your config +file: + +```yaml +auth: + keyStore: + provider: 'static' + static: + keys: + # Must be declared at least once and the first one will be used for signing + - keyId: 'primary' + publicKeyFile: /path/to/public.key + privateKeyFile: /path/to/private.key + algorithm: # Optional, algorithm used to generate the keys, defaults to ES256 + # More keys can be added so with future key rotations caches already know about it + - keyId: ... +``` + +The private key should be stored in the PKCS#8 format. The public key should be stored in the SPKI format. +You can generate the public/private key pair, using openssl and the ES256 algorithm by performing the following +steps: + +Generate a private key using the ES256 algorithm + +```sh +openssl ecparam -name prime256v1 -genkey -out private.ec.key +``` + +Convert it to PKCS#8 format + +```sh +openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private.ec.key -out private.key +``` + +Extract the public key + +```sh +openssl ec -inform PEM -outform PEM -pubout -in private.key -out public.key +``` diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index 583139cff1..2051a7aa0b 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -38,13 +38,18 @@ auth: clientId: ${AUTH_MICROSOFT_CLIENT_ID} clientSecret: ${AUTH_MICROSOFT_CLIENT_SECRET} tenantId: ${AUTH_MICROSOFT_TENANT_ID} + domainHint: ${AZURE_TENANT_ID} ``` -The Microsoft provider is a structure with three configuration keys: +The Microsoft provider is a structure with three mandatory configuration keys: - `clientId`: Application (client) ID, found on App Registration > Overview - `clientSecret`: Secret, found on App Registration > Certificates & secrets - `tenantId`: Directory (tenant) ID, found on App Registration > Overview +- `domainHint` (optional): Typically the same as `tenantId`. + Leave blank if your app registration is multi tenant. + When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants. + For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy) ## Outbound Network Access diff --git a/docs/backend-system/index.md b/docs/backend-system/index.md index 5dde66378a..c74dd34102 100644 --- a/docs/backend-system/index.md +++ b/docs/backend-system/index.md @@ -10,6 +10,6 @@ description: The Backend System ## Status -The new backend system is in alpha, and only a small number of plugins have been migrated so far. It is possible to try it out, but it is not recommended to use this new system in production yet. +The new backend system is in alpha, but many plugins have already been migrated. We recommend all plugins to migrate to the new system, and you can also try it out in your own production deployments. You can find an example backend setup in [the `backend-next` package](https://github.com/backstage/backstage/tree/master/packages/backend-next). diff --git a/docs/deployment/backstage-deploy/aws.md b/docs/deployment/backstage-deploy/aws.md index 957dc27437..818850a6f5 100644 --- a/docs/deployment/backstage-deploy/aws.md +++ b/docs/deployment/backstage-deploy/aws.md @@ -107,7 +107,9 @@ $ export AWS_SECRET_ACCESS_KEY=.... (second secret value) ## Configuring the Pulumi CLI -Second, install the [Pulumi CLI](https://www.pulumi.com/docs/get-started/install/). +Second, install the [Pulumi CLI](https://www.pulumi.com/docs/get-started/install/) - `backstage-deploy` uses it to +simplify the management of cloud resources (Pulumi allows us to simply specify the desired "target cloud state", and +Pulumi will intelligently create/modify/delete resources to reach that state. Nice!). Then we need to execute the following commands, to set Pulumi up: @@ -126,9 +128,9 @@ By using `pulumi login --local` we are making sure that Pulumi stores our state ## Deploying your instance on Lightsail -:::warning +:::tip -Make sure that [Docker](https://docs.docker.com/) is running before you start this section. +Make sure that [Docker](https://docs.docker.com/) is running on your machine before you start this section. ::: diff --git a/docs/features/search/how-to-guides.md b/docs/features/search/how-to-guides.md index 382221d781..a5662999ae 100644 --- a/docs/features/search/how-to-guides.md +++ b/docs/features/search/how-to-guides.md @@ -114,18 +114,18 @@ of the `SearchType` component. > Check out the documentation around [integrating search into plugins](../../plugins/integrating-search-into-plugins.md#create-a-collator) for how to create your own collator. -## How to customize fields in the Software Catalog index +## How to customize fields in the Software Catalog or TechDocs index -Sometimes you will might want to have ability to control -which data passes to search index in catalog collator, or to customize data for specific kind. -You can easily do that by passing `entityTransformer` callback to `DefaultCatalogCollatorFactory`. -You can either just simply amend default behaviour, or even to write completely new document -(which should follow some required basic structure though). +Sometimes, you might want to have the ability to control which data passes into the search index +in the catalog collator or customize data for a specific kind. You can easily achieve this +by passing an `entityTransformer` callback to the `DefaultCatalogCollatorFactory`. This behavior +is also possible for the `DefaultTechDocsCollatorFactory`. You can either simply amend the default behavior +or even write an entirely new document (which should still follow some required basic structure). > `authorization` and `location` cannot be modified via a `entityTransformer`, `location` can be modified only through `locationTemplate`. ```ts title="packages/backend/src/plugins/search.ts" -const entityTransformer: CatalogCollatorEntityTransformer = ( +const catalogEntityTransformer: CatalogCollatorEntityTransformer = ( entity: Entity, ) => { if (entity.kind === 'SomeKind') { @@ -146,7 +146,26 @@ indexBuilder.addCollator({ discovery: env.discovery, tokenManager: env.tokenManager, /* highlight-add-next-line */ - entityTransformer, + entityTransformer: catalogEntityTransformer, + }), +}); + +const techDocsEntityTransformer: TechDocsCollatorEntityTransformer = ( + entity: Entity, +) => { + return { + // add more fields to the index + ...defaultTechDocsCollatorEntityTransformer(entity), + tags: entity.metadata.tags, + }; +}; + +indexBuilder.addCollator({ + collator: DefaultTechDocsCollatorFactory.fromConfig(env.config, { + discovery: env.discovery, + tokenManager: env.tokenManager, + /* highlight-add-next-line */ + entityTransformer: techDocsEntityTransformer, }), }); ``` diff --git a/docs/local-dev/cli-build-system.md b/docs/local-dev/cli-build-system.md index 61e24863fe..7bb4918d47 100644 --- a/docs/local-dev/cli-build-system.md +++ b/docs/local-dev/cli-build-system.md @@ -674,38 +674,3 @@ To add subpath exports to an existing package, simply add the desired `"exports" ```bash yarn backstage-cli migrate package-exports ``` - -## Experimental Type Build - -> Note: Experimental type builds are deprecated and will be removed in the future. They have been replaced by [subpath exports](#subpath-exports). - -The Backstage CLI has an experimental feature where multiple different type definition files can be generated for different release stages. The release stages are marked in the [TSDoc](https://tsdoc.org/) for each individual export, using either `@public`, `@alpha`, or `@beta`. Rather than just building a single `index.d.ts` file, the build process will instead output `index.d.ts`, `index.beta.d.ts`, and `index.alpha.d.ts`. Each of these files will have exports from more unstable release stages stripped, meaning that `index.d.ts` will omit all exports marked with `@alpha` or `@beta`, while `index.beta.d.ts` will omit all exports marked with `@alpha`. - -This feature is aimed at projects that publish to package registries and wish to maintain different levels of API stability within each package. There is no need to use this within a single monorepo, as it has no effect due to only applying to built and published packages. - -In order for the experimental type build to work, `@microsoft/api-extractor` must be installed in your project, as it is an optional peer dependency of the Backstage CLI. There are then three steps that need to be taken for each package where you want to enable this feature: - -- Add the `--experimental-type-build` flag to the `"build"` script of the package. -- Add either one or both of `"alphaTypes"` and `"betaTypes"` to the `"publishConfig"` of the package: - ```json - "publishConfig": { - ... - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts", - "betaTypes": "dist/index.beta.d.ts" - }, - ``` -- Add either one or both of `"alpha"` and `"beta"` to the `"files"` of the package: - ```json - "files": [ - "dist", - "alpha", - "beta" - ] - ``` - -Once this setup is complete, users of the published packages will only be able to access the stable API via the main package entry point, for example `@acme/my-plugin`. Exports marked with `@alpha` or `@beta` will only be available via the `/alpha` entry point, for example `@acme/my-plugin/alpha`, and exports marked with `@beta` will only be available via `/beta`. This does not apply within the monorepo that contains the package. There all exports still have to be imported via the main entry point. - -Note that these different entry points are only separated during type checking. At runtime they all share the same code which contains the exports from all releases stages. - -An example of this setup can be seen in the [`@backstage/catalog-model`](https://github.com/backstage/backstage/blob/da0675bf9f28ed1460f03635a22d3c26abd14707/packages/catalog-model/package.json#L14) package, which has enabled `alpha` type exports. With this setup, exports marked as `@alpha` are only available for import via `@backstage/catalog-model/alpha`. The `@backstage/catalog-model` package currently does not have any exports marked as `@beta`, or a `/beta` entry point. diff --git a/docs/openapi/test-case-validation.md b/docs/openapi/test-case-validation.md new file mode 100644 index 0000000000..50686b5ba0 --- /dev/null +++ b/docs/openapi/test-case-validation.md @@ -0,0 +1,41 @@ +## OpenAPI Validation using Test Cases + +This is primarily performed by `backstage-repo-tools schema openapi test`. Any errors found in the generated specs can be either + +1. Fixed manually, this is usually relevant for request body or response body changes. +2. Fixed automatically with `backstage-repo-tools schema openapi test --update`. +3. Fixing the test case. This can happen where a response is mocked as + +```ts +it('should return the right value', () => { + // We will assume that this is the actual response and update the spec accordingly. + // Ideally, this should be a fully populated return value. + const entity: Entity = {} as any; + app.get('/test', () => { + return entity; + }); + const response = await request(app).get('/test'); + expect(response.body).toEqual(entity); +}); +``` + +will cause an invalid spec validation. The return value doesn't have all properties as defined in the type. Try to avoid this if possible. Something better would be, + +```ts +it('should return the right value', () => { + // We will assume that this is the actual response and update the spec accordingly. + // Ideally, this should be a fully populated return value. + const entity: Entity = { + apiVersion: 'a1', + kind: 'k1', + metadata: { name: 'n1' }, + }; + app.get('/test', () => { + return entity; + }); + const response = await request(app).get('/test'); + expect(response.body).toEqual(entity); +}); +``` + +Additionally, for more advanced use cases, you can run `yarn optic capture {PATH_TO_OPENAPI_FILE} --update interactive` and go through the prompts on the screen. Under the hood, the test validation + updating is done by [Optic](https://github.com/opticdev/optic), a great project around supporting OpenAPI specs and development. You can find additional options [here](https://www.useoptic.com/docs/verify-openapi). diff --git a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md index 839441e733..23bc4e84f1 100644 --- a/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md +++ b/docs/permissions/plugin-authors/02-adding-a-basic-permission-check.md @@ -349,7 +349,7 @@ import { createRouter } from './service/router'; /** * The example TODO list backend plugin. * -* @alpha +* @public */ export const exampleTodoListPlugin = createBackendPlugin({ pluginId: 'exampleTodoList', diff --git a/docs/releases/v1.20.0-next.0-changelog.md b/docs/releases/v1.20.0-next.0-changelog.md new file mode 100644 index 0000000000..521cbe0d8b --- /dev/null +++ b/docs/releases/v1.20.0-next.0-changelog.md @@ -0,0 +1,3261 @@ +# Release v1.20.0-next.0 + +## @backstage/backend-openapi-utils@0.1.0-next.0 + +### Minor Changes + +- 785fb1ea75: Adds a new route, `/openapi.json` to validated routers for displaying their full OpenAPI spec in a standard endpoint. + +### Patch Changes + +- 6694b369a3: Adds a new function `wrapInOpenApiTestServer` that allows for proxied requests at runtime. This will support the new `yarn backstage-repo-tools schema openapi test` command. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/cli@0.24.0-next.0 + +### Minor Changes + +- 8db5c3cd7a: Removed support for the `publishConfig.alphaTypes` and `.betaTypes` fields that were used together with `--experimental-type-build` to generate `/alpha` and `/beta` entry points. Use the `exports` field to achieve this instead. +- 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`. + +### Patch Changes + +- 4ba4ac351f: Switch from using deprecated `@esbuild-kit/*` packages to using `tsx`. This also switches to using the new module loader `register` API when available, avoiding the experimental warning when starting backends. +- 6bf7561d3c: The experimental package detection will now ignore packages that don't make `package.json` available. +- e14cbf563d: Added `EXPERIMENTAL_VITE` flag for using [vite](https://vitejs.dev) as dev server instead of Webpack +- 7cd34392f5: Ignore `stdin` when spawning backend child process for the `start` command. Fixing an issue where backend startup would hang. +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/cli-node@0.2.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.10 + - @backstage/types@1.1.1 + +## @backstage/cli-node@0.2.0-next.0 + +### Minor Changes + +- 8db5c3cd7a: Removed support for the `publishConfig.alphaTypes` and `.betaTypes` fields that were used together with `--experimental-type-build` to generate `/alpha` and `/beta` entry points. Use the `exports` field to achieve this instead. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/core-plugin-api@1.8.0-next.0 + +### Minor Changes + +- 1e5b7d993a: `IconComponent` can now have a `fontSize` of `inherit`, which is useful for in-line icons. +- cb6db75bc2: Introduced `AnyRouteRefParams` as a replacement for `AnyParams`, which is now deprecated. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- cb6db75bc2: Deprecated several types related to the routing system that are scheduled to be removed, as well as several fields on the route ref types themselves. +- 68fc9dc60e: Added a new `/alpha` export `convertLegacyRouteRef`, which is a temporary utility to allow existing route refs to be used with the new experimental packages. +- Updated dependencies + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/frontend-app-api@0.3.0-next.0 + +### Minor Changes + +- 68fc9dc60e: Added the ability to configure bound routes through `app.routes.bindings`. The routing system used by `createApp` has been replaced by one that only supports route refs of the new format from `@backstage/frontend-plugin-api`. The requirement for route refs to have the same ID as their associated extension has been removed. + +### Patch Changes + +- e28d379e32: Refactor internal extension instance system into an app graph. +- 6c2b872153: Add official support for React 18. +- dc613f9bcf: Updated `app.extensions` configuration schema. +- 685a4c8901: Installed features are now deduplicated both by reference and ID when available. Features passed to `createApp` now override both discovered and loaded features. +- bb98953cb9: Register default implementation for the `Translation API` on the new `createApp`. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/frontend-plugin-api@0.3.0-next.0 + +### Minor Changes + +- 68fc9dc60e: Added `RouteRef`, `SubRouteRef`, `ExternalRouteRef`, and related types. All exports from this package that previously relied on the types with the same name from `@backstage/core-plugin-api` now use the new types instead. To convert and existing legacy route ref to be compatible with the APIs from this package, use the `convertLegacyRouteRef` utility from `@backstage/core-plugin-api/alpha`. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 6af88a05ff: Improve the extension boundary component and create a default extension suspense component. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/types@1.1.1 + +## @backstage/repo-tools@0.4.0-next.0 + +### Minor Changes + +- 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`. +- 6694b369a3: Adds a new command `schema openapi test` that performs runtime validation of your OpenAPI specs using your test data. Under the hood, we're using Optic to perform this check, really cool work by them! + + To use this new command, you will have to run `yarn add @useoptic/optic` in the root of your repo. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + +## @backstage/plugin-auth-backend@0.20.0-next.0 + +### Minor Changes + +- bdf08ad04a: Adds the StaticTokenIssuer and StaticKeyStore, an alternative token issuer that can be used to sign the Authorization header using a predefined public/private key pair. + +### Patch Changes + +- 96c4f54bf6: Reverted the Microsoft auth provider to the previous implementation. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.1-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog@1.15.0-next.0 + +### Minor Changes + +- 1e5b7d993a: Added the `DefaultEntityPresentationApi`, which is an implementation of the + `EntityPresentationApi` that `@backstage/plugin-catalog-react` exposes through + its `entityPresentationApiRef`. This implementation is also by default made + available automatically by the catalog plugin, unless you replace it with a + custom one. It batch fetches and caches data from the catalog as needed for + display, and is customizable by adopters to add their own rendering functions. + +### Patch Changes + +- 8a8445663b: Migrate catalog entity cards to new frontend system extension format. + +- e964c17db9: Use default extensions boundary and suspense on the alpha declarative `createCatalogFilterExtension` extension factory. + +- 71c97e7d73: The \`spec.lifecycle' field in entities will now always be rendered as a string. + +- 6c2b872153: Add official support for React 18. + +- 0bf6ebda88: Initial entity page implementation for new frontend system at `/alpha`, with an overview page enabled by default and the about card available as an optional card. + +- bb98953cb9: Create declarative extensions for the `Catalog` plugin; this initial plugin preset contains sidebar item, index page and filter extensions, all distributed via `/alpha` subpath. + + The `EntityPage` will be migrated in a follow-up patch. + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-catalog-backend@1.15.0-next.0 + +### Minor Changes + +- 8d756968f9: Introduce a new optional config parameter `catalog.stitchingStrategy.mode`, + which can have the values `'immediate'` (default) and `'deferred'`. The default + is for stitching to work as it did before this change, which means that it + happens "in-band" (blocking) immediately when each processing task finishes. + When set to `'deferred'`, stitching is instead deferred to happen on a separate + asynchronous worker queue just like processing. + + Deferred stitching should make performance smoother when ingesting large amounts + of entities, and reduce p99 processing times and repeated over-stitching of + hot spot entities when fan-out/fan-in in terms of relations is very large. It + does however also come with some performance cost due to the queuing with how + much wall-clock time some types of task take. + +### Patch Changes + +- 6694b369a3: Update the OpenAPI spec with more complete error responses and request bodies using Optic. Also, updates the test cases to use the new `supertest` pass through from `@backstage/backend-openapi-utils`. +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + +## @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.0-next.0 + +### Minor Changes + +- 785fb1ea75: Adds a new catalog module for ingesting Backstage plugin OpenAPI specs into the catalog for display as an API entity. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-react@1.9.0-next.0 + +### Minor Changes + +- 1e5b7d993a: Added an `EntityPresentationApi` and associated `entityPresentationApiRef`. This + API lets you control how references to entities (e.g. in links, headings, + iconography etc) are represented in the user interface. + + Usage of this API is initially added to the `EntityRefLink` and `EntityRefLinks` + components, so that they can render richer, more correct representation of + entity refs. There's also a new `EntityDisplayName` component, which works just like + the `EntityRefLink` but without the link. + + Along with that change, the `fetchEntities` and `getTitle` props of + `EntityRefLinksProps` are deprecated and no longer used, since the same need + instead is fulfilled (and by default always enabled) by the + `entityPresentationApiRef`. + +- 1fd53fa0c6: The `UserListPicker` component has undergone improvements to enhance its performance. + + The previous implementation inferred the number of owned and starred entities based on the entities available in the `EntityListContext`. The updated version no longer relies on the `EntityListContext` for inference, allowing for better decoupling. + + The component now loads the entities' count asynchronously, resulting in improved performance and responsiveness. For this purpose, some of the exported filters such as `EntityTagFilter`, `EntityOwnerFilter`, `EntityLifecycleFilter` and `EntityNamespaceFilter` have now the `getCatalogFilters` method implemented. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added new APIs at the `/alpha` subpath for creating entity page cards and content for the new frontend system. +- 71c97e7d73: The `spec.type` field in entities will now always be rendered as a string. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-graphiql@0.3.0-next.0 + +### Minor Changes + +- 57fda44b90: Upgrade to GraphiQL to 3.0.6 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-scaffolder@1.16.0-next.0 + +### Minor Changes + +- 3fdffbb699: Release design improvements for the `Scaffolder` plugin and support v5 of `@rjsf/*` libraries. + + This change should be non-breaking. If you're seeing typescript issues after migrating please [open an issue](https://github.com/backstage/backstage/issues/new/choose) + + The `next` versions like `createNextFieldExtension` and `NextScaffolderPage` have been promoted to the public interface under `createScaffolderFieldExtension` and `ScaffolderPage`, so any older imports which are no longer found will need updating from `@backstage/plugin-scaffolder/alpha` or `@backstage/plugin-scaffolder-react/alpha` will need to be imported from `@backstage/plugin-scaffolder` and `@backstage/plugin-scaffolder-react` respectively. + + The legacy versions are now available in `/alpha` under `createLegacyFieldExtension` and `LegacyScaffolderPage` if you're running into issues, but be aware that these will be removed in a next mainline release. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + +## @backstage/plugin-scaffolder-backend@1.19.0-next.0 + +### Minor Changes + +- 5e4127c18e: Allow setting `update: true` in `publish:github:pull-request` scaffolder action + +### Patch Changes + +- 0920fd02ac: Add examples for `github:environment:create` scaffolder action & improve related tests +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- 99d4936f6c: Add examples for `github:webhook` scaffolder action & improve related tests +- f8727ad228: Add examples for `publish:github:pull-request` scaffolder action & improve related tests +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-scaffolder-react@1.6.0-next.0 + +### Minor Changes + +- 3fdffbb699: Release design improvements for the `Scaffolder` plugin and support v5 of `@rjsf/*` libraries. + + This change should be non-breaking. If you're seeing typescript issues after migrating please [open an issue](https://github.com/backstage/backstage/issues/new/choose) + + The `next` versions like `createNextFieldExtension` and `NextScaffolderPage` have been promoted to the public interface under `createScaffolderFieldExtension` and `ScaffolderPage`, so any older imports which are no longer found will need updating from `@backstage/plugin-scaffolder/alpha` or `@backstage/plugin-scaffolder-react/alpha` will need to be imported from `@backstage/plugin-scaffolder` and `@backstage/plugin-scaffolder-react` respectively. + + The legacy versions are now available in `/alpha` under `createLegacyFieldExtension` and `LegacyScaffolderPage` if you're running into issues, but be aware that these will be removed in a next mainline release. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.0-next.0 + +### Minor Changes + +- 46f0f1700e: Extract a package for the Stack Overflow new backend system plugin. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-vault-backend@0.4.0-next.0 + +### Minor Changes + +- a873a32a1f: Added support for the [new backend system](https://backstage.io/docs/backend-system/). + + In your `packages/backend/src/index.ts` make the following changes: + + ```diff + import { createBackend } from '@backstage/backend-defaults'; + const backend = createBackend(); + // ... other feature additions + + backend.add(import('@backstage/plugin-vault-backend'); + backend.start(); + ``` + + If you use the new backend system, the token renewal task can be defined via configuration file: + + ```diff + vault: + baseUrl: + token: + schedule: + + frequency: ... + + timeout: ... + + # Other schedule options, such as scope or initialDelay + ``` + + If the `schedule` is omitted or set to `false` no token renewal task will be scheduled. + If the value of `schedule` is set to `true` the renew will be scheduled hourly (the default). + In other cases (like in the diff above), the defined schedule will be used. + + **DEPRECATIONS**: The interface `VaultApi` and the type `VaultSecret` are now deprecated. Import them from `@backstage/plugin-vault-node`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-vault-node@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-vault-node@0.1.0-next.0 + +### Minor Changes + +- 7a41bcf2af: Initial version of the \`plugin-vault-node\`\` package. It contains the extension point definitions + for the vault backend, as well as some types that will be deprecated in the backend plugin. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + +## @backstage/app-defaults@1.4.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/backend-app-api@0.5.8-next.0 + +### Patch Changes + +- bc9a18d5ec: Added a workaround for double `default` wrapping when dynamically importing CommonJS modules with default exports. +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/cli-node@0.2.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/backend-common@0.19.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-dev-utils@0.1.2 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/backend-defaults@0.2.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + +## @backstage/backend-plugin-api@0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/backend-tasks@0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/backend-test-utils@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/config-loader@1.5.2-next.0 + +### Patch Changes + +- 22ca64f117: Correctly resolve config targets into absolute paths +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/core-app-api@1.11.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 89d13e5618: Add current and default scopes when refreshing session +- 9ab0572217: Add component data `core.type` marker for `AppRouter` and `FlatRoutes`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/core-components@0.13.7-next.0 + +### Patch Changes + +- 81c8db2088: Fix `RoutedTabs` so that it does not explode without tabs. + +- 6c2b872153: Add official support for React 18. + +- 7bdc1b0a12: Fixed compatibility with Safari <16.3 by eliminating RegEx lookbehind in `extractInitials`. + + This PR also changed how initials are generated resulting in _John Jonathan Doe_ => _JD_ instead of _JJ_. + +- 71c97e7d73: Fixed the type declaration of `DependencyGraphProps`, the `defs` prop now expects `JSX.Element`s. + +- Updated dependencies + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/create-app@0.5.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- ae1602e54d: If create app installs dependencies, don't suggest to user that they also need to do it. +- Updated dependencies + - @backstage/cli-common@0.1.13 + +## @backstage/dev-utils@1.0.23-next.0 + +### Patch Changes + +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/integration-react@1.1.21-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + +## @techdocs/cli@1.6.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + +## @backstage/test-utils@1.4.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/theme@0.4.4-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + +## @backstage/version-bridge@1.0.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + +## @backstage/plugin-adr@0.6.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-adr-common@0.2.16 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-adr-backend@0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.16 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-airbrake@0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/dev-utils@1.0.23-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-airbrake-backend@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-allure@0.1.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-analytics-module-ga@0.1.35-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-analytics-module-ga4@0.1.6-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-analytics-module-newrelic-browser@0.0.4-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-apache-airflow@0.2.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + +## @backstage/plugin-api-docs@0.9.13-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-api-docs-module-protoc-gen-doc@0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + +## @backstage/plugin-apollo-explorer@0.1.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-app-backend@0.3.55-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.7-next.0 + +## @backstage/plugin-app-node@0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + +## @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-github-provider@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-gitlab-provider@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-google-provider@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-microsoft-provider@0.1.2-next.0 + +### Patch Changes + +- 2817115d09: Removed `prompt=consent` from start method to fix #20641 +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-oauth2-provider@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-backend-module-pinniped-provider@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-auth-node@0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-azure-devops@0.3.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 361bb34d8e: Consolidated getting the annotation values into a single function to help with future changes +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-azure-devops-common@0.3.1 + +## @backstage/plugin-azure-devops-backend@0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-devops-common@0.3.1 + +## @backstage/plugin-azure-sites@0.1.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-azure-sites-common@0.1.1 + +## @backstage/plugin-azure-sites-backend@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-sites-common@0.1.1 + +## @backstage/plugin-badges@0.2.50-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-badges-backend@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-bazaar@0.2.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-bazaar-backend@0.3.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-bitrise@0.1.53-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-catalog-backend-module-aws@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-catalog-backend-module-azure@0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket@0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.13 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-cloud@0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.13 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-catalog-backend-module-bitbucket-server@0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-gcp@0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-catalog-backend-module-gerrit@0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-github@0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-catalog-backend-module-github-org@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-backend-module-github@0.4.5-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-gitlab@0.3.4-next.0 + +### Patch Changes + +- d732f17610: Added try catch around fetching gitlab group users to prevent refresh from failing completely while only a select number of groups might not be able to load correctly. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-incremental-ingestion@0.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-catalog-backend-module-ldap@0.5.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-msgraph@0.5.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-openapi@0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-puppetdb@0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-catalog-backend-module-unprocessed@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-catalog-graph@0.2.38-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-graphql@0.4.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/plugin-catalog-import@0.10.2-next.0 + +### Patch Changes + +- 6db75b900a: Create an experimental plugin that is compatible with the declarative integration system, it is exported from the `/alpha` subpath. +- 6c2b872153: Add official support for React 18. +- 71c97e7d73: The `app.title` configuration is now properly required to be a string. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + +## @backstage/plugin-catalog-node@1.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + +## @backstage/plugin-catalog-unprocessed-entities@0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-cicd-statistics@0.1.28-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-cicd-statistics-module-gitlab@0.1.22-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-cicd-statistics@0.1.28-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-circleci@0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-cloudbuild@0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-code-climate@0.1.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-code-coverage@0.2.19-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 71c97e7d73: The warning for missing code coverage will now render the entity as a reference. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-code-coverage-backend@0.2.21-next.0 + +### Patch Changes + +- 11f671eaa9: Added support for new backend system +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-codescene@0.1.19-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-config-schema@0.1.47-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-cost-insights@0.12.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-cost-insights-common@0.1.2 + +## @backstage/plugin-devtools@0.1.6-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.5 + +## @backstage/plugin-devtools-backend@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-devtools-common@0.1.5 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-dynatrace@8.0.0-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-entity-feedback@0.2.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-feedback-backend@0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-entity-feedback-common@0.1.3 + +## @backstage/plugin-entity-validation@0.1.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + +## @backstage/plugin-events-backend@0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-aws-sqs@0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-azure@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-bitbucket-cloud@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-gerrit@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-github@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-module-gitlab@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-backend-test-utils@0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.16-next.0 + +## @backstage/plugin-events-node@0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + +## @backstage/plugin-explore@0.4.12-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-explore-react@0.0.33-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-explore-backend@0.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-explore-react@0.0.33-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-explore-common@0.0.2 + +## @backstage/plugin-firehydrant@0.2.10-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-fossa@0.2.58-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-gcalendar@0.3.20-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-gcp-projects@0.3.43-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-git-release-manager@0.3.39-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + +## @backstage/plugin-github-actions@0.6.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-github-deployments@0.1.57-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-github-issues@0.2.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-github-pull-requests-board@0.1.20-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-gitops-profiles@0.3.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-gocd@0.1.32-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-graphql-backend@0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-graphql@0.4.1-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-graphql-voyager@0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-home@0.5.10-next.0 + +### Patch Changes + +- 3fdffbb699: Remove the duplicate versions of `@rjsf/*` as they're no longer needed +- 6c2b872153: Add official support for React 18. +- 5b364984bf: Added experimental support for declarative integration via the `/alpha` subpath. +- cc0e8d0b51: Temporarily pin the `react-grid-layout` sub-dependency to version `1.3.4` while the horizontal resizing of the latest version is not fixed. For more details, see [#20712](https://github.com/backstage/backstage/issues/20712). +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/plugin-home-react@0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + +## @backstage/plugin-ilert@0.2.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-jenkins@0.9.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-jenkins-common@0.1.20 + +## @backstage/plugin-jenkins-backend@0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-jenkins-common@0.1.20 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-kafka@0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + +## @backstage/plugin-kafka-backend@0.3.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-kubernetes@0.11.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-kubernetes-react@0.1.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-kubernetes-backend@0.13.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + - @backstage/plugin-kubernetes-node@0.1.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-kubernetes-cluster@0.0.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-kubernetes-react@0.1.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-kubernetes-common@0.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-kubernetes-node@0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-kubernetes-react@0.1.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + +## @backstage/plugin-lighthouse@0.4.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-lighthouse-backend@0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-lighthouse-common@0.1.4 + +## @backstage/plugin-linguist@0.1.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-linguist-backend@0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-linguist-common@0.1.2 + +## @backstage/plugin-microsoft-calendar@0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-newrelic@0.3.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-newrelic-dashboard@0.3.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-nomad@0.1.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + +## @backstage/plugin-nomad-backend@0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-octopus-deploy@0.2.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-opencost@0.2.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-org@0.6.16-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-org-react@0.1.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-pagerduty@0.6.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-periskop@0.1.24-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-periskop-backend@0.2.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-permission-backend@0.5.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-permission-backend-module-allow-all-policy@0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + +## @backstage/plugin-permission-node@0.7.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-permission-react@0.4.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-playlist@0.1.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-playlist-common@0.1.11 + +## @backstage/plugin-playlist-backend@0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-common@0.1.11 + +## @backstage/plugin-proxy-backend@0.4.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-puppetdb@0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-rollbar@0.4.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-rollbar-backend@0.1.52-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-cookiecutter@0.2.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-gitlab@0.2.10-next.0 + +### Patch Changes + +- 26ca97ebaa: Add examples for `gitlab:projectAccessToken:create` scaffolder action & improve related tests +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-rails@0.4.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-backend-module-sentry@0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-scaffolder-backend-module-yeoman@0.2.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + +## @backstage/plugin-scaffolder-common@1.4.3-next.0 + +### Patch Changes + +- 2e0cef42ab: Add missing required property `type` in `Template.v1beta3.schema.json` schema +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + +## @backstage/plugin-scaffolder-node@0.2.8-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-search@1.4.2-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- fa11120050: Fixed incorrect plugin ID in `/alpha` export. +- 71c97e7d73: Minor internal code cleanup. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend@1.4.7-next.0 + +### Patch Changes + +- 6694b369a3: Update the OpenAPI spec with more complete error responses and request bodies using Optic. Also, updates the test cases to use the new `supertest` pass through from `@backstage/backend-openapi-utils`. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend-module-elasticsearch@1.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend-module-pg@0.5.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + +### Patch Changes + +- c437253b7a: The process of adding or modifying fields in the techdocs search index has been simplified. For more details, see [How to customize fields in the Software Catalog or TechDocs index](https://backstage.io/docs/features/search/how-to-guides.md#how-to-customize-fields-in-the-software-catalog-or-techdocs-index). +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + +## @backstage/plugin-search-backend-node@1.2.11-next.0 + +### Patch Changes + +- b168d7e7ea: Fix highlighting for non-string fields on the `Lunr` search engine implementation. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-search-react@1.7.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- f75caf9f3d: Fixed a rare occurrence where a race in the search bar could throw away user input or cause the clear button not to work. +- 71c97e7d73: The filter options passed to `SearchResultGroupLayout` are now always explicitly rendered as strings by default. +- e7c09c4f4b: Use default extensions boundary and suspense on the alpha declarative `createSearchResultListItem` extension factory. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-sentry@0.5.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-shortcuts@0.3.16-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/types@1.1.1 + +## @backstage/plugin-sonarqube@0.7.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-sonarqube-react@0.1.10-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-sonarqube-backend@0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + +## @backstage/plugin-sonarqube-react@0.1.10-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-splunk-on-call@0.4.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + +## @backstage/plugin-stack-overflow@0.1.22-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- b168d7e7ea: Migrate package to the new Frontend system, the new module is distributed with a `/alpha` subpath. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-stack-overflow-backend@0.2.11-next.0 + +### Patch Changes + +- b168d7e7ea: Deprecate package in favor of the new `@backstage/plugin-search-backend-module-stack-overflow-collator` module. + + The search collator `requestParams` option is optional now, so its default value is `{ order: 'desc', sort: 'activity', site: 'stackoverflow' }` as defined in the `Try It` section on the [official Stack Overflow API documentation](https://api.stackexchange.com/docs/questions). + +- Updated dependencies + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-stackstorm@0.1.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + +## @backstage/plugin-tech-insights@0.3.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-insights-backend@0.5.21-next.0 + +### Patch Changes + +- 193ad022bb: Add `factRetrieverId` to the fact retriever's logger metadata. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + +## @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.39-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + +## @backstage/plugin-tech-insights-node@0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + +## @backstage/plugin-tech-radar@0.6.10-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-techdocs@1.8.1-next.0 + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- a3add7a682: Export alpha routes and nav item extension, only available for applications that uses the new Frontend system. +- 71c97e7d73: The \`spec.lifecycle' field in entities will now always be rendered as a string. +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added entity page content for the new plugin exported via `/alpha`. +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-techdocs-addons-test-utils@1.0.23-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + +## @backstage/plugin-techdocs-backend@1.8.1-next.0 + +### Patch Changes + +- c3c5c7e514: Add info about the entity when tech docs fail to build +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + +## @backstage/plugin-techdocs-module-addons-contrib@1.1.2-next.0 + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + +## @backstage/plugin-techdocs-node@1.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/plugin-techdocs-react@1.1.13-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + +## @backstage/plugin-todo@0.2.30-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-todo-backend@0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + +## @backstage/plugin-user-settings@0.7.12-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + +## @backstage/plugin-user-settings-backend@0.2.6-next.0 + +### Patch Changes + +- dd0350379b: Added dependency on `@backstage/config` +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + +## @backstage/plugin-vault@0.1.21-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + +## @backstage/plugin-xcmetrics@0.2.45-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + +## example-app@0.2.89-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-import@0.10.2-next.0 + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-home@0.5.10-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.2-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/plugin-user-settings@0.7.12-next.0 + - @backstage/plugin-tech-radar@0.6.10-next.0 + - @backstage/plugin-search@1.4.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.1.5-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.2-next.0 + - @backstage/plugin-microsoft-calendar@0.1.9-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.1-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/plugin-entity-feedback@0.2.9-next.0 + - @backstage/plugin-apache-airflow@0.2.17-next.0 + - @backstage/plugin-github-actions@0.6.7-next.0 + - @backstage/plugin-octopus-deploy@0.2.8-next.0 + - @backstage/plugin-stack-overflow@0.1.22-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-catalog-graph@0.2.38-next.0 + - @backstage/plugin-code-coverage@0.2.19-next.0 + - @backstage/plugin-cost-insights@0.12.15-next.0 + - @backstage/plugin-tech-insights@0.3.18-next.0 + - @backstage/plugin-azure-devops@0.3.8-next.0 + - @backstage/plugin-gcp-projects@0.3.43-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-azure-sites@0.1.15-next.0 + - @backstage/plugin-cloudbuild@0.3.26-next.0 + - @backstage/plugin-kubernetes@0.11.1-next.0 + - @backstage/plugin-lighthouse@0.4.11-next.0 + - @backstage/plugin-scaffolder@1.16.0-next.0 + - @backstage/plugin-stackstorm@0.1.8-next.0 + - @backstage/plugin-dynatrace@8.0.0-next.0 + - @backstage/plugin-gcalendar@0.3.20-next.0 + - @backstage/plugin-pagerduty@0.6.7-next.0 + - @backstage/plugin-shortcuts@0.3.16-next.0 + - @backstage/plugin-airbrake@0.3.26-next.0 + - @backstage/plugin-api-docs@0.9.13-next.0 + - @backstage/plugin-circleci@0.3.26-next.0 + - @backstage/plugin-devtools@0.1.6-next.0 + - @backstage/plugin-linguist@0.1.11-next.0 + - @backstage/plugin-newrelic@0.3.42-next.0 + - @backstage/plugin-playlist@0.1.18-next.0 + - @backstage/plugin-puppetdb@0.1.9-next.0 + - @backstage/plugin-explore@0.4.12-next.0 + - @backstage/plugin-jenkins@0.9.1-next.0 + - @backstage/plugin-rollbar@0.4.26-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/plugin-badges@0.2.50-next.0 + - @backstage/plugin-sentry@0.5.11-next.0 + - @backstage/plugin-kafka@0.3.26-next.0 + - @backstage/plugin-nomad@0.1.7-next.0 + - @backstage/plugin-gocd@0.1.32-next.0 + - @backstage/plugin-todo@0.2.30-next.0 + - @backstage/plugin-adr@0.6.9-next.0 + - @backstage/plugin-org@0.6.16-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.7 + +## example-app-next@0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-import@0.10.2-next.0 + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-home@0.5.10-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.2-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/plugin-user-settings@0.7.12-next.0 + - @backstage/plugin-tech-radar@0.6.10-next.0 + - @backstage/plugin-search@1.4.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.1.5-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/plugin-microsoft-calendar@0.1.9-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.1-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/plugin-entity-feedback@0.2.9-next.0 + - @backstage/plugin-apache-airflow@0.2.17-next.0 + - @backstage/plugin-github-actions@0.6.7-next.0 + - @backstage/plugin-octopus-deploy@0.2.8-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-catalog-graph@0.2.38-next.0 + - @backstage/plugin-code-coverage@0.2.19-next.0 + - @backstage/plugin-cost-insights@0.12.15-next.0 + - @backstage/plugin-tech-insights@0.3.18-next.0 + - @backstage/plugin-azure-devops@0.3.8-next.0 + - @backstage/plugin-gcp-projects@0.3.43-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-azure-sites@0.1.15-next.0 + - @backstage/plugin-cloudbuild@0.3.26-next.0 + - @backstage/plugin-kubernetes@0.11.1-next.0 + - @backstage/plugin-lighthouse@0.4.11-next.0 + - @backstage/plugin-scaffolder@1.16.0-next.0 + - @backstage/plugin-stackstorm@0.1.8-next.0 + - @backstage/plugin-dynatrace@8.0.0-next.0 + - @backstage/plugin-gcalendar@0.3.20-next.0 + - @backstage/plugin-pagerduty@0.6.7-next.0 + - @backstage/plugin-shortcuts@0.3.16-next.0 + - @backstage/plugin-airbrake@0.3.26-next.0 + - @backstage/plugin-api-docs@0.9.13-next.0 + - @backstage/plugin-circleci@0.3.26-next.0 + - @backstage/plugin-devtools@0.1.6-next.0 + - @backstage/plugin-linguist@0.1.11-next.0 + - @backstage/plugin-newrelic@0.3.42-next.0 + - @backstage/plugin-playlist@0.1.18-next.0 + - @backstage/plugin-puppetdb@0.1.9-next.0 + - @backstage/plugin-explore@0.4.12-next.0 + - @backstage/plugin-jenkins@0.9.1-next.0 + - @backstage/plugin-rollbar@0.4.26-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/plugin-badges@0.2.50-next.0 + - @backstage/plugin-sentry@0.5.11-next.0 + - @backstage/plugin-kafka@0.3.26-next.0 + - @backstage/plugin-gocd@0.1.32-next.0 + - @backstage/plugin-todo@0.2.30-next.0 + - @backstage/plugin-adr@0.6.9-next.0 + - @backstage/plugin-org@0.6.16-next.0 + - app-next-example-plugin@0.0.3-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-compat-api@0.0.1-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.7 + +## app-next-example-plugin@0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + +## example-backend@0.2.89-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-techdocs-backend@1.8.1-next.0 + - @backstage/plugin-code-coverage-backend@0.2.21-next.0 + - @backstage/plugin-scaffolder-backend@1.19.0-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-search-backend@1.4.7-next.0 + - @backstage/plugin-tech-insights-backend@0.5.21-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/plugin-kafka-backend@0.3.5-next.0 + - @backstage/plugin-proxy-backend@0.4.5-next.0 + - @backstage/plugin-auth-backend@0.20.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/plugin-app-backend@0.3.55-next.0 + - @backstage/plugin-devtools-backend@0.2.4-next.0 + - example-app@0.2.89-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-azure-devops-backend@0.4.4-next.0 + - @backstage/plugin-azure-sites-backend@0.1.17-next.0 + - @backstage/plugin-badges-backend@0.3.4-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.4-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-entity-feedback-backend@0.2.4-next.0 + - @backstage/plugin-events-backend@0.2.16-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-explore-backend@0.0.17-next.0 + - @backstage/plugin-graphql-backend@0.2.1-next.0 + - @backstage/plugin-jenkins-backend@0.3.1-next.0 + - @backstage/plugin-kubernetes-backend@0.13.1-next.0 + - @backstage/plugin-lighthouse-backend@0.3.4-next.0 + - @backstage/plugin-linguist-backend@0.5.4-next.0 + - @backstage/plugin-nomad-backend@0.1.9-next.0 + - @backstage/plugin-permission-backend@0.5.30-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-backend@0.3.11-next.0 + - @backstage/plugin-rollbar-backend@0.1.52-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.24-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.10-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.16-next.0 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.39-next.0 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + - @backstage/plugin-todo-backend@0.3.5-next.0 + +## example-backend-next@0.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.0-next.0 + - @backstage/plugin-techdocs-backend@1.8.1-next.0 + - @backstage/plugin-scaffolder-backend@1.19.0-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-search-backend@1.4.7-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/plugin-proxy-backend@0.4.5-next.0 + - @backstage/plugin-app-backend@0.3.55-next.0 + - @backstage/plugin-devtools-backend@0.2.4-next.0 + - @backstage/backend-defaults@0.2.7-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/plugin-adr-backend@0.4.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-azure-devops-backend@0.4.4-next.0 + - @backstage/plugin-badges-backend@0.3.4-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.1.24-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.4-next.0 + - @backstage/plugin-entity-feedback-backend@0.2.4-next.0 + - @backstage/plugin-jenkins-backend@0.3.1-next.0 + - @backstage/plugin-kubernetes-backend@0.13.1-next.0 + - @backstage/plugin-lighthouse-backend@0.3.4-next.0 + - @backstage/plugin-linguist-backend@0.5.4-next.0 + - @backstage/plugin-nomad-backend@0.1.9-next.0 + - @backstage/plugin-permission-backend@0.5.30-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.4-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-backend@0.3.11-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-sonarqube-backend@0.2.9-next.0 + - @backstage/plugin-todo-backend@0.3.5-next.0 + +## @backstage/backend-plugin-manager@0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.0-next.0 + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-events-backend@0.2.16-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-common@1.2.7 + +## @backstage/core-compat-api@0.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + +## e2e-test@0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + +## techdocs-cli-embedded-app@0.2.88-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + +## @internal/plugin-todo-list@1.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + +## @internal/plugin-todo-list-backend@1.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 diff --git a/docs/tutorials/yarn-migration.md b/docs/tutorials/yarn-migration.md index 9e041a5969..204829b093 100644 --- a/docs/tutorials/yarn-migration.md +++ b/docs/tutorials/yarn-migration.md @@ -4,8 +4,6 @@ title: Migration to Yarn 3 description: Guide for how to migrate a Backstage project to use Yarn 3 --- -> NOTE: We do not yet recommend all projects to migrate to Yarn 3. Only do so if you have specific reasons for it. - While Backstage projects created with `@backstage/create-app` use [Yarn 1](https://classic.yarnpkg.com/) by default, it is possible to switch them to instead use [Yarn 3](https://yarnpkg.com/). Tools like `yarn backstage-cli versions:bump` will still work, as they recognize both lockfile formats. diff --git a/microsite/data/plugins/analytics-module-matomo.yaml b/microsite/data/plugins/analytics-module-matomo.yaml new file mode 100644 index 0000000000..e71e7c41d7 --- /dev/null +++ b/microsite/data/plugins/analytics-module-matomo.yaml @@ -0,0 +1,10 @@ +--- +title: 'Analytics Module: Matomo Analytics' +author: Red Hat +authorUrl: https://redhat.com +category: Monitoring +description: Track usage of your Backstage instance using Matomo Analytics. +documentation: https://github.com/janus-idp/backstage-plugins/blob/main/plugins/analytics-module-matomo/README.md +iconUrl: img/matomo.png +npmPackageName: '@janus-idp/backstage-plugin-analytics-module-matomo' +addedDate: '2023-10-17' diff --git a/microsite/data/plugins/changelog.yaml b/microsite/data/plugins/changelog.yaml new file mode 100644 index 0000000000..1b4684db7f --- /dev/null +++ b/microsite/data/plugins/changelog.yaml @@ -0,0 +1,12 @@ +--- +title: Changelog viewer +author: RSC Labs +authorUrl: https://rsoftcon.com/ +category: Discovery +description: View changelogs for your components in Backstage. Built-in support of "Keep the changelog" format. +documentation: https://github.com/RSC-Labs/backstage-changelog-plugin/blob/main/README.md +iconUrl: https://raw.githubusercontent.com/RSC-Labs/backstage-changelog-plugin/main/docs/plugin_icon.png +npmPackageName: '@rsc-labs/backstage-changelog-plugin' +tags: + - changelog +addedDate: '2023-10-22' diff --git a/microsite/package.json b/microsite/package.json index 3f266c189e..61bfcbd04a 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -19,7 +19,7 @@ "devDependencies": { "@docusaurus/module-type-aliases": "0.0.0-5703", "@spotify/prettier-config": "^14.0.0", - "@tsconfig/docusaurus": "^1.0.6", + "@tsconfig/docusaurus": "^2.0.0", "@types/luxon": "^3.0.0", "@types/webpack-env": "^1.18.0", "js-yaml": "^4.1.0", diff --git a/microsite/static/img/matomo.png b/microsite/static/img/matomo.png new file mode 100644 index 0000000000..025786b948 Binary files /dev/null and b/microsite/static/img/matomo.png differ diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 8147655315..ba22850109 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -2701,90 +2701,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-arm64@npm:1.3.92" +"@swc/core-darwin-arm64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-arm64@npm:1.3.95" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-x64@npm:1.3.92" +"@swc/core-darwin-x64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-x64@npm:1.3.95" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.92" +"@swc/core-linux-arm-gnueabihf@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.95" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.92" +"@swc/core-linux-arm64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.92" +"@swc/core-linux-arm64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.92" +"@swc/core-linux-x64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-musl@npm:1.3.92" +"@swc/core-linux-x64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-musl@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.92" +"@swc/core-win32-arm64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.95" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.92" +"@swc/core-win32-ia32-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.95" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.92" +"@swc/core-win32-x64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.95" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.92 - resolution: "@swc/core@npm:1.3.92" + version: 1.3.95 + resolution: "@swc/core@npm:1.3.95" dependencies: - "@swc/core-darwin-arm64": 1.3.92 - "@swc/core-darwin-x64": 1.3.92 - "@swc/core-linux-arm-gnueabihf": 1.3.92 - "@swc/core-linux-arm64-gnu": 1.3.92 - "@swc/core-linux-arm64-musl": 1.3.92 - "@swc/core-linux-x64-gnu": 1.3.92 - "@swc/core-linux-x64-musl": 1.3.92 - "@swc/core-win32-arm64-msvc": 1.3.92 - "@swc/core-win32-ia32-msvc": 1.3.92 - "@swc/core-win32-x64-msvc": 1.3.92 + "@swc/core-darwin-arm64": 1.3.95 + "@swc/core-darwin-x64": 1.3.95 + "@swc/core-linux-arm-gnueabihf": 1.3.95 + "@swc/core-linux-arm64-gnu": 1.3.95 + "@swc/core-linux-arm64-musl": 1.3.95 + "@swc/core-linux-x64-gnu": 1.3.95 + "@swc/core-linux-x64-musl": 1.3.95 + "@swc/core-win32-arm64-msvc": 1.3.95 + "@swc/core-win32-ia32-msvc": 1.3.95 + "@swc/core-win32-x64-msvc": 1.3.95 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -2813,7 +2813,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 88c0c62ff790e896180862c341be8bae98baf0a5c5e87f2f04f49e14b8c4fba460d6b352618b4dda066c8ae6bf152cd843eab25837f38128175208b8c0635721 + checksum: 49856ad64fa16151f7f784956c8134060c5ed612c9b3fcb79e33676c77219f61fa3bb6b9e0dd15d78c7bff92cacc0c944d2b4e60849eb93caca917a8544b1c2c languageName: node linkType: hard @@ -2854,10 +2854,10 @@ __metadata: languageName: node linkType: hard -"@tsconfig/docusaurus@npm:^1.0.6": - version: 1.0.7 - resolution: "@tsconfig/docusaurus@npm:1.0.7" - checksum: 8f5b14005d90b2008f10daf03a5edec86d2a7603e5641c579ea936a5c2d165a8c3007a72254fc4c2adb0554d73062f52bb97b30ff818f01c9215957822f3c4db +"@tsconfig/docusaurus@npm:^2.0.0": + version: 2.0.1 + resolution: "@tsconfig/docusaurus@npm:2.0.1" + checksum: 63bebda70d83c56f95a90176d2e188e1ea9c08c23b499e5e7b292ebfae0ce7117f712809828ed21ae3b8440daf22191d6bf71bf2575f9bd474a51b1770ca30cc languageName: node linkType: hard @@ -3894,7 +3894,7 @@ __metadata: "@docusaurus/preset-classic": 0.0.0-5703 "@spotify/prettier-config": ^14.0.0 "@swc/core": ^1.3.46 - "@tsconfig/docusaurus": ^1.0.6 + "@tsconfig/docusaurus": ^2.0.0 "@types/luxon": ^3.0.0 "@types/webpack-env": ^1.18.0 clsx: ^1.1.1 diff --git a/package.json b/package.json index 6a21034cae..82a23cf7eb 100644 --- a/package.json +++ b/package.json @@ -54,10 +54,11 @@ "@material-ui/pickers@^3.3.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch", "@material-ui/pickers@^3.2.10": "patch:@material-ui/pickers@npm%3A3.3.11#./.yarn/patches/@material-ui-pickers-npm-3.3.11-1c8f68ea20.patch" }, - "version": "1.19.0", + "version": "1.20.0-next.0", "dependencies": { "@backstage/errors": "workspace:^", - "@manypkg/get-packages": "^1.1.3" + "@manypkg/get-packages": "^1.1.3", + "@useoptic/optic": "^0.50.10" }, "devDependencies": { "@backstage/cli": "workspace:*", diff --git a/packages/app-defaults/CHANGELOG.md b/packages/app-defaults/CHANGELOG.md index d212580484..a2a3c28d4b 100644 --- a/packages/app-defaults/CHANGELOG.md +++ b/packages/app-defaults/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/app-defaults +## 1.4.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + ## 1.4.4 ### Patch Changes diff --git a/packages/app-defaults/package.json b/packages/app-defaults/package.json index 9ab12f7cc5..9f6f16d3e1 100644 --- a/packages/app-defaults/package.json +++ b/packages/app-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/app-defaults", "description": "Provides the default wiring of a Backstage App", - "version": "1.4.4", + "version": "1.4.5-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next-example-plugin/CHANGELOG.md b/packages/app-next-example-plugin/CHANGELOG.md index 3402944211..f21bbd45c3 100644 --- a/packages/app-next-example-plugin/CHANGELOG.md +++ b/packages/app-next-example-plugin/CHANGELOG.md @@ -1,5 +1,13 @@ # app-next-example-plugin +## 0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + ## 0.0.2 ### Patch Changes diff --git a/packages/app-next-example-plugin/package.json b/packages/app-next-example-plugin/package.json index 865c6e0800..08186e1210 100644 --- a/packages/app-next-example-plugin/package.json +++ b/packages/app-next-example-plugin/package.json @@ -1,7 +1,7 @@ { "name": "app-next-example-plugin", "description": "Backstage internal example plugin", - "version": "0.0.2", + "version": "0.0.3-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/app-next/CHANGELOG.md b/packages/app-next/CHANGELOG.md index 0a80d6d902..5bb2a54561 100644 --- a/packages/app-next/CHANGELOG.md +++ b/packages/app-next/CHANGELOG.md @@ -1,5 +1,82 @@ # example-app-next +## 0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-import@0.10.2-next.0 + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-home@0.5.10-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.2-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/plugin-user-settings@0.7.12-next.0 + - @backstage/plugin-tech-radar@0.6.10-next.0 + - @backstage/plugin-search@1.4.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.1.5-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/plugin-microsoft-calendar@0.1.9-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.1-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/plugin-entity-feedback@0.2.9-next.0 + - @backstage/plugin-apache-airflow@0.2.17-next.0 + - @backstage/plugin-github-actions@0.6.7-next.0 + - @backstage/plugin-octopus-deploy@0.2.8-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-catalog-graph@0.2.38-next.0 + - @backstage/plugin-code-coverage@0.2.19-next.0 + - @backstage/plugin-cost-insights@0.12.15-next.0 + - @backstage/plugin-tech-insights@0.3.18-next.0 + - @backstage/plugin-azure-devops@0.3.8-next.0 + - @backstage/plugin-gcp-projects@0.3.43-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-azure-sites@0.1.15-next.0 + - @backstage/plugin-cloudbuild@0.3.26-next.0 + - @backstage/plugin-kubernetes@0.11.1-next.0 + - @backstage/plugin-lighthouse@0.4.11-next.0 + - @backstage/plugin-scaffolder@1.16.0-next.0 + - @backstage/plugin-stackstorm@0.1.8-next.0 + - @backstage/plugin-dynatrace@8.0.0-next.0 + - @backstage/plugin-gcalendar@0.3.20-next.0 + - @backstage/plugin-pagerduty@0.6.7-next.0 + - @backstage/plugin-shortcuts@0.3.16-next.0 + - @backstage/plugin-airbrake@0.3.26-next.0 + - @backstage/plugin-api-docs@0.9.13-next.0 + - @backstage/plugin-circleci@0.3.26-next.0 + - @backstage/plugin-devtools@0.1.6-next.0 + - @backstage/plugin-linguist@0.1.11-next.0 + - @backstage/plugin-newrelic@0.3.42-next.0 + - @backstage/plugin-playlist@0.1.18-next.0 + - @backstage/plugin-puppetdb@0.1.9-next.0 + - @backstage/plugin-explore@0.4.12-next.0 + - @backstage/plugin-jenkins@0.9.1-next.0 + - @backstage/plugin-rollbar@0.4.26-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/plugin-badges@0.2.50-next.0 + - @backstage/plugin-sentry@0.5.11-next.0 + - @backstage/plugin-kafka@0.3.26-next.0 + - @backstage/plugin-gocd@0.1.32-next.0 + - @backstage/plugin-todo@0.2.30-next.0 + - @backstage/plugin-adr@0.6.9-next.0 + - @backstage/plugin-org@0.6.16-next.0 + - app-next-example-plugin@0.0.3-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/core-compat-api@0.0.1-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.7 + ## 0.0.2 ### Patch Changes diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 6f6081fece..a46e119ac0 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -4,12 +4,22 @@ app: routes: bindings: plugin.pages.externalRoutes.pageX: plugin.pages.routes.pageX - # waiting for https://github.com/backstage/backstage/pull/20605 - # catalog.externalRoutes.viewTechDoc: techdocs.routes.docRoot + plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot extensions: - apis.plugin.graphiql.browse.gitlab: true + # Entity page cards + - entity.cards.about + - entity.cards.labels + - entity.cards.links: + config: + filter: + - isKind: component + + # Entity page content + - entity.content.techdocs + # scmAuthExtension: >- # createScmAuthExtension({ # id: 'apis.scmAuth.addons.ghe', diff --git a/packages/app-next/package.json b/packages/app-next/package.json index 5437c66272..62c00fca94 100644 --- a/packages/app-next/package.json +++ b/packages/app-next/package.json @@ -1,6 +1,6 @@ { "name": "example-app-next", - "version": "0.0.2", + "version": "0.0.3-next.0", "private": true, "backstage": { "role": "frontend" diff --git a/packages/app-next/src/App.test.tsx b/packages/app-next/src/App.test.tsx index b3cef58fe1..350daaaecc 100644 --- a/packages/app-next/src/App.test.tsx +++ b/packages/app-next/src/App.test.tsx @@ -21,6 +21,10 @@ jest.mock('@backstage/plugin-graphiql', () => ({ GraphiQLIcon: () => null, })); +// Rarely, and only in windows CI, do these tests take slightly more than the +// default five seconds +jest.setTimeout(15_000); + describe('App', () => { it('should render', async () => { process.env = { diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 4e42d9b268..978c40ef44 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -29,11 +29,8 @@ import { createExtension, createApiExtension, createExtensionOverrides, - createPageExtension, } from '@backstage/frontend-plugin-api'; -import { entityRouteRef } from '@backstage/plugin-catalog-react'; import techdocsPlugin from '@backstage/plugin-techdocs/alpha'; -import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha'; import { homePage } from './HomePage'; import { collectLegacyRoutes } from '@backstage/core-compat-api'; import { FlatRoutes } from '@backstage/core-app-api'; @@ -75,13 +72,6 @@ TODO: /* app.tsx */ -const entityPageExtension = createPageExtension({ - id: 'catalog:entity', - defaultPath: '/catalog/:namespace/:kind/:name', - routeRef: convertLegacyRouteRef(entityRouteRef), - loader: async () =>
Just a temporary mocked entity page
, -}); - const homePageExtension = createExtension({ id: 'myhomepage', attachTo: { id: 'home', input: 'props' }, @@ -89,8 +79,8 @@ const homePageExtension = createExtension({ children: coreExtensionData.reactElement, title: titleExtensionDataRef, }, - factory({ bind }) { - bind({ children: homePage, title: 'just a title' }); + factory() { + return { children: homePage, title: 'just a title' }; }, }); @@ -122,12 +112,7 @@ const app = createApp({ homePlugin, ...collectedLegacyPlugins, createExtensionOverrides({ - extensions: [ - entityPageExtension, - homePageExtension, - scmAuthExtension, - scmIntegrationApi, - ], + extensions: [homePageExtension, scmAuthExtension, scmIntegrationApi], }), ], /* Handled through config instead */ diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index f339cf74da..c360b1108d 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,82 @@ # example-app +## 0.2.89-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-import@0.10.2-next.0 + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-home@0.5.10-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/plugin-techdocs-module-addons-contrib@1.1.2-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/plugin-user-settings@0.7.12-next.0 + - @backstage/plugin-tech-radar@0.6.10-next.0 + - @backstage/plugin-search@1.4.2-next.0 + - @backstage/plugin-catalog-unprocessed-entities@0.1.5-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/plugin-kubernetes-cluster@0.0.2-next.0 + - @backstage/plugin-microsoft-calendar@0.1.9-next.0 + - @backstage/plugin-newrelic-dashboard@0.3.1-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/plugin-entity-feedback@0.2.9-next.0 + - @backstage/plugin-apache-airflow@0.2.17-next.0 + - @backstage/plugin-github-actions@0.6.7-next.0 + - @backstage/plugin-octopus-deploy@0.2.8-next.0 + - @backstage/plugin-stack-overflow@0.1.22-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-catalog-graph@0.2.38-next.0 + - @backstage/plugin-code-coverage@0.2.19-next.0 + - @backstage/plugin-cost-insights@0.12.15-next.0 + - @backstage/plugin-tech-insights@0.3.18-next.0 + - @backstage/plugin-azure-devops@0.3.8-next.0 + - @backstage/plugin-gcp-projects@0.3.43-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-azure-sites@0.1.15-next.0 + - @backstage/plugin-cloudbuild@0.3.26-next.0 + - @backstage/plugin-kubernetes@0.11.1-next.0 + - @backstage/plugin-lighthouse@0.4.11-next.0 + - @backstage/plugin-scaffolder@1.16.0-next.0 + - @backstage/plugin-stackstorm@0.1.8-next.0 + - @backstage/plugin-dynatrace@8.0.0-next.0 + - @backstage/plugin-gcalendar@0.3.20-next.0 + - @backstage/plugin-pagerduty@0.6.7-next.0 + - @backstage/plugin-shortcuts@0.3.16-next.0 + - @backstage/plugin-airbrake@0.3.26-next.0 + - @backstage/plugin-api-docs@0.9.13-next.0 + - @backstage/plugin-circleci@0.3.26-next.0 + - @backstage/plugin-devtools@0.1.6-next.0 + - @backstage/plugin-linguist@0.1.11-next.0 + - @backstage/plugin-newrelic@0.3.42-next.0 + - @backstage/plugin-playlist@0.1.18-next.0 + - @backstage/plugin-puppetdb@0.1.9-next.0 + - @backstage/plugin-explore@0.4.12-next.0 + - @backstage/plugin-jenkins@0.9.1-next.0 + - @backstage/plugin-rollbar@0.4.26-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/plugin-badges@0.2.50-next.0 + - @backstage/plugin-sentry@0.5.11-next.0 + - @backstage/plugin-kafka@0.3.26-next.0 + - @backstage/plugin-nomad@0.1.7-next.0 + - @backstage/plugin-gocd@0.1.32-next.0 + - @backstage/plugin-todo@0.2.30-next.0 + - @backstage/plugin-adr@0.6.9-next.0 + - @backstage/plugin-org@0.6.16-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-linguist-common@0.1.2 + - @backstage/plugin-search-common@1.2.7 + ## 0.2.88 ### Patch Changes diff --git a/packages/app/e2e-tests/HomePage.test.ts b/packages/app/e2e-tests/HomePage.test.ts new file mode 100644 index 0000000000..0b1459935f --- /dev/null +++ b/packages/app/e2e-tests/HomePage.test.ts @@ -0,0 +1,74 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { test, expect } from '@playwright/test'; + +test('Should not throw `ResizeObserver loop completed with undelivered notifications`', async ({ + page, +}) => { + await page.goto('/'); + + const enterButton = page.getByRole('button', { name: 'Enter' }); + await expect(enterButton).toBeVisible(); + await enterButton.click(); + + await page.goto('/home'); + await expect( + page + .frameLocator('#webpack-dev-server-client-overlay') + .getByText( + /ResizeObserver loop completed with undelivered notifications/, + ), + ).not.toBeVisible(); +}); + +test('Should resize widgets vertically and horizontally', async ({ page }) => { + await page.goto('/'); + + const enterButton = page.getByRole('button', { name: 'Enter' }); + await expect(enterButton).toBeVisible(); + await enterButton.click(); + + await page.goto('/home'); + await expect(page.getByText('Backstage Example App')).toBeVisible(); + + // Start editing mode + await page.getByRole('button', { name: /Edit/ }).click(); + await expect(page.getByRole('button', { name: /Save/ })).toBeVisible(); + + // Resize the last installed widget + const widgetElement = await page.locator('.react-grid-item:nth-child(3)'); + const defaultWidgetBox = { x: 1, y: 1, width: 1, height: 1 }; + const widgetBoxBefore = + (await widgetElement.boundingBox()) ?? defaultWidgetBox; + const widgetResizeHandle = await widgetElement.locator( + '.react-resizable-handle', + ); + await widgetResizeHandle.hover(); + await page.mouse.down(); + await page.mouse.move(widgetBoxBefore.width / 2, widgetBoxBefore.height / 2); + await page.mouse.up(); + const widgetBoxAfter = + (await widgetElement.boundingBox()) ?? defaultWidgetBox; + + // Ensure that both height and width was reduced + expect(widgetBoxAfter.width).toBeLessThan(widgetBoxBefore.width); + expect(widgetBoxAfter.height).toBeLessThan(widgetBoxBefore.height); + + // Exit editing mode + await page.getByRole('button', { name: 'Save' }).click(); + await expect(page.getByRole('button', { name: /Edit/ })).toBeVisible(); +}); diff --git a/packages/app/package.json b/packages/app/package.json index df48491481..4c02aa5cb6 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "example-app", - "version": "0.2.88", + "version": "0.2.89-next.0", "private": true, "backstage": { "role": "frontend" @@ -93,12 +93,16 @@ "@roadiehq/backstage-plugin-github-insights": "^2.0.5", "@roadiehq/backstage-plugin-github-pull-requests": "^2.2.7", "@roadiehq/backstage-plugin-travis-ci": "^2.0.5", + "@vitejs/plugin-react": "^4.0.4", "history": "^5.0.0", "react": "^18.0.2", "react-dom": "^18.0.2", "react-router": "^6.3.0", "react-router-dom": "^6.3.0", "react-use": "^17.2.4", + "vite": "^4.4.9", + "vite-plugin-html": "^3.2.0", + "vite-plugin-node-polyfills": "^0.14.1", "zen-observable": "^0.10.0" }, "devDependencies": { diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 81af3b91e7..ecc1f008b5 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -41,6 +41,7 @@ import { EntityAzurePullRequestsContent, isAzureDevOpsAvailable, isAzurePipelinesAvailable, + EntityAzureReadmeCard, } from '@backstage/plugin-azure-devops'; import { isOctopusDeployAvailable, @@ -415,6 +416,14 @@ const overviewContent = ( + + + + + + + + diff --git a/packages/backend-app-api/CHANGELOG.md b/packages/backend-app-api/CHANGELOG.md index efc72cefc8..a6705a897f 100644 --- a/packages/backend-app-api/CHANGELOG.md +++ b/packages/backend-app-api/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/backend-app-api +## 0.5.8-next.0 + +### Patch Changes + +- bc9a18d5ec: Added a workaround for double `default` wrapping when dynamically importing CommonJS modules with default exports. +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/cli-node@0.2.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.5.6 ### Patch Changes diff --git a/packages/backend-app-api/package.json b/packages/backend-app-api/package.json index 48a3b7a05b..b5bf276e33 100644 --- a/packages/backend-app-api/package.json +++ b/packages/backend-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-app-api", "description": "Core API used by Backstage backend apps", - "version": "0.5.6", + "version": "0.5.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-app-api/src/wiring/BackstageBackend.ts b/packages/backend-app-api/src/wiring/BackstageBackend.ts index 2b916908de..d523481cf7 100644 --- a/packages/backend-app-api/src/wiring/BackstageBackend.ts +++ b/packages/backend-app-api/src/wiring/BackstageBackend.ts @@ -57,7 +57,26 @@ function isPromise(value: unknown | Promise): value is Promise { } function unwrapFeature( - feature: BackendFeature | (() => BackendFeature), + feature: + | BackendFeature + | (() => BackendFeature) + | { default: BackendFeature | (() => BackendFeature) }, ): BackendFeature { - return typeof feature === 'function' ? feature() : feature; + if (typeof feature === 'function') { + return feature(); + } + if ('$$type' in feature) { + return feature; + } + // This is a workaround where default exports get transpiled to `exports['default'] = ...` + // in CommonJS modules, which in turn results in a double `{ default: { default: ... } }` nesting + // when importing using a dynamic import. + // TODO: This is a broader issue than just this piece of code, and should move away from CommonJS. + if ('default' in feature) { + const defaultFeature = feature.default; + return typeof defaultFeature === 'function' + ? defaultFeature() + : defaultFeature; + } + return feature; } diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 50216d5139..cae7c3314a 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/backend-common +## 0.19.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-dev-utils@0.1.2 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.19.8 ### Patch Changes diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index 11facfa7ce..111370ad4b 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-common", "description": "Common functionality library for Backstage backends", - "version": "0.19.8", + "version": "0.19.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-defaults/CHANGELOG.md b/packages/backend-defaults/CHANGELOG.md index 6c9089fd06..a85c49aacc 100644 --- a/packages/backend-defaults/CHANGELOG.md +++ b/packages/backend-defaults/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/backend-defaults +## 0.2.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + ## 0.2.6 ### Patch Changes diff --git a/packages/backend-defaults/package.json b/packages/backend-defaults/package.json index c843bbc412..13982228ea 100644 --- a/packages/backend-defaults/package.json +++ b/packages/backend-defaults/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-defaults", "description": "Backend defaults used by Backstage backend apps", - "version": "0.2.6", + "version": "0.2.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-next/CHANGELOG.md b/packages/backend-next/CHANGELOG.md index 171d82887c..1e4227084d 100644 --- a/packages/backend-next/CHANGELOG.md +++ b/packages/backend-next/CHANGELOG.md @@ -1,5 +1,46 @@ # example-backend-next +## 0.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-catalog-backend-module-backstage-openapi@0.1.0-next.0 + - @backstage/plugin-techdocs-backend@1.8.1-next.0 + - @backstage/plugin-scaffolder-backend@1.19.0-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-search-backend@1.4.7-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/plugin-proxy-backend@0.4.5-next.0 + - @backstage/plugin-app-backend@0.3.55-next.0 + - @backstage/plugin-devtools-backend@0.2.4-next.0 + - @backstage/backend-defaults@0.2.7-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/plugin-adr-backend@0.4.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-azure-devops-backend@0.4.4-next.0 + - @backstage/plugin-badges-backend@0.3.4-next.0 + - @backstage/plugin-catalog-backend-module-openapi@0.1.24-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.4-next.0 + - @backstage/plugin-entity-feedback-backend@0.2.4-next.0 + - @backstage/plugin-jenkins-backend@0.3.1-next.0 + - @backstage/plugin-kubernetes-backend@0.13.1-next.0 + - @backstage/plugin-lighthouse-backend@0.3.4-next.0 + - @backstage/plugin-linguist-backend@0.5.4-next.0 + - @backstage/plugin-nomad-backend@0.1.9-next.0 + - @backstage/plugin-permission-backend@0.5.30-next.0 + - @backstage/plugin-permission-backend-module-allow-all-policy@0.1.4-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-backend@0.3.11-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-sonarqube-backend@0.2.9-next.0 + - @backstage/plugin-todo-backend@0.3.5-next.0 + ## 0.0.16 ### Patch Changes diff --git a/packages/backend-next/package.json b/packages/backend-next/package.json index 69aa81a9cb..63bfc35d29 100644 --- a/packages/backend-next/package.json +++ b/packages/backend-next/package.json @@ -1,6 +1,6 @@ { "name": "example-backend-next", - "version": "0.0.16", + "version": "0.0.17-next.0", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,6 +34,8 @@ "@backstage/plugin-azure-devops-backend": "workspace:^", "@backstage/plugin-badges-backend": "workspace:^", "@backstage/plugin-catalog-backend": "workspace:^", + "@backstage/plugin-catalog-backend-module-backstage-openapi": "workspace:^", + "@backstage/plugin-catalog-backend-module-openapi": "workspace:^", "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^", "@backstage/plugin-catalog-backend-module-unprocessed": "workspace:^", "@backstage/plugin-devtools-backend": "workspace:^", diff --git a/packages/backend-next/src/index.ts b/packages/backend-next/src/index.ts index bdfbc2c26b..53a51fcfa7 100644 --- a/packages/backend-next/src/index.ts +++ b/packages/backend-next/src/index.ts @@ -39,11 +39,14 @@ backend.add( import('@backstage/plugin-permission-backend-module-allow-all-policy'), ); backend.add(import('@backstage/plugin-permission-backend/alpha')); -backend.add(import('@backstage/plugin-proxy-backend')); +backend.add(import('@backstage/plugin-proxy-backend/alpha')); backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); backend.add(import('@backstage/plugin-search-backend-module-catalog/alpha')); backend.add(import('@backstage/plugin-search-backend-module-explore/alpha')); backend.add(import('@backstage/plugin-search-backend-module-techdocs/alpha')); +backend.add( + import('@backstage/plugin-catalog-backend-module-backstage-openapi'), +); backend.add(import('@backstage/plugin-search-backend/alpha')); backend.add(import('@backstage/plugin-techdocs-backend/alpha')); backend.add(import('@backstage/plugin-todo-backend')); diff --git a/packages/backend-openapi-utils/CHANGELOG.md b/packages/backend-openapi-utils/CHANGELOG.md index 097b0aa089..371ef680c8 100644 --- a/packages/backend-openapi-utils/CHANGELOG.md +++ b/packages/backend-openapi-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/backend-openapi-utils +## 0.1.0-next.0 + +### Minor Changes + +- 785fb1ea75: Adds a new route, `/openapi.json` to validated routers for displaying their full OpenAPI spec in a standard endpoint. + +### Patch Changes + +- 6694b369a3: Adds a new function `wrapInOpenApiTestServer` that allows for proxied requests at runtime. This will support the new `yarn backstage-repo-tools schema openapi test` command. +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.0.5 ### Patch Changes diff --git a/packages/backend-openapi-utils/README.md b/packages/backend-openapi-utils/README.md index ca3f03fcc1..3d3f40682e 100644 --- a/packages/backend-openapi-utils/README.md +++ b/packages/backend-openapi-utils/README.md @@ -65,7 +65,7 @@ export function createRouter() { ### Why am I getting `unknown` as the type for a response? -This can happen when you have a `charset` defined in your `response.content` section. Something like `response.content[ 'application/json; charset=utf-8:']` will cause this issue. +This can happen when you have a `charset` defined in your `response.content` section. Something like `response.content['application/json; charset=utf-8:']` will cause this issue. ## INTERNAL diff --git a/packages/backend-openapi-utils/api-report.md b/packages/backend-openapi-utils/api-report.md index 97ea0224d1..67132ca74b 100644 --- a/packages/backend-openapi-utils/api-report.md +++ b/packages/backend-openapi-utils/api-report.md @@ -5,6 +5,7 @@ ```ts import type { ContentObject } from 'openapi3-ts'; import type core from 'express-serve-static-core'; +import { Express as Express_2 } from 'express'; import { FromSchema } from 'json-schema-to-ts'; import { JSONSchema7 } from 'json-schema-to-ts'; import { middleware } from 'express-openapi-validator'; @@ -16,6 +17,7 @@ import { RequestHandler } from 'express'; import type { ResponseObject } from 'openapi3-ts'; import { Router } from 'express'; import type { SchemaObject } from 'openapi3-ts'; +import { Server } from 'http'; // @public export interface ApiRouter extends Router { @@ -248,6 +250,9 @@ type FullMap< }, > = RequiredMap & OptionalMap; +// @public +export function getOpenApiSpecRoute(baseUrl: string): string; + // @public (undocumented) interface HeaderObject extends ParameterObject { // (undocumented) @@ -708,4 +713,7 @@ type UnknownIfNever

= [P] extends [never] ? unknown : P; // @public type ValueOf = T[keyof T]; + +// @public +export const wrapInOpenApiTestServer: (app: Express_2) => Server | Express_2; ``` diff --git a/packages/backend-openapi-utils/package.json b/packages/backend-openapi-utils/package.json index 04dc3f19f2..9fd5b5a83f 100644 --- a/packages/backend-openapi-utils/package.json +++ b/packages/backend-openapi-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-openapi-utils", "description": "OpenAPI typescript support.", - "version": "0.0.5", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -37,6 +37,8 @@ "dist" ], "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", "@types/express": "^4.17.6", "@types/express-serve-static-core": "^4.17.5", @@ -45,6 +47,7 @@ "express-promise-router": "^4.1.0", "json-schema-to-ts": "^2.6.2", "lodash": "^4.17.21", + "openapi-merge": "^1.3.2", "openapi3-ts": "^3.1.2" } } diff --git a/packages/backend-openapi-utils/src/constants.ts b/packages/backend-openapi-utils/src/constants.ts new file mode 100644 index 0000000000..b55279f6b2 --- /dev/null +++ b/packages/backend-openapi-utils/src/constants.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +/** + * The route that all OpenAPI specs should be served from. + * @public + */ +export const OPENAPI_SPEC_ROUTE = '/openapi.json'; diff --git a/packages/backend-openapi-utils/src/index.ts b/packages/backend-openapi-utils/src/index.ts index 2b612fd617..9f5ccdc03b 100644 --- a/packages/backend-openapi-utils/src/index.ts +++ b/packages/backend-openapi-utils/src/index.ts @@ -31,4 +31,5 @@ export type { PathParameters, } from './utility'; export type { ApiRouter } from './router'; -export { createValidatedOpenApiRouter } from './stub'; +export { createValidatedOpenApiRouter, getOpenApiSpecRoute } from './stub'; +export { wrapInOpenApiTestServer } from './testUtils'; diff --git a/packages/backend-openapi-utils/src/stub.test.ts b/packages/backend-openapi-utils/src/stub.test.ts index c9ec955e95..d4a1c19153 100644 --- a/packages/backend-openapi-utils/src/stub.test.ts +++ b/packages/backend-openapi-utils/src/stub.test.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { createValidatedOpenApiRouter } from './stub'; +import { createValidatedOpenApiRouter, getOpenApiSpecRoute } from './stub'; import express from 'express'; import request from 'supertest'; import singlePathSpec from './___fixtures__/single-path'; import { Response } from './utility'; +import { OPENAPI_SPEC_ROUTE } from './constants'; describe('createRouter', () => { const pet: Response = { @@ -28,6 +29,33 @@ describe('createRouter', () => { photoUrls: [], }; + const specs = [singlePathSpec]; + const ONCE_NESTED_ROUTER_PREFIX = '/pet-store'; + const TWICE_NESTED_ROUTER_PREFIX = `/api`; + + const routers = specs.flatMap(spec => { + const router = createValidatedOpenApiRouter(spec); + const unnestedApp = express(); + unnestedApp.use('/', router); + + const onceNestedRouter = express.Router(); + onceNestedRouter.use(`${ONCE_NESTED_ROUTER_PREFIX}`, router); + const onceNestedApp = express(); + onceNestedApp.use(`/`, onceNestedRouter); + + const twiceNestedApp = express(); + twiceNestedApp.use(`${TWICE_NESTED_ROUTER_PREFIX}`, onceNestedRouter); + return [ + ['', unnestedApp, router], + [ONCE_NESTED_ROUTER_PREFIX, onceNestedApp, router], + [ + `${TWICE_NESTED_ROUTER_PREFIX}${ONCE_NESTED_ROUTER_PREFIX}`, + twiceNestedApp, + router, + ], + ] as const; + }); + it('does NOT override originalUrl and basePath after execution', async () => { expect.assertions(2); const router = createValidatedOpenApiRouter(singlePathSpec); @@ -61,19 +89,40 @@ describe('createRouter', () => { expect(routerGetFn).toHaveBeenCalledTimes(1); }); - it('handles coercing parameters correctly', async () => { - expect.assertions(1); - const router = createValidatedOpenApiRouter(singlePathSpec); - router.get('/pet/:petId', (req, res) => { - expect(typeof req.params.petId).toBe('integer'); - res.json(pet); - }); + it.each(routers)( + '%s handles coercing parameters correctly', + async (prefix, app, router) => { + expect.assertions(1); + router.get('/pet/:petId', (req, res) => { + expect(typeof req.params.petId).toBe('integer'); + res.send(pet); + }); - const apiRouter = express.Router(); - apiRouter.use('/pet-store', router); - const appRouter = express(); - appRouter.use('/api', apiRouter); + await request(app).get(`${prefix}/pet/1`); + }, + ); - await request(appRouter).get('/api/pet-store/pet/1'); + it.each(routers)( + '%s adds the openapi spec to the router', + async (prefix, app) => { + const response = await request(app) + .get(`${prefix}${OPENAPI_SPEC_ROUTE}`) + .expect(200); + expect(response.body).toHaveProperty('paths'); + Object.keys(response.body.paths).forEach(key => { + const specKey = key.replace(prefix, ''); + expect(singlePathSpec.paths).toHaveProperty(specKey); + expect(response.body.paths[key]).toEqual( + (singlePathSpec.paths as any)[specKey], + ); + }); + }, + ); +}); + +describe('getOpenApiSpecRoute', () => { + it('handles expected values', () => { + expect(getOpenApiSpecRoute('/api/test')).toEqual('/api/test/openapi.json'); + expect(getOpenApiSpecRoute('api/test')).toEqual('api/test/openapi.json'); }); }); diff --git a/packages/backend-openapi-utils/src/stub.ts b/packages/backend-openapi-utils/src/stub.ts index 6ec0e1db3c..0f62bf348c 100644 --- a/packages/backend-openapi-utils/src/stub.ts +++ b/packages/backend-openapi-utils/src/stub.ts @@ -27,6 +27,8 @@ import { } from 'express'; import { InputError } from '@backstage/errors'; import { middleware as OpenApiValidator } from 'express-openapi-validator'; +import { OPENAPI_SPEC_ROUTE } from './constants'; +import { isErrorResult, merge } from 'openapi-merge'; type PropertyOverrideRequest = Request & { [key: symbol]: string; @@ -45,6 +47,16 @@ export function getDefaultRouterMiddleware() { return [json()]; } +/** + * Given a base url for a plugin, find the given OpenAPI spec for that plugin. + * @param baseUrl - Plugin base url. + * @returns OpenAPI spec route for the base url. + * @public + */ +export function getOpenApiSpecRoute(baseUrl: string) { + return `${baseUrl}${OPENAPI_SPEC_ROUTE}`; +} + /** * Create a new OpenAPI router with some default middleware. * @param spec - Your OpenAPI spec imported as a JSON object. @@ -59,7 +71,7 @@ export function createValidatedOpenApiRouter( middleware?: RequestHandler[]; }, ) { - const router = PromiseRouter() as ApiRouter; + const router = PromiseRouter(); router.use(options?.middleware || getDefaultRouterMiddleware()); /** @@ -116,5 +128,30 @@ export function createValidatedOpenApiRouter( // Any errors from the middleware get through here. router.use(validatorErrorTransformer()); - return router; + router.get(OPENAPI_SPEC_ROUTE, async (req, res) => { + const mergeOutput = merge([ + { + oas: spec as any, + pathModification: { + /** + * Get the route that this OpenAPI spec is hosted on. The other + * approach of using the discovery API increases the router constructor + * significantly and since we're just looking for path and not full URL, + * this works. + * + * If we wanted to add a list of servers, there may be a case for adding + * discovery API to get an exhaustive list of upstream servers, but that's + * also not currently supported. + */ + prepend: req.originalUrl.replace(OPENAPI_SPEC_ROUTE, ''), + }, + }, + ]); + if (isErrorResult(mergeOutput)) { + throw new InputError('Invalid spec defined'); + } + res.json(mergeOutput.output); + }); + + return router as ApiRouter; } diff --git a/packages/backend-openapi-utils/src/testUtils.ts b/packages/backend-openapi-utils/src/testUtils.ts new file mode 100644 index 0000000000..2ac5575fc4 --- /dev/null +++ b/packages/backend-openapi-utils/src/testUtils.ts @@ -0,0 +1,36 @@ +/* + * 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 { Express } from 'express'; +import { Server } from 'http'; + +/** + * !!! THIS CURRENTLY ONLY SUPPORTS SUPERTEST !!! + * Running against supertest, we need some way to hit the optic proxy. This ensures that + * that happens at runtime when in the context of a `yarn optic capture` command. + * @param app - Express router that would be passed to supertest's `request`. + * @returns A wrapper around the express router (or the router untouched) that still works with supertest. + * @public + */ +export const wrapInOpenApiTestServer = (app: Express): Server | Express => { + if (process.env.OPTIC_PROXY) { + const server = app.listen(+process.env.PORT!); + return { + ...server, + address: () => new URL(process.env.OPTIC_PROXY!), + } as any; + } + return app; +}; diff --git a/packages/backend-plugin-api/CHANGELOG.md b/packages/backend-plugin-api/CHANGELOG.md index 63600bed9f..e83e630a54 100644 --- a/packages/backend-plugin-api/CHANGELOG.md +++ b/packages/backend-plugin-api/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/backend-plugin-api +## 0.6.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + ## 0.6.6 ### Patch Changes diff --git a/packages/backend-plugin-api/package.json b/packages/backend-plugin-api/package.json index 2706b1bdaf..cea829b8d7 100644 --- a/packages/backend-plugin-api/package.json +++ b/packages/backend-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-api", "description": "Core API used by Backstage backend plugins", - "version": "0.6.6", + "version": "0.6.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-plugin-manager/CHANGELOG.md b/packages/backend-plugin-manager/CHANGELOG.md index 526bc6c802..bb9184ec0c 100644 --- a/packages/backend-plugin-manager/CHANGELOG.md +++ b/packages/backend-plugin-manager/CHANGELOG.md @@ -1,5 +1,28 @@ # @backstage/backend-plugin-manager +## 0.0.3-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.0-next.0 + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-events-backend@0.2.16-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-common@1.2.7 + ## 0.0.2 ### Patch Changes diff --git a/packages/backend-plugin-manager/api-report.md b/packages/backend-plugin-manager/api-report.md index 9617dc1096..b4221007ae 100644 --- a/packages/backend-plugin-manager/api-report.md +++ b/packages/backend-plugin-manager/api-report.md @@ -174,7 +174,7 @@ export class PluginManager implements BackendPluginProvider { config: Config, logger: LoggerService, preferAlpha?: boolean, - mooduleLoader?: ModuleLoader, + moduleLoader?: ModuleLoader, ): Promise; // (undocumented) readonly plugins: DynamicPlugin[]; diff --git a/packages/backend-plugin-manager/package.json b/packages/backend-plugin-manager/package.json index 20446196cf..7fc6b554b0 100644 --- a/packages/backend-plugin-manager/package.json +++ b/packages/backend-plugin-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-plugin-manager", "description": "Backstage plugin management backend", - "version": "0.0.2", + "version": "0.0.3-next.0", "private": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts b/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts index dd64dc78f3..06a08117cd 100644 --- a/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts +++ b/packages/backend-plugin-manager/src/loader/CommonJSModuleLoader.ts @@ -24,8 +24,8 @@ export class CommonJSModuleLoader implements ModuleLoader { backstageRoot: string, dynamicPluginsPaths: string[], ): Promise { - const allowedNodeModulesPaths = [ - `${backstageRoot}/node_modules`, + const backstageRootNodeModulesPath = `${backstageRoot}/node_modules`; + const dynamicNodeModulesPaths = [ ...dynamicPluginsPaths.map(p => path.resolve(p, 'node_modules')), ]; const Module = require('module'); @@ -35,8 +35,12 @@ export class CommonJSModuleLoader implements ModuleLoader { if (!dynamicPluginsPaths.some(p => from.startsWith(p))) { return result; } - - const filtered = result.filter(p => allowedNodeModulesPaths.includes(p)); + const filtered = result.filter(nodeModulePath => { + return ( + nodeModulePath === backstageRootNodeModulesPath || + dynamicNodeModulesPaths.some(p => nodeModulePath.startsWith(p)) + ); + }); this.logger.debug( `Overriding node_modules search path for dynamic plugin ${from} to: ${filtered}`, ); diff --git a/packages/backend-plugin-manager/src/manager/plugin-manager.ts b/packages/backend-plugin-manager/src/manager/plugin-manager.ts index 89ea144426..ad76c7529e 100644 --- a/packages/backend-plugin-manager/src/manager/plugin-manager.ts +++ b/packages/backend-plugin-manager/src/manager/plugin-manager.ts @@ -49,7 +49,7 @@ export class PluginManager implements BackendPluginProvider { config: Config, logger: LoggerService, preferAlpha: boolean = false, - mooduleLoader: ModuleLoader = new CommonJSModuleLoader(logger), + moduleLoader: ModuleLoader = new CommonJSModuleLoader(logger), ): Promise { /* eslint-disable-next-line no-restricted-syntax */ const backstageRoot = findPaths(__dirname).targetRoot; @@ -61,7 +61,7 @@ export class PluginManager implements BackendPluginProvider { ); const scannedPlugins = await scanner.scanRoot(); scanner.trackChanges(); - const manager = new PluginManager(logger, scannedPlugins, mooduleLoader); + const manager = new PluginManager(logger, scannedPlugins, moduleLoader); const dynamicPluginsPaths = scannedPlugins.map(p => fs.realpathSync( @@ -73,7 +73,7 @@ export class PluginManager implements BackendPluginProvider { ), ); - mooduleLoader.bootstrap(backstageRoot, dynamicPluginsPaths); + moduleLoader.bootstrap(backstageRoot, dynamicPluginsPaths); scanner.subscribeToRootDirectoryChange(async () => { manager._availablePackages = await scanner.scanRoot(); diff --git a/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts b/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts index e226a1a8ad..51e38002e9 100644 --- a/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts +++ b/packages/backend-plugin-manager/src/scanner/plugin-scanner.ts @@ -217,6 +217,8 @@ export class PluginScanner { .watch(this._rootDirectory, { ignoreInitial: true, followSymlinks: true, + depth: 1, + disableGlobbing: true, }) .on( 'all', diff --git a/packages/backend-tasks/CHANGELOG.md b/packages/backend-tasks/CHANGELOG.md index 2a209521f1..b7cdf841eb 100644 --- a/packages/backend-tasks/CHANGELOG.md +++ b/packages/backend-tasks/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/backend-tasks +## 0.5.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.5.11 ### Patch Changes diff --git a/packages/backend-tasks/package.json b/packages/backend-tasks/package.json index c386654a24..0b08c29c05 100644 --- a/packages/backend-tasks/package.json +++ b/packages/backend-tasks/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-tasks", "description": "Common distributed task management library for Backstage backends", - "version": "0.5.11", + "version": "0.5.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index 8cb13b24c5..320d11e8d2 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/backend-test-utils +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-app-api@0.5.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.2.7 ### Patch Changes diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 1094de8ced..55d2d13aa1 100644 --- a/packages/backend-test-utils/package.json +++ b/packages/backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/backend-test-utils", "description": "Test helpers library for Backstage backends", - "version": "0.2.7", + "version": "0.2.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { diff --git a/packages/backend-test-utils/src/filesystem/MockDirectory.ts b/packages/backend-test-utils/src/filesystem/MockDirectory.ts index b066b9acd5..fff8a35a06 100644 --- a/packages/backend-test-utils/src/filesystem/MockDirectory.ts +++ b/packages/backend-test-utils/src/filesystem/MockDirectory.ts @@ -311,7 +311,7 @@ class MockDirectoryImpl { }; remove = (): void => { - fs.removeSync(this.#root); + fs.rmSync(this.#root, { recursive: true, force: true, maxRetries: 10 }); }; #transformInput(input: MockDirectoryContent[string]): MockEntry[] { diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 9eb282bb1c..618218ebd8 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,64 @@ # example-backend +## 0.2.89-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/plugin-techdocs-backend@1.8.1-next.0 + - @backstage/plugin-code-coverage-backend@0.2.21-next.0 + - @backstage/plugin-scaffolder-backend@1.19.0-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-search-backend@1.4.7-next.0 + - @backstage/plugin-tech-insights-backend@0.5.21-next.0 + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/plugin-kafka-backend@0.3.5-next.0 + - @backstage/plugin-proxy-backend@0.4.5-next.0 + - @backstage/plugin-auth-backend@0.20.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/plugin-app-backend@0.3.55-next.0 + - @backstage/plugin-devtools-backend@0.2.4-next.0 + - example-app@0.2.89-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-adr-backend@0.4.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-azure-devops-backend@0.4.4-next.0 + - @backstage/plugin-azure-sites-backend@0.1.17-next.0 + - @backstage/plugin-badges-backend@0.3.4-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-backend-module-unprocessed@0.3.4-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-entity-feedback-backend@0.2.4-next.0 + - @backstage/plugin-events-backend@0.2.16-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-explore-backend@0.0.17-next.0 + - @backstage/plugin-graphql-backend@0.2.1-next.0 + - @backstage/plugin-jenkins-backend@0.3.1-next.0 + - @backstage/plugin-kubernetes-backend@0.13.1-next.0 + - @backstage/plugin-lighthouse-backend@0.3.4-next.0 + - @backstage/plugin-linguist-backend@0.5.4-next.0 + - @backstage/plugin-nomad-backend@0.1.9-next.0 + - @backstage/plugin-permission-backend@0.5.30-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-backend@0.3.11-next.0 + - @backstage/plugin-rollbar-backend@0.1.52-next.0 + - @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.8-next.0 + - @backstage/plugin-scaffolder-backend-module-rails@0.4.24-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + - @backstage/plugin-search-backend-module-elasticsearch@1.3.10-next.0 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-search-backend-module-pg@0.5.16-next.0 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-tech-insights-backend-module-jsonfc@0.1.39-next.0 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + - @backstage/plugin-todo-backend@0.3.5-next.0 + ## 0.2.88 ### Patch Changes diff --git a/packages/backend/package.json b/packages/backend/package.json index 76ef8dfb2d..5ccc70b104 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.88", + "version": "0.2.89-next.0", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli-node/CHANGELOG.md b/packages/cli-node/CHANGELOG.md index d5dcb6e976..efe94c5ffa 100644 --- a/packages/cli-node/CHANGELOG.md +++ b/packages/cli-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/cli-node +## 0.2.0-next.0 + +### Minor Changes + +- 8db5c3cd7a: Removed support for the `publishConfig.alphaTypes` and `.betaTypes` fields that were used together with `--experimental-type-build` to generate `/alpha` and `/beta` entry points. Use the `exports` field to achieve this instead. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.1.5 ### Patch Changes diff --git a/packages/cli-node/api-report.md b/packages/cli-node/api-report.md index 91148596b3..71be6c4bd4 100644 --- a/packages/cli-node/api-report.md +++ b/packages/cli-node/api-report.md @@ -53,8 +53,6 @@ export interface BackstagePackageJson { access?: 'public' | 'restricted'; directory?: string; registry?: string; - alphaTypes?: string; - betaTypes?: string; }; // (undocumented) scripts?: { diff --git a/packages/cli-node/package.json b/packages/cli-node/package.json index 1d1158c818..74051dbd94 100644 --- a/packages/cli-node/package.json +++ b/packages/cli-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli-node", "description": "Node.js library for Backstage CLIs", - "version": "0.1.5", + "version": "0.2.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli-node/src/monorepo/PackageGraph.ts b/packages/cli-node/src/monorepo/PackageGraph.ts index 421c49f9b9..fb46dbde26 100644 --- a/packages/cli-node/src/monorepo/PackageGraph.ts +++ b/packages/cli-node/src/monorepo/PackageGraph.ts @@ -56,8 +56,6 @@ export interface BackstagePackageJson { access?: 'public' | 'restricted'; directory?: string; registry?: string; - alphaTypes?: string; - betaTypes?: string; }; dependencies?: { diff --git a/packages/cli/CHANGELOG.md b/packages/cli/CHANGELOG.md index 65d0b752e7..10f60a2f78 100644 --- a/packages/cli/CHANGELOG.md +++ b/packages/cli/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/cli +## 0.24.0-next.0 + +### Minor Changes + +- 8db5c3cd7a: Removed support for the `publishConfig.alphaTypes` and `.betaTypes` fields that were used together with `--experimental-type-build` to generate `/alpha` and `/beta` entry points. Use the `exports` field to achieve this instead. +- 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`. + +### Patch Changes + +- 4ba4ac351f: Switch from using deprecated `@esbuild-kit/*` packages to using `tsx`. This also switches to using the new module loader `register` API when available, avoiding the experimental warning when starting backends. +- 6bf7561d3c: The experimental package detection will now ignore packages that don't make `package.json` available. +- e14cbf563d: Added `EXPERIMENTAL_VITE` flag for using [vite](https://vitejs.dev) as dev server instead of Webpack +- 7cd34392f5: Ignore `stdin` when spawning backend child process for the `start` command. Fixing an issue where backend startup would hang. +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/cli-node@0.2.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/eslint-plugin@0.1.3 + - @backstage/release-manifests@0.0.10 + - @backstage/types@1.1.1 + ## 0.23.0 ### Minor Changes diff --git a/packages/cli/cli-report.md b/packages/cli/cli-report.md index 9d6ffd3f69..9a471e2ca6 100644 --- a/packages/cli/cli-report.md +++ b/packages/cli/cli-report.md @@ -224,7 +224,6 @@ Usage: backstage-cli package build [options] Options: --role --minify - --experimental-type-build --skip-build-dependencies --stats --config @@ -344,11 +343,13 @@ Options: --notifyMode -o, --onlyChanged -f, --onlyFailures + --openHandlesTimeout --outputFile --passWithNoTests --preset --prettierPath --projects + --randomize --reporters --resetMocks --resetModules @@ -392,6 +393,7 @@ Options: --watchAll --watchPathIgnorePatterns --watchman + --workerThreads ``` ### `backstage-cli repo` @@ -536,11 +538,13 @@ Options: --notifyMode -o, --onlyChanged -f, --onlyFailures + --openHandlesTimeout --outputFile --passWithNoTests --preset --prettierPath --projects + --randomize --reporters --resetMocks --resetModules @@ -584,6 +588,7 @@ Options: --watchAll --watchPathIgnorePatterns --watchman + --workerThreads ``` ### `backstage-cli versions:bump` diff --git a/packages/cli/package.json b/packages/cli/package.json index 419b02d929..f70b0f2696 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/cli", "description": "CLI for developing Backstage plugins and apps", - "version": "0.23.0", + "version": "0.24.0-next.0", "publishConfig": { "access": "public" }, @@ -40,8 +40,6 @@ "@backstage/integration": "workspace:^", "@backstage/release-manifests": "workspace:^", "@backstage/types": "workspace:^", - "@esbuild-kit/cjs-loader": "^2.4.1", - "@esbuild-kit/esm-loader": "^2.5.5", "@manypkg/get-packages": "^1.1.3", "@octokit/graphql": "^5.0.0", "@octokit/graphql-schema": "^13.7.0", @@ -131,6 +129,7 @@ "swc-loader": "^0.2.3", "tar": "^6.1.12", "terser-webpack-plugin": "^5.1.3", + "tsx": "^3.14.0", "util": "^0.12.3", "webpack": "^5.70.0", "webpack-dev-server": "^4.7.3", @@ -153,6 +152,7 @@ "@backstage/theme": "workspace:^", "@types/cross-spawn": "^6.0.2", "@types/diff": "^5.0.0", + "@types/ejs": "^3.1.3", "@types/express": "^4.17.6", "@types/fs-extra": "^9.0.1", "@types/http-proxy": "^1.17.4", @@ -174,10 +174,22 @@ "type-fest": "^2.19.0" }, "peerDependencies": { - "@microsoft/api-extractor": "^7.21.2" + "@vitejs/plugin-react": "^4.0.4", + "vite": "^4.4.9", + "vite-plugin-html": "^3.2.0", + "vite-plugin-node-polyfills": "^0.14.1" }, "peerDependenciesMeta": { - "@microsoft/api-extractor": { + "@vitejs/plugin-react": { + "optional": true + }, + "vite": { + "optional": true + }, + "vite-plugin-html": { + "optional": true + }, + "vite-plugin-node-polyfills": { "optional": true } }, diff --git a/packages/cli/src/commands/build/command.ts b/packages/cli/src/commands/build/command.ts index 507085e0a0..1b3aeff2f9 100644 --- a/packages/cli/src/commands/build/command.ts +++ b/packages/cli/src/commands/build/command.ts @@ -65,6 +65,5 @@ export async function command(opts: OptionValues): Promise { return buildPackage({ outputs, minify: Boolean(opts.minify), - useApiExtractor: Boolean(opts.experimentalTypeBuild), }); } diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index ce4a4d2bae..fb0a55f37e 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -130,10 +130,6 @@ export function registerScriptCommand(program: Command) { '--minify', 'Minify the generated code. Does not apply to app or backend packages.', ) - .option( - '--experimental-type-build', - 'Enable experimental type build. Does not apply to app or backend packages. [DEPRECATED]', - ) .option( '--skip-build-dependencies', 'Skip the automatic building of local dependencies. Applies to backend packages only.', diff --git a/packages/cli/src/commands/migrate/packageScripts.ts b/packages/cli/src/commands/migrate/packageScripts.ts index 1552208a5f..e9ff20aed3 100644 --- a/packages/cli/src/commands/migrate/packageScripts.ts +++ b/packages/cli/src/commands/migrate/packageScripts.ts @@ -49,9 +49,6 @@ export async function command() { if (scripts.build?.includes('--minify')) { buildCmd.push('--minify'); } - if (scripts.build?.includes('--experimental-type-build')) { - buildCmd.push('--experimental-type-build'); - } if (scripts.build?.includes('--config')) { buildCmd.push(...(scripts.build.match(configArgPattern) ?? [])); } diff --git a/packages/cli/src/commands/repo/build.ts b/packages/cli/src/commands/repo/build.ts index 71e58c6201..32f227c208 100644 --- a/packages/cli/src/commands/repo/build.ts +++ b/packages/cli/src/commands/repo/build.ts @@ -137,7 +137,6 @@ export async function command(opts: OptionValues, cmd: Command): Promise { outputs, logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `, minify: buildOptions.minify, - useApiExtractor: buildOptions.experimentalTypeBuild, }; }); diff --git a/packages/cli/src/lib/builder/buildTypeDefinitions.ts b/packages/cli/src/lib/builder/buildTypeDefinitions.ts deleted file mode 100644 index 3bf3a4495f..0000000000 --- a/packages/cli/src/lib/builder/buildTypeDefinitions.ts +++ /dev/null @@ -1,129 +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 fs from 'fs-extra'; -import chalk from 'chalk'; -import { relative as relativePath, resolve as resolvePath } from 'path'; -import { paths } from '../paths'; -import { buildTypeDefinitionsWorker } from './buildTypeDefinitionsWorker'; -import { runWorkerThreads } from '../parallel'; - -// These message types are ignored since we want to avoid duplicating the logic of -// handling them correctly, and we already have the API Reports warning about them. -const ignoredMessages = new Set(['tsdoc-undefined-tag', 'ae-forgotten-export']); - -export async function buildTypeDefinitions( - targetDirs: string[] = [paths.targetDir], -) { - const packageDirs = targetDirs.map(dir => - relativePath(paths.targetRoot, dir), - ); - const entryPoints = await Promise.all( - packageDirs.map(async dir => { - const entryPoint = paths.resolveTargetRoot( - 'dist-types', - dir, - 'src/index.d.ts', - ); - - const declarationsExist = await fs.pathExists(entryPoint); - if (!declarationsExist) { - throw new Error( - `No declaration files found at ${entryPoint}, be sure to run ${chalk.bgRed.white( - 'yarn tsc', - )} to generate .d.ts files before packaging`, - ); - } - return entryPoint; - }), - ); - - const workerConfigs = packageDirs.map(packageDir => { - const targetDir = paths.resolveTargetRoot(packageDir); - const targetTypesDir = paths.resolveTargetRoot('dist-types', packageDir); - const extractorOptions = { - configObject: { - mainEntryPointFilePath: resolvePath(targetTypesDir, 'src/index.d.ts'), - bundledPackages: [], - - compiler: { - skipLibCheck: true, - tsconfigFilePath: paths.resolveTargetRoot('tsconfig.json'), - }, - - dtsRollup: { - enabled: true, - untrimmedFilePath: resolvePath(targetDir, 'dist/index.alpha.d.ts'), - betaTrimmedFilePath: resolvePath(targetDir, 'dist/index.beta.d.ts'), - publicTrimmedFilePath: resolvePath(targetDir, 'dist/index.d.ts'), - }, - - newlineKind: 'lf', - - projectFolder: targetDir, - }, - configObjectFullPath: targetDir, - packageJsonFullPath: resolvePath(targetDir, 'package.json'), - }; - return { extractorOptions, targetTypesDir }; - }); - - const typescriptDir = paths.resolveTargetRoot('node_modules/typescript'); - const hasTypescript = await fs.pathExists(typescriptDir); - const typescriptCompilerFolder = hasTypescript ? typescriptDir : undefined; - await runWorkerThreads({ - threadCount: 1, - workerData: { - entryPoints, - workerConfigs, - typescriptCompilerFolder, - }, - worker: buildTypeDefinitionsWorker, - onMessage: ({ - message, - targetTypesDir, - }: { - message: any; - targetTypesDir: string; - }) => { - if (ignoredMessages.has(message.messageId)) { - return; - } - - let text = `${message.text} (${message.messageId})`; - if (message.sourceFilePath) { - text += ' at '; - text += relativePath(targetTypesDir, message.sourceFilePath); - if (message.sourceFileLine) { - text += `:${message.sourceFileLine}`; - if (message.sourceFileColumn) { - text += `:${message.sourceFileColumn}`; - } - } - } - if (message.logLevel === 'error') { - console.error(chalk.red(`Error: ${text}`)); - } else if ( - message.logLevel === 'warning' || - message.category === 'Extractor' - ) { - console.warn(`Warning: ${text}`); - } else { - console.log(text); - } - }, - }); -} diff --git a/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts b/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts deleted file mode 100644 index c3c37d91cf..0000000000 --- a/packages/cli/src/lib/builder/buildTypeDefinitionsWorker.ts +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * NOTE: This is a worker thread function that is stringified and executed - * within a `worker_threads.Worker`. Everything in this function must - * be self-contained. - * Using TypeScript is fine as it is transpiled before being stringified. - */ -export async function buildTypeDefinitionsWorker( - workerData: any, - sendMessage: (message: any) => void, -) { - try { - require('@microsoft/api-extractor'); - } catch (error) { - throw new Error( - 'Failed to resolve @microsoft/api-extractor, it must best installed ' + - 'as a dependency of your project in order to use experimental type builds', - ); - } - - const { dirname } = require('path'); - const { entryPoints, workerConfigs, typescriptCompilerFolder } = workerData; - - const apiExtractor = require('@microsoft/api-extractor'); - const { Extractor, ExtractorConfig, CompilerState } = apiExtractor; - - /** - * All of this monkey patching below is because Material UI has these bare package.json file as a method - * for making TypeScript accept imports like `@material-ui/core/Button`, and improve tree-shaking - * by declaring them side effect free. - * - * The package.json lookup logic in api-extractor really doesn't like that though, as it enforces - * that the 'name' field exists in all package.json files that it discovers. This below is just - * making sure that we ignore those file package.json files instead of crashing. - */ - const { - PackageJsonLookup, - // eslint-disable-next-line @backstage/no-undeclared-imports - } = require('@rushstack/node-core-library/lib/PackageJsonLookup'); - - const old = PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor; - PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor = - function tryGetPackageJsonFilePathForPatch(path: string) { - if ( - path.includes('@material-ui') && - !dirname(path).endsWith('@material-ui') - ) { - return undefined; - } - return old.call(this, path); - }; - - let compilerState; - for (const { extractorOptions, targetTypesDir } of workerConfigs) { - const extractorConfig = ExtractorConfig.prepare(extractorOptions); - - if (!compilerState) { - compilerState = CompilerState.create(extractorConfig, { - additionalEntryPoints: entryPoints, - }); - } - - const extractorResult = Extractor.invoke(extractorConfig, { - compilerState, - localBuild: false, - typescriptCompilerFolder, - showVerboseMessages: false, - showDiagnostics: false, - messageCallback: (message: any) => { - message.handled = true; - sendMessage({ message, targetTypesDir }); - }, - }); - - if (!extractorResult.succeeded) { - throw new Error( - `Type definition build completed with ${extractorResult.errorCount} errors` + - ` and ${extractorResult.warningCount} warnings`, - ); - } - } -} diff --git a/packages/cli/src/lib/builder/config.ts b/packages/cli/src/lib/builder/config.ts index f614581320..41e9dd6906 100644 --- a/packages/cli/src/lib/builder/config.ts +++ b/packages/cli/src/lib/builder/config.ts @@ -151,7 +151,7 @@ export async function makeRollupConfigs( }); } - if (options.outputs.has(Output.types) && !options.useApiExtractor) { + if (options.outputs.has(Output.types)) { const input = Object.fromEntries( scriptEntryPoints.map(e => [ e.name, diff --git a/packages/cli/src/lib/builder/packager.ts b/packages/cli/src/lib/builder/packager.ts index 780f9c2cfe..be9bc9a6ea 100644 --- a/packages/cli/src/lib/builder/packager.ts +++ b/packages/cli/src/lib/builder/packager.ts @@ -21,7 +21,6 @@ import { relative as relativePath, resolve as resolvePath } from 'path'; import { paths } from '../paths'; import { makeRollupConfigs } from './config'; import { BuildOptions, Output } from './types'; -import { buildTypeDefinitions } from './buildTypeDefinitions'; import { PackageRoles } from '@backstage/cli-node'; import { runParallelWorkers } from '../parallel'; @@ -112,10 +111,6 @@ export const buildPackage = async (options: BuildOptions) => { const buildTasks = rollupConfigs.map(rollupBuild); - if (options.outputs.has(Output.types) && options.useApiExtractor) { - buildTasks.push(buildTypeDefinitions()); - } - await Promise.all(buildTasks); }; @@ -131,18 +126,6 @@ export const buildPackages = async (options: BuildOptions[]) => { const buildTasks = rollupConfigs.flat().map(opts => () => rollupBuild(opts)); - const typeDefinitionTargetDirs = options - .filter( - ({ outputs, useApiExtractor }) => - outputs.has(Output.types) && useApiExtractor, - ) - .map(_ => _.targetDir!); - - if (typeDefinitionTargetDirs.length > 0) { - // Make sure this one is started first - buildTasks.unshift(() => buildTypeDefinitions(typeDefinitionTargetDirs)); - } - await runParallelWorkers({ items: buildTasks, worker: async task => task(), diff --git a/packages/cli/src/lib/builder/types.ts b/packages/cli/src/lib/builder/types.ts index 330419235f..7d9c34fe29 100644 --- a/packages/cli/src/lib/builder/types.ts +++ b/packages/cli/src/lib/builder/types.ts @@ -28,5 +28,4 @@ export type BuildOptions = { packageJson?: BackstagePackageJson; outputs: Set; minify?: boolean; - useApiExtractor?: boolean; }; diff --git a/packages/cli/src/lib/bundler/config.ts b/packages/cli/src/lib/bundler/config.ts index 534ffe8c58..542cc83ce9 100644 --- a/packages/cli/src/lib/bundler/config.ts +++ b/packages/cli/src/lib/bundler/config.ts @@ -39,6 +39,7 @@ import { runPlain } from '../run'; import { transforms } from './transforms'; import { version } from '../../lib/version'; import yn from 'yn'; +import { hasReactDomClient } from './hasReactDomClient'; const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE'; @@ -81,15 +82,6 @@ async function readBuildInfo() { }; } -function hasReactDomClient() { - try { - require.resolve('react-dom/client'); - return true; - } catch { - return false; - } -} - export async function createConfig( paths: BundlingPaths, options: BundlingOptions, diff --git a/packages/cli/src/lib/bundler/hasReactDomClient.ts b/packages/cli/src/lib/bundler/hasReactDomClient.ts new file mode 100644 index 0000000000..e7ef7bbac9 --- /dev/null +++ b/packages/cli/src/lib/bundler/hasReactDomClient.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ +export function hasReactDomClient() { + try { + require.resolve('react-dom/client'); + return true; + } catch { + return false; + } +} diff --git a/packages/cli/src/lib/bundler/packageDetection.ts b/packages/cli/src/lib/bundler/packageDetection.ts index 2095fe23e7..3aa0955c06 100644 --- a/packages/cli/src/lib/bundler/packageDetection.ts +++ b/packages/cli/src/lib/bundler/packageDetection.ts @@ -77,24 +77,28 @@ async function detectPackages( return []; } - const depPackageJson: BackstagePackageJson = require(require.resolve( - `${depName}/package.json`, - { paths: [targetPath] }, - )); - if ( - ['frontend-plugin', 'frontend-plugin-module'].includes( - depPackageJson.backstage?.role ?? '', - ) - ) { - // Include alpha entry point if available. If there's no default export it will be ignored - const exp = depPackageJson.exports; - if (exp && typeof exp === 'object' && './alpha' in exp) { - return [ - { name: depName, import: depName }, - { name: depName, export: './alpha', import: `${depName}/alpha` }, - ]; + try { + const depPackageJson: BackstagePackageJson = require(require.resolve( + `${depName}/package.json`, + { paths: [targetPath] }, + )); + if ( + ['frontend-plugin', 'frontend-plugin-module'].includes( + depPackageJson.backstage?.role ?? '', + ) + ) { + // Include alpha entry point if available. If there's no default export it will be ignored + const exp = depPackageJson.exports; + if (exp && typeof exp === 'object' && './alpha' in exp) { + return [ + { name: depName, import: depName }, + { name: depName, export: './alpha', import: `${depName}/alpha` }, + ]; + } + return [{ name: depName, import: depName }]; } - return [{ name: depName, import: depName }]; + } catch { + /* ignore packages that don't make package.json available */ } return []; }); diff --git a/packages/cli/src/lib/bundler/server.ts b/packages/cli/src/lib/bundler/server.ts index 78f6bb7823..bd70b2b074 100644 --- a/packages/cli/src/lib/bundler/server.ts +++ b/packages/cli/src/lib/bundler/server.ts @@ -34,6 +34,7 @@ import { createConfig, resolveBaseUrl } from './config'; import { createDetectedModulesEntryPoint } from './packageDetection'; import { resolveBundlingPaths } from './paths'; import { ServeOptions } from './types'; +import { hasReactDomClient } from './hasReactDomClient'; export async function serveBundle(options: ServeOptions) { const paths = resolveBundlingPaths(options); @@ -77,7 +78,9 @@ export async function serveBundle(options: ServeOptions) { const { name } = await fs.readJson(libPaths.resolveTarget('package.json')); - let server: WebpackDevServer | undefined = undefined; + let webpackServer: WebpackDevServer | undefined = undefined; + let viteServer: import('vite').ViteDevServer | undefined = undefined; + let latestFrontendAppConfigs: AppConfig[] = []; const cliConfig = await loadCliConfig({ @@ -86,7 +89,9 @@ export async function serveBundle(options: ServeOptions) { withFilteredKeys: true, watch(appConfigs) { latestFrontendAppConfigs = appConfigs; - server?.invalidate(); + + webpackServer?.invalidate(); + viteServer?.restart(); }, }); latestFrontendAppConfigs = cliConfig.frontendAppConfigs; @@ -123,7 +128,8 @@ export async function serveBundle(options: ServeOptions) { config: fullConfig, targetPath: paths.targetPath, watch() { - server?.invalidate(); + webpackServer?.invalidate(); + viteServer?.restart(); }, }); @@ -139,64 +145,111 @@ export async function serveBundle(options: ServeOptions) { additionalEntryPoints: detectedModulesEntryPoint, }); - const compiler = webpack(config); - - server = new WebpackDevServer( - { - hot: !process.env.CI, - devMiddleware: { - publicPath: config.output?.publicPath as string, - stats: 'errors-warnings', + if (process.env.EXPERIMENTAL_VITE) { + const { default: vite } = await import('vite'); + const { default: viteReact } = await import('@vitejs/plugin-react'); + const { nodePolyfills: viteNodePolyfills } = await import( + 'vite-plugin-node-polyfills' + ); + const { createHtmlPlugin: viteHtml } = await import('vite-plugin-html'); + viteServer = await vite.createServer({ + define: { + global: 'window', + 'process.argv': JSON.stringify(process.argv), + 'process.env.APP_CONFIG': JSON.stringify(cliConfig.frontendAppConfigs), + // This allows for conditional imports of react-dom/client, since there's no way + // to check for presence of it in source code without module resolution errors. + 'process.env.HAS_REACT_DOM_CLIENT': JSON.stringify(hasReactDomClient()), }, - static: paths.targetPublic - ? { - publicPath: config.output?.publicPath as string, - directory: paths.targetPublic, - } - : undefined, - historyApiFallback: { - // Paths with dots should still use the history fallback. - // See https://github.com/facebookincubator/create-react-app/issues/387. - disableDotRule: true, - - // The index needs to be rewritten relative to the new public path, including subroutes. - index: `${config.output?.publicPath}index.html`, + plugins: [ + viteReact(), + viteNodePolyfills(), + viteHtml({ + entry: paths.targetEntry, + // todo(blam): we should look at contributing to thPe plugin here + // to support absolute paths, but works in the interim at least. + template: 'public/index.html', + inject: { + data: { + config: frontendConfig, + publicPath: config.output?.publicPath, + }, + }, + }), + ], + server: { + host, + port, }, - https: - url.protocol === 'https:' - ? { - cert: fullConfig.getString('app.https.certificate.cert'), - key: fullConfig.getString('app.https.certificate.key'), - } - : false, - host, - port, - proxy: targetPkg.proxy, - // When the dev server is behind a proxy, the host and public hostname differ - allowedHosts: [url.hostname], - client: { - webSocketURL: 'auto://0.0.0.0:0/ws', - }, - } as any, - compiler as any, - ); - - await new Promise((resolve, reject) => { - server?.startCallback((err?: Error) => { - if (err) { - reject(err); - return; - } - - openBrowser(url.href); - resolve(); + publicDir: paths.targetPublic, + root: paths.targetPath, }); + } else { + const compiler = webpack(config); + + webpackServer = new WebpackDevServer( + { + hot: !process.env.CI, + devMiddleware: { + publicPath: config.output?.publicPath as string, + stats: 'errors-warnings', + }, + static: paths.targetPublic + ? { + publicPath: config.output?.publicPath as string, + directory: paths.targetPublic, + } + : undefined, + historyApiFallback: { + // Paths with dots should still use the history fallback. + // See https://github.com/facebookincubator/create-react-app/issues/387. + disableDotRule: true, + + // The index needs to be rewritten relative to the new public path, including subroutes. + index: `${config.output?.publicPath}index.html`, + }, + https: + url.protocol === 'https:' + ? { + cert: fullConfig.getString('app.https.certificate.cert'), + key: fullConfig.getString('app.https.certificate.key'), + } + : false, + host, + port, + proxy: targetPkg.proxy, + // When the dev server is behind a proxy, the host and public hostname differ + allowedHosts: [url.hostname], + client: { + webSocketURL: 'auto://0.0.0.0:0/ws', + }, + }, + compiler, + ); + } + + await viteServer?.listen(); + await new Promise(async (resolve, reject) => { + if (webpackServer) { + webpackServer.startCallback((err?: Error) => { + if (err) { + reject(err); + return; + } + resolve(); + }); + } else { + resolve(); + } }); + openBrowser(url.href); + const waitForExit = async () => { for (const signal of ['SIGINT', 'SIGTERM'] as const) { process.on(signal, () => { - server?.close(); + webpackServer?.close(); + viteServer?.close(); // exit instead of resolve. The process is shutting down and resolving a promise here logs an error process.exit(); }); diff --git a/packages/cli/src/lib/experimental/startBackendExperimental.ts b/packages/cli/src/lib/experimental/startBackendExperimental.ts index c4271deb3d..af6e33a1e4 100644 --- a/packages/cli/src/lib/experimental/startBackendExperimental.ts +++ b/packages/cli/src/lib/experimental/startBackendExperimental.ts @@ -27,11 +27,14 @@ import { isAbsolute as isAbsolutePath } from 'path'; import { paths } from '../paths'; import spawn from 'cross-spawn'; +const [nodeMajor, nodeMinor] = process.versions.node.split('.').map(Number); +const supportsModuleLoaderRegister = nodeMajor >= 20 && nodeMinor >= 6; + const loaderArgs = [ '--require', - require.resolve('@esbuild-kit/cjs-loader'), - '--loader', - pathToFileURL(require.resolve('@esbuild-kit/esm-loader')).toString(), // Windows prefers a URL here + require.resolve('tsx/preflight'), + supportsModuleLoaderRegister ? '--import' : '--loader', + pathToFileURL(require.resolve('tsx')).toString(), // Windows prefers a URL here ]; export async function startBackendExperimental(options: BackendServeOptions) { @@ -95,7 +98,7 @@ export async function startBackendExperimental(options: BackendServeOptions) { process.execPath, [...loaderArgs, ...optionArgs, options.entry, ...userArgs], { - stdio: ['inherit', 'inherit', 'inherit', 'ipc'], + stdio: ['ignore', 'inherit', 'inherit', 'ipc'], env: { ...process.env, BACKSTAGE_CLI_CHANNEL: '1', diff --git a/packages/cli/src/lib/packager/createDistWorkspace.ts b/packages/cli/src/lib/packager/createDistWorkspace.ts index e91924dd37..00b8784d6d 100644 --- a/packages/cli/src/lib/packager/createDistWorkspace.ts +++ b/packages/cli/src/lib/packager/createDistWorkspace.ts @@ -206,7 +206,6 @@ export async function createDistWorkspace( logPrefix: `${chalk.cyan(relativePath(paths.targetRoot, pkg.dir))}: `, // No need to detect these for the backend builds, we assume no minification or types minify: false, - useApiExtractor: false, }); } } diff --git a/packages/cli/src/lib/packager/productionPack.ts b/packages/cli/src/lib/packager/productionPack.ts index aea5df649a..6a59924a30 100644 --- a/packages/cli/src/lib/packager/productionPack.ts +++ b/packages/cli/src/lib/packager/productionPack.ts @@ -23,7 +23,7 @@ import { readEntryPoints } from '../entryPoints'; const PKG_PATH = 'package.json'; const PKG_BACKUP_PATH = 'package.json-prepack'; -const SKIPPED_KEYS = ['access', 'registry', 'tag', 'alphaTypes', 'betaTypes']; +const SKIPPED_KEYS = ['access', 'registry', 'tag']; const SCRIPT_EXTS = ['.js', '.jsx', '.ts', '.tsx']; interface ProductionPackOptions { @@ -42,14 +42,6 @@ export async function productionPack(options: ProductionPackOptions) { await fs.writeFile(PKG_BACKUP_PATH, pkgContent); } - const hasStageEntry = - !!pkg.publishConfig?.alphaTypes || !!pkg.publishConfig?.betaTypes; - if (pkg.exports && hasStageEntry) { - throw new Error( - 'Combining both exports and alpha/beta types is not supported', - ); - } - // This mutates pkg to fill in index exports, so call it before applying publishConfig const writeCompatibilityEntryPoints = await prepareExportsEntryPoints( pkg, @@ -99,12 +91,6 @@ export async function productionPack(options: ProductionPackOptions) { await fs.writeJson(pkgPath, pkg, { encoding: 'utf8', spaces: 2 }); } - if (publishConfig.alphaTypes) { - await writeReleaseStageEntrypoint(pkg, 'alpha', targetDir ?? packageDir); - } - if (publishConfig.betaTypes) { - await writeReleaseStageEntrypoint(pkg, 'beta', targetDir ?? packageDir); - } if (writeCompatibilityEntryPoints) { await writeCompatibilityEntryPoints(targetDir ?? packageDir); } @@ -118,12 +104,6 @@ export async function revertProductionPack(packageDir: string) { // Check if we're shipping types for other release stages, clean up in that case const pkg = await fs.readJson(PKG_PATH); - if (pkg.publishConfig?.alphaTypes) { - await fs.remove(resolvePath(packageDir, 'alpha')); - } - if (pkg.publishConfig?.betaTypes) { - await fs.remove(resolvePath(packageDir, 'beta')); - } // Remove any extra entrypoint backwards compatibility directories const entryPoints = readEntryPoints(pkg); @@ -140,32 +120,6 @@ export async function revertProductionPack(packageDir: string) { } } -function resolveEntrypoint(pkg: any, name: string) { - const targetEntry = pkg.publishConfig[name] || pkg[name]; - return targetEntry && posixPath.join('..', targetEntry); -} - -// Writes e.g. alpha/package.json -async function writeReleaseStageEntrypoint( - pkg: BackstagePackageJson, - stage: 'alpha' | 'beta', - targetDir: string, -) { - await fs.ensureDir(resolvePath(targetDir, stage)); - await fs.writeJson( - resolvePath(targetDir, stage, PKG_PATH), - { - name: pkg.name, - version: pkg.version, - main: resolveEntrypoint(pkg, 'main'), - module: resolveEntrypoint(pkg, 'module'), - browser: resolveEntrypoint(pkg, 'browser'), - types: posixPath.join('..', pkg.publishConfig![`${stage}Types`]!), - }, - { encoding: 'utf8', spaces: 2 }, - ); -} - const EXPORT_MAP = { import: '.esm.js', require: '.cjs.js', diff --git a/packages/config-loader/CHANGELOG.md b/packages/config-loader/CHANGELOG.md index b0d59d8a8b..269c1b5c31 100644 --- a/packages/config-loader/CHANGELOG.md +++ b/packages/config-loader/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/config-loader +## 1.5.2-next.0 + +### Patch Changes + +- 22ca64f117: Correctly resolve config targets into absolute paths +- Updated dependencies + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 1.5.1 ### Patch Changes diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index fa2ad992c6..652820d5f0 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/config-loader", "description": "Config loading functionality used by Backstage backend, and CLI", - "version": "1.5.1", + "version": "1.5.2-next.0", "publishConfig": { "access": "public", "main": "dist/index.cjs.js", diff --git a/packages/config-loader/src/sources/ConfigSources.test.ts b/packages/config-loader/src/sources/ConfigSources.test.ts index f095bbec42..4b14572666 100644 --- a/packages/config-loader/src/sources/ConfigSources.test.ts +++ b/packages/config-loader/src/sources/ConfigSources.test.ts @@ -93,7 +93,7 @@ describe('ConfigSources', () => { targets: [{ type: 'path', target: '/config.yaml' }], }), ), - ).toEqual([{ name: 'FileConfigSource', path: '/config.yaml' }]); + ).toEqual([{ name: 'FileConfigSource', path: `${root}config.yaml` }]); expect( mergeSources( @@ -201,9 +201,9 @@ describe('ConfigSources', () => { ]), ), ).toEqual([ - { name: 'FileConfigSource', path: '/a.yaml' }, - { name: 'FileConfigSource', path: '/b.yaml' }, - { name: 'FileConfigSource', path: '/c.yaml' }, + { name: 'FileConfigSource', path: `${root}a.yaml` }, + { name: 'FileConfigSource', path: `${root}b.yaml` }, + { name: 'FileConfigSource', path: `${root}c.yaml` }, ]); }); diff --git a/packages/core-app-api/CHANGELOG.md b/packages/core-app-api/CHANGELOG.md index bce1da4c9d..3ce35f99c0 100644 --- a/packages/core-app-api/CHANGELOG.md +++ b/packages/core-app-api/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/core-app-api +## 1.11.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 89d13e5618: Add current and default scopes when refreshing session +- 9ab0572217: Add component data `core.type` marker for `AppRouter` and `FlatRoutes`. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 1.11.0 ### Minor Changes diff --git a/packages/core-app-api/package.json b/packages/core-app-api/package.json index e6792a3402..2a5a3cf0c8 100644 --- a/packages/core-app-api/package.json +++ b/packages/core-app-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-app-api", "description": "Core app API used by Backstage apps", - "version": "1.11.0", + "version": "1.11.1-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts index 1c51dd0698..b6d59bd3f2 100644 --- a/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts +++ b/packages/core-app-api/src/apis/implementations/auth/microsoft/MicrosoftAuth.test.ts @@ -44,9 +44,7 @@ describe('MicrosoftAuth', () => { return res( ctx.json({ providerInfo: { - accessToken: scopeParam - ? 'tokenForOtherResource' - : 'tokenForGrantScopes', + accessToken: `token:${scopeParam}`, scope: scopeParam || 'grant-resource/scope', }, }), @@ -59,7 +57,9 @@ describe('MicrosoftAuth', () => { it('gets access token with requested scopes for grant', async () => { const accessToken = await microsoftAuth.getAccessToken(); - expect(accessToken).toEqual('tokenForGrantScopes'); + expect(accessToken).toEqual( + 'token:openid offline_access profile email User.Read', + ); }); it('gets access token for other consented scopes besides those directly granted', async () => { @@ -67,7 +67,7 @@ describe('MicrosoftAuth', () => { 'azure-resource/scope', ); - expect(accessToken).toEqual('tokenForOtherResource'); + expect(accessToken).toEqual('token:azure-resource/scope offline_access'); }); it('fails when requesting scopes for multiple resources at once', async () => { @@ -86,7 +86,7 @@ describe('MicrosoftAuth', () => { ); await expect(accessTokenPromise).resolves.toEqual( - 'tokenForOtherResource', + 'token:same-resource/one-scope same-resource/other-scope offline_access', ); }); }); 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 23432856cb..e26934f393 100644 --- a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts +++ b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.test.ts @@ -47,7 +47,7 @@ describe('RefreshingAuthSessionManager', () => { await manager.getSession({}); expect(createSession).toHaveBeenCalledTimes(1); - expect(refreshSession).toHaveBeenCalledWith(undefined); + expect(refreshSession).toHaveBeenCalledWith(new Set()); expect(stateSubscriber.mock.calls).toEqual([ [SessionState.SignedOut], [SessionState.SignedIn], @@ -134,7 +134,7 @@ describe('RefreshingAuthSessionManager', () => { expect(await manager.getSession({ optional: true })).toBe(undefined); expect(createSession).toHaveBeenCalledTimes(0); - expect(refreshSession).toHaveBeenCalledWith(undefined); + expect(refreshSession).toHaveBeenCalledWith(new Set()); }); it('should forward option to instantly show auth popup and not attempt refresh', async () => { diff --git a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts index c04eb637de..c92fe9fb20 100644 --- a/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts +++ b/packages/core-app-api/src/lib/AuthSessionManager/RefreshingAuthSessionManager.ts @@ -137,7 +137,9 @@ export class RefreshingAuthSessionManager implements SessionManager { return this.refreshPromise; } - this.refreshPromise = this.connector.refreshSession(scopes); + this.refreshPromise = this.connector.refreshSession( + this.helper.getExtendedScope(this.currentSession, scopes), + ); try { const session = await this.refreshPromise; diff --git a/packages/core-compat-api/CHANGELOG.md b/packages/core-compat-api/CHANGELOG.md new file mode 100644 index 0000000000..348132ac66 --- /dev/null +++ b/packages/core-compat-api/CHANGELOG.md @@ -0,0 +1,9 @@ +# @backstage/core-compat-api + +## 0.0.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 diff --git a/packages/core-compat-api/package.json b/packages/core-compat-api/package.json index d27049c2a4..0753917fd8 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.0.0", + "version": "0.0.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/core-compat-api/src/convertLegacyApp.ts b/packages/core-compat-api/src/convertLegacyApp.ts index 59cb136c65..93521b3955 100644 --- a/packages/core-compat-api/src/convertLegacyApp.ts +++ b/packages/core-compat-api/src/convertLegacyApp.ts @@ -113,18 +113,18 @@ export function convertLegacyApp( output: { element: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { + factory({ inputs }) { // Clone the root element, this replaces the FlatRoutes declared in the app with out content input - bind({ + return { element: React.cloneElement(rootEl, undefined, inputs.content.element), - }); + }; }, }); const CoreNavOverride = createExtension({ id: 'core.nav', attachTo: { id: 'core.layout', input: 'nav' }, output: {}, - factory() {}, + factory: () => ({}), disabled: true, }); diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 3ee6eae062..5b6718e958 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/core-components +## 0.13.7-next.0 + +### Patch Changes + +- 81c8db2088: Fix `RoutedTabs` so that it does not explode without tabs. +- 6c2b872153: Add official support for React 18. +- 7bdc1b0a12: Fixed compatibility with Safari <16.3 by eliminating RegEx lookbehind in `extractInitials`. + + This PR also changed how initials are generated resulting in _John Jonathan Doe_ => _JD_ instead of _JJ_. + +- 71c97e7d73: Fixed the type declaration of `DependencyGraphProps`, the `defs` prop now expects `JSX.Element`s. +- Updated dependencies + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.13.6 ### Patch Changes diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 842a52df43..4c63eeaac5 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-components", "description": "Core components used by Backstage plugins and apps", - "version": "0.13.6", + "version": "0.13.7-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/core-plugin-api/CHANGELOG.md b/packages/core-plugin-api/CHANGELOG.md index e35fd4d6a3..23dbfc0968 100644 --- a/packages/core-plugin-api/CHANGELOG.md +++ b/packages/core-plugin-api/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/core-plugin-api +## 1.8.0-next.0 + +### Minor Changes + +- 1e5b7d993a: `IconComponent` can now have a `fontSize` of `inherit`, which is useful for in-line icons. +- cb6db75bc2: Introduced `AnyRouteRefParams` as a replacement for `AnyParams`, which is now deprecated. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- cb6db75bc2: Deprecated several types related to the routing system that are scheduled to be removed, as well as several fields on the route ref types themselves. +- 68fc9dc60e: Added a new `/alpha` export `convertLegacyRouteRef`, which is a temporary utility to allow existing route refs to be used with the new experimental packages. +- Updated dependencies + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 1.7.0 ### Minor Changes diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 789fc2bfdc..1fa40e2d0c 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/api-report.md @@ -506,10 +506,10 @@ export const googleAuthApiRef: ApiRef< // @public export type IconComponent = ComponentType< | { - fontSize?: 'large' | 'small' | 'default'; + fontSize?: 'large' | 'small' | 'default' | 'inherit'; } | { - fontSize?: 'medium' | 'large' | 'small'; + fontSize?: 'medium' | 'large' | 'small' | 'inherit'; } >; diff --git a/packages/core-plugin-api/package.json b/packages/core-plugin-api/package.json index 42f3b8b4d4..5aac6b3fb7 100644 --- a/packages/core-plugin-api/package.json +++ b/packages/core-plugin-api/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/core-plugin-api", "description": "Core API used by Backstage plugins", - "version": "1.7.0", + "version": "1.8.0-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/core-plugin-api/src/icons/types.ts b/packages/core-plugin-api/src/icons/types.ts index aa264294e3..4d54629de2 100644 --- a/packages/core-plugin-api/src/icons/types.ts +++ b/packages/core-plugin-api/src/icons/types.ts @@ -36,10 +36,10 @@ import { ComponentType } from 'react'; export type IconComponent = ComponentType< /* Material UI v4 */ | { - fontSize?: 'large' | 'small' | 'default'; + fontSize?: 'large' | 'small' | 'default' | 'inherit'; } /* Material UI v5: https://mui.com/material-ui/migration/v5-component-changes/#icon */ | { - fontSize?: 'medium' | 'large' | 'small'; + fontSize?: 'medium' | 'large' | 'small' | 'inherit'; } >; diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 25728675d6..89a46a3b18 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/create-app +## 0.5.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- ae1602e54d: If create app installs dependencies, don't suggest to user that they also need to do it. +- Updated dependencies + - @backstage/cli-common@0.1.13 + ## 0.5.6 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 597908c894..bf7780bd49 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "A CLI that helps you create your own Backstage app", - "version": "0.5.6", + "version": "0.5.7-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/create-app/src/createApp.ts b/packages/create-app/src/createApp.ts index 09cede933d..af6b0c9c70 100644 --- a/packages/create-app/src/createApp.ts +++ b/packages/create-app/src/createApp.ts @@ -129,7 +129,7 @@ export default async (opts: OptionValues): Promise => { ); Task.log(); Task.section('All set! Now you might want to'); - if (!opts.skipInstall) { + if (opts.skipInstall) { Task.log( ` Install the dependencies: ${chalk.cyan( `cd ${opts.path ?? answers.name} && yarn install`, diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 3bb6f8e324..3d800a8bf0 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/dev-utils +## 1.0.23-next.0 + +### Patch Changes + +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 1.0.22 ### Patch Changes diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index d860d67070..f1d4420922 100644 --- a/packages/dev-utils/package.json +++ b/packages/dev-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/dev-utils", "description": "Utilities for developing Backstage plugins.", - "version": "1.0.22", + "version": "1.0.23-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/e2e-test/CHANGELOG.md b/packages/e2e-test/CHANGELOG.md index 37d8b1d24a..4b50bb883e 100644 --- a/packages/e2e-test/CHANGELOG.md +++ b/packages/e2e-test/CHANGELOG.md @@ -1,5 +1,14 @@ # e2e-test +## 0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/create-app@0.5.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + ## 0.2.8 ### Patch Changes diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index f6e90e116e..70e58832f2 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -1,7 +1,7 @@ { "name": "e2e-test", "description": "E2E test for verifying Backstage packages", - "version": "0.2.8", + "version": "0.2.9-next.0", "private": true, "backstage": { "role": "cli" diff --git a/packages/frontend-app-api/CHANGELOG.md b/packages/frontend-app-api/CHANGELOG.md index 96bf43f73c..84d19133cb 100644 --- a/packages/frontend-app-api/CHANGELOG.md +++ b/packages/frontend-app-api/CHANGELOG.md @@ -1,5 +1,29 @@ # @backstage/frontend-app-api +## 0.3.0-next.0 + +### Minor Changes + +- 68fc9dc60e: Added the ability to configure bound routes through `app.routes.bindings`. The routing system used by `createApp` has been replaced by one that only supports route refs of the new format from `@backstage/frontend-plugin-api`. The requirement for route refs to have the same ID as their associated extension has been removed. + +### Patch Changes + +- e28d379e32: Refactor internal extension instance system into an app graph. +- 6c2b872153: Add official support for React 18. +- dc613f9bcf: Updated `app.extensions` configuration schema. +- 685a4c8901: Installed features are now deduplicated both by reference and ID when available. Features passed to `createApp` now override both discovered and loaded features. +- bb98953cb9: Register default implementation for the `Translation API` on the new `createApp`. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-graphiql@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/frontend-app-api/package.json b/packages/frontend-app-api/package.json index d4a595707a..b3f3a98dab 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.2.0", + "version": "0.3.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-app-api/src/extensions/Core.tsx b/packages/frontend-app-api/src/extensions/Core.tsx index 6efe3984dc..60772d435c 100644 --- a/packages/frontend-app-api/src/extensions/Core.tsx +++ b/packages/frontend-app-api/src/extensions/Core.tsx @@ -22,7 +22,7 @@ import { export const Core = createExtension({ id: 'core', - attachTo: { id: 'root', input: 'default' }, + attachTo: { id: 'root', input: 'default' }, // ignored inputs: { apis: createExtensionInput({ api: coreExtensionData.apiFactory, @@ -40,9 +40,9 @@ export const Core = createExtension({ output: { root: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { - bind({ + factory({ inputs }) { + return { root: inputs.root.element, - }); + }; }, }); diff --git a/packages/frontend-app-api/src/extensions/CoreLayout.tsx b/packages/frontend-app-api/src/extensions/CoreLayout.tsx index bf0f382cfc..97f49acac9 100644 --- a/packages/frontend-app-api/src/extensions/CoreLayout.tsx +++ b/packages/frontend-app-api/src/extensions/CoreLayout.tsx @@ -42,14 +42,14 @@ export const CoreLayout = createExtension({ output: { element: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { - bind({ + factory({ inputs }) { + return { element: ( {inputs.nav.element} {inputs.content.element} ), - }); + }; }, }); diff --git a/packages/frontend-app-api/src/extensions/CoreNav.tsx b/packages/frontend-app-api/src/extensions/CoreNav.tsx index cd12ca129a..37ce50276b 100644 --- a/packages/frontend-app-api/src/extensions/CoreNav.tsx +++ b/packages/frontend-app-api/src/extensions/CoreNav.tsx @@ -82,8 +82,8 @@ export const CoreNav = createExtension({ output: { element: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { - bind({ + factory({ inputs }) { + return { element: ( @@ -93,6 +93,6 @@ export const CoreNav = createExtension({ ))} ), - }); + }; }, }); diff --git a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx index 8d22b40eec..985fd07be9 100644 --- a/packages/frontend-app-api/src/extensions/CoreRoutes.tsx +++ b/packages/frontend-app-api/src/extensions/CoreRoutes.tsx @@ -35,7 +35,7 @@ export const CoreRoutes = createExtension({ output: { element: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { + factory({ inputs }) { const Routes = () => { const element = useRoutes( inputs.routes.map(route => ({ @@ -46,8 +46,8 @@ export const CoreRoutes = createExtension({ return element; }; - bind({ + return { element: , - }); + }; }, }); diff --git a/packages/frontend-app-api/src/graph/createAppGraph.test.ts b/packages/frontend-app-api/src/graph/createAppGraph.test.ts new file mode 100644 index 0000000000..1a046b210b --- /dev/null +++ b/packages/frontend-app-api/src/graph/createAppGraph.test.ts @@ -0,0 +1,123 @@ +/* + * 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 { + createExtension, + createExtensionOverrides, + createPlugin, +} from '@backstage/frontend-plugin-api'; +import { MockConfigApi } from '@backstage/test-utils'; +import { createAppGraph } from './createAppGraph'; + +const extBase = { + id: 'test', + attachTo: { id: 'core', input: 'root' }, + output: {}, + factory: () => ({}), +}; + +describe('createAppGraph', () => { + it('throws an error when a core extension is parametrized', () => { + const config = new MockConfigApi({ + app: { + extensions: [ + { + core: {}, + }, + ], + }, + }); + const features = [ + createPlugin({ + id: 'plugin', + extensions: [], + }), + ]; + expect(() => + createAppGraph({ features, config, builtinExtensions: [] }), + ).toThrow("Configuration of the 'core' extension is forbidden"); + }); + + it('throws an error when a core extension is overridden', () => { + const config = new MockConfigApi({}); + const features = [ + createPlugin({ + id: 'plugin', + extensions: [ + createExtension({ + id: 'core', + attachTo: { id: 'core.routes', input: 'route' }, + inputs: {}, + output: {}, + factory: () => ({}), + }), + ], + }), + ]; + expect(() => + createAppGraph({ features, config, builtinExtensions: [] }), + ).toThrow( + "It is forbidden to override the following extension(s): 'core', which is done by the following plugin(s): 'plugin'", + ); + }); + + it('throws an error when duplicated extensions are detected', () => { + const config = new MockConfigApi({}); + + const ExtensionA = createExtension({ ...extBase, id: 'A' }); + + const ExtensionB = createExtension({ ...extBase, id: 'B' }); + + const PluginA = createPlugin({ + id: 'A', + extensions: [ExtensionA, ExtensionA], + }); + + const PluginB = createPlugin({ + id: 'B', + extensions: [ExtensionA, ExtensionB, ExtensionB], + }); + + const features = [PluginA, PluginB]; + + expect(() => + createAppGraph({ features, config, builtinExtensions: [] }), + ).toThrow( + "The following extensions are duplicated: The extension 'A' was provided 2 time(s) by the plugin 'A' and 1 time(s) by the plugin 'B', The extension 'B' was provided 2 time(s) by the plugin 'B'", + ); + }); + + it('throws an error when duplicated extension overrides are detected', () => { + expect(() => + createAppGraph({ + features: [ + createExtensionOverrides({ + extensions: [ + createExtension({ ...extBase, id: 'a' }), + createExtension({ ...extBase, id: 'a' }), + createExtension({ ...extBase, id: 'b' }), + ], + }), + createExtensionOverrides({ + extensions: [createExtension({ ...extBase, id: 'b' })], + }), + ], + config: new MockConfigApi({}), + builtinExtensions: [], + }), + ).toThrow('The following extensions had duplicate overrides: a, b'); + }); +}); diff --git a/packages/frontend-app-api/src/graph/createAppGraph.ts b/packages/frontend-app-api/src/graph/createAppGraph.ts new file mode 100644 index 0000000000..ace8595f53 --- /dev/null +++ b/packages/frontend-app-api/src/graph/createAppGraph.ts @@ -0,0 +1,49 @@ +/* + * 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 { + BackstagePlugin, + Extension, + ExtensionOverrides, +} from '@backstage/frontend-plugin-api'; +import { readAppExtensionsConfig } from './readAppExtensionsConfig'; +import { resolveAppGraph } from './resolveAppGraph'; +import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; +import { AppGraph } from './types'; +import { Config } from '@backstage/config'; +import { instantiateAppNodeTree } from './instantiateAppNodeTree'; + +/** @internal */ +export interface CreateAppGraphOptions { + features: (BackstagePlugin | ExtensionOverrides)[]; + builtinExtensions: Extension[]; + config: Config; +} + +/** @internal */ +export function createAppGraph(options: CreateAppGraphOptions): AppGraph { + const appGraph = resolveAppGraph( + 'core', + resolveAppNodeSpecs({ + features: options.features, + builtinExtensions: options.builtinExtensions, + parameters: readAppExtensionsConfig(options.config), + forbidden: new Set(['core']), + }), + ); + instantiateAppNodeTree(appGraph.root); + return appGraph; +} diff --git a/packages/frontend-app-api/src/graph/index.ts b/packages/frontend-app-api/src/graph/index.ts new file mode 100644 index 0000000000..4a3b54dffb --- /dev/null +++ b/packages/frontend-app-api/src/graph/index.ts @@ -0,0 +1,23 @@ +/* + * 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. + */ + +export type { + AppNode, + AppNodeEdges, + AppNodeInstance, + AppNodeSpec, +} from './types'; +export { createAppGraph } from './createAppGraph'; diff --git a/packages/frontend-app-api/src/graph/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/graph/instantiateAppNodeTree.test.ts new file mode 100644 index 0000000000..9be3482b5d --- /dev/null +++ b/packages/frontend-app-api/src/graph/instantiateAppNodeTree.test.ts @@ -0,0 +1,554 @@ +/* + * 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 { + Extension, + createExtension, + createExtensionDataRef, + createExtensionInput, + createSchemaFromZod, +} from '@backstage/frontend-plugin-api'; +import { + createAppNodeInstance, + instantiateAppNodeTree, +} from './instantiateAppNodeTree'; +import { AppNodeInstance, AppNodeSpec } from './types'; +import { resolveAppGraph } from './resolveAppGraph'; + +const testDataRef = createExtensionDataRef('test'); +const otherDataRef = createExtensionDataRef('other'); +const inputMirrorDataRef = createExtensionDataRef('mirror'); + +const simpleExtension = createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test: testDataRef, + other: otherDataRef.optional(), + }, + configSchema: createSchemaFromZod(z => + z.object({ + output: z.string().default('test'), + other: z.number().optional(), + }), + ), + factory({ config }) { + return { test: config.output, other: config.other }; + }, +}); + +function makeSpec( + extension: Extension, + config?: TConfig, +): AppNodeSpec { + return { + id: extension.id, + attachTo: extension.attachTo, + disabled: extension.disabled, + extension, + config, + source: undefined, + }; +} + +function makeInstanceWithId( + extension: Extension, + config?: TConfig, +): { id: string; instance: AppNodeInstance } { + return { + id: extension.id, + instance: createAppNodeInstance({ + spec: makeSpec(extension, config), + attachments: new Map(), + }), + }; +} + +describe('instantiateAppNodeTree', () => { + it('should instantiate a single node', () => { + const graph = resolveAppGraph('root-node', [ + { ...makeSpec(simpleExtension), id: 'root-node' }, + ]); + expect(graph.root.instance).not.toBeDefined(); + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).toBeDefined(); + expect(graph.root.instance?.getData(testDataRef)).toBe('test'); + + // Multiple calls should have no effect + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).toBeDefined(); + }); + + it('should not instantiate disabled nodes', () => { + const graph = resolveAppGraph('root-node', [ + { ...makeSpec(simpleExtension), id: 'root-node', disabled: true }, + ]); + expect(graph.root.instance).not.toBeDefined(); + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).not.toBeDefined(); + }); + + it('should instantiate a node with attachments', () => { + const graph = resolveAppGraph('root-node', [ + { + ...makeSpec( + createExtension({ + id: 'root-node', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput({ test: testDataRef }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), + }, + { + ...makeSpec(simpleExtension), + id: 'child-node', + attachTo: { id: 'root-node', input: 'test' }, + }, + ]); + + const childNode = graph.nodes.get('child-node'); + expect(childNode).toBeDefined(); + + expect(graph.root.instance).not.toBeDefined(); + expect(childNode?.instance).not.toBeDefined(); + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).toBeDefined(); + expect(childNode?.instance).toBeDefined(); + expect(graph.root.instance?.getData(inputMirrorDataRef)).toEqual({ + test: [{ test: 'test' }], + }); + + // Multiple calls should have no effect + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).toBeDefined(); + expect(childNode?.instance).toBeDefined(); + }); + + it('should not instantiate disabled attachments', () => { + const graph = resolveAppGraph('root-node', [ + { + ...makeSpec( + createExtension({ + id: 'root-node', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + test: createExtensionInput({ test: testDataRef }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), + }, + { + ...makeSpec(simpleExtension), + id: 'child-node', + attachTo: { id: 'root-node', input: 'test' }, + disabled: true, + }, + ]); + + const childNode = graph.nodes.get('child-node'); + expect(childNode).toBeDefined(); + + expect(graph.root.instance).not.toBeDefined(); + expect(childNode?.instance).not.toBeDefined(); + instantiateAppNodeTree(graph.root); + expect(graph.root.instance).toBeDefined(); + expect(childNode?.instance).not.toBeDefined(); + expect(graph.root.instance?.getData(inputMirrorDataRef)).toEqual({ + test: [], + }); + }); +}); + +describe('createAppNodeInstance', () => { + it('should create a simple extension instance', () => { + const attachments = new Map(); + const instance = createAppNodeInstance({ + spec: makeSpec(simpleExtension), + attachments, + }); + + expect(Array.from(instance.getDataRefs())).toEqual([ + testDataRef, + otherDataRef.optional(), + ]); + expect(instance.getData(testDataRef)).toEqual('test'); + }); + + it('should create an extension with different kind of inputs', () => { + const attachments = new Map([ + [ + 'optionalSingletonPresent', + [ + makeInstanceWithId(simpleExtension, { + output: 'optionalSingletonPresent', + }), + ], + ], + [ + 'singleton', + [ + makeInstanceWithId(simpleExtension, { + output: 'singleton', + other: 2, + }), + ], + ], + [ + 'many', + [ + makeInstanceWithId(simpleExtension, { output: 'many1' }), + makeInstanceWithId(simpleExtension, { output: 'many2', other: 3 }), + ], + ], + ]); + const instance = createAppNodeInstance({ + attachments, + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + optionalSingletonPresent: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true, optional: true }, + ), + optionalSingletonMissing: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true, optional: true }, + ), + singleton: createExtensionInput( + { + test: testDataRef, + other: otherDataRef.optional(), + }, + { singleton: true }, + ), + many: createExtensionInput({ + test: testDataRef, + other: otherDataRef.optional(), + }), + }, + output: { + inputMirror: inputMirrorDataRef, + }, + factory({ inputs }) { + return { inputMirror: inputs }; + }, + }), + ), + }); + + expect(Array.from(instance.getDataRefs())).toEqual([inputMirrorDataRef]); + expect(instance.getData(inputMirrorDataRef)).toEqual({ + optionalSingletonPresent: { test: 'optionalSingletonPresent' }, + singleton: { test: 'singleton', other: 2 }, + many: [{ test: 'many1' }, { test: 'many2', other: 3 }], + }); + }); + + it('should refuse to create an extension with invalid config', () => { + expect(() => + createAppNodeInstance({ + spec: { + ...makeSpec(simpleExtension), + config: { other: 'not-a-number' }, + }, + attachments: new Map(), + }), + ).toThrow( + "Invalid configuration for extension 'core.test'; caused by Error: Expected number, received string at 'other'", + ); + }); + + it('should forward extension factory errors', () => { + expect(() => + createAppNodeInstance({ + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: {}, + factory() { + const error = new Error('NOPE'); + error.name = 'NopeError'; + throw error; + }, + }), + ), + attachments: new Map(), + }), + ).toThrow( + "Failed to instantiate extension 'core.test'; caused by NopeError: NOPE", + ); + }); + + it('should refuse to create an instance with duplicate output', () => { + expect(() => + createAppNodeInstance({ + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test1: testDataRef, + test2: testDataRef, + }, + factory({}) { + return { test1: 'test', test2: 'test2' }; + }, + }), + ), + attachments: new Map(), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', duplicate extension data 'test' received via output 'test2'", + ); + }); + + it('should refuse to create an instance with disconnected output data', () => { + expect(() => + createAppNodeInstance({ + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: { + test: testDataRef, + }, + factory({}) { + return { nonexistent: 'test' } as any; + }, + }), + ), + attachments: new Map(), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', unknown output provided via 'nonexistent'", + ); + }); + + it('should refuse to create an instance with missing required input', () => { + expect(() => + createAppNodeInstance({ + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), + attachments: new Map(), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', input 'singleton' is required but was not received", + ); + }); + + it('should refuse to create an instance with undeclared inputs', () => { + expect(() => + createAppNodeInstance({ + attachments: new Map([ + [ + 'declared', + [ + makeInstanceWithId(simpleExtension, { + output: 'many1', + }), + ], + ], + [ + 'undeclared', + [ + makeInstanceWithId(simpleExtension, { + output: 'many1', + }), + ], + ], + ]), + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + declared: createExtensionInput({ + test: testDataRef, + }), + }, + output: {}, + factory: () => ({}), + }), + ), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', received undeclared input 'undeclared' from extension 'core.test'", + ); + }); + + it('should refuse to create an instance with multiple undeclared inputs', () => { + expect(() => + createAppNodeInstance({ + attachments: new Map([ + [ + 'undeclared1', + [makeInstanceWithId(simpleExtension, { output: 'many1' })], + ], + [ + 'undeclared2', + [ + makeInstanceWithId(simpleExtension, { output: 'many1' }), + makeInstanceWithId(simpleExtension, { output: 'many1' }), + ], + ], + ]), + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + output: {}, + factory: () => ({}), + }), + ), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', received undeclared inputs 'undeclared1' from extension 'core.test' and 'undeclared2' from extensions 'core.test', 'core.test'", + ); + }); + + it('should refuse to create an instance with multiple inputs for required singleton', () => { + expect(() => + createAppNodeInstance({ + attachments: new Map([ + [ + 'singleton', + [ + makeInstanceWithId(simpleExtension, { output: 'many1' }), + makeInstanceWithId(simpleExtension, { output: 'many2' }), + ], + ], + ]), + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', expected exactly one 'singleton' input but received multiple: 'core.test', 'core.test'", + ); + }); + + it('should refuse to create an instance with multiple inputs for optional singleton', () => { + expect(() => + createAppNodeInstance({ + attachments: new Map([ + [ + 'singleton', + [ + makeInstanceWithId(simpleExtension, { output: 'many1' }), + makeInstanceWithId(simpleExtension, { output: 'many2' }), + ], + ], + ]), + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + test: testDataRef, + }, + { singleton: true, optional: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', expected at most one 'singleton' input but received multiple: 'core.test', 'core.test'", + ); + }); + + it('should refuse to create an instance with multiple inputs that did not provide required data', () => { + expect(() => + createAppNodeInstance({ + attachments: new Map([ + ['singleton', [makeInstanceWithId(simpleExtension, undefined)]], + ]), + spec: makeSpec( + createExtension({ + id: 'core.test', + attachTo: { id: 'ignored', input: 'ignored' }, + inputs: { + singleton: createExtensionInput( + { + other: otherDataRef, + }, + { singleton: true }, + ), + }, + output: {}, + factory: () => ({}), + }), + ), + }), + ).toThrow( + "Failed to instantiate extension 'core.test', input 'singleton' did not receive required extension data 'other' from extension 'core.test'", + ); + }); +}); diff --git a/packages/frontend-app-api/src/graph/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/graph/instantiateAppNodeTree.ts new file mode 100644 index 0000000000..bf6028554b --- /dev/null +++ b/packages/frontend-app-api/src/graph/instantiateAppNodeTree.ts @@ -0,0 +1,191 @@ +/* + * 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 { + AnyExtensionDataMap, + AnyExtensionInputMap, + ExtensionDataRef, +} from '@backstage/frontend-plugin-api'; +import mapValues from 'lodash/mapValues'; +import { AppNode, AppNodeInstance, AppNodeSpec } from './types'; + +type Mutable = { + -readonly [P in keyof T]: T[P]; +}; + +function resolveInputData( + dataMap: AnyExtensionDataMap, + attachment: { id: string; instance: AppNodeInstance }, + inputName: string, +) { + return mapValues(dataMap, ref => { + const value = attachment.instance.getData(ref); + if (value === undefined && !ref.config.optional) { + throw new Error( + `input '${inputName}' did not receive required extension data '${ref.id}' from extension '${attachment.id}'`, + ); + } + return value; + }); +} + +function resolveInputs( + inputMap: AnyExtensionInputMap, + attachments: ReadonlyMap, +) { + const undeclaredAttachments = Array.from(attachments.entries()).filter( + ([inputName]) => inputMap[inputName] === undefined, + ); + // TODO: Make this a warning rather than an error + if (undeclaredAttachments.length > 0) { + throw new Error( + `received undeclared input${ + undeclaredAttachments.length > 1 ? 's' : '' + } ${undeclaredAttachments + .map( + ([k, exts]) => + `'${k}' from extension${exts.length > 1 ? 's' : ''} '${exts + .map(e => e.id) + .join("', '")}'`, + ) + .join(' and ')}`, + ); + } + + return mapValues(inputMap, (input, inputName) => { + const attachedNodes = attachments.get(inputName) ?? []; + + if (input.config.singleton) { + if (attachedNodes.length > 1) { + const attachedNodeIds = attachedNodes.map(e => e.id); + throw Error( + `expected ${ + input.config.optional ? 'at most' : 'exactly' + } one '${inputName}' input but received multiple: '${attachedNodeIds.join( + "', '", + )}'`, + ); + } else if (attachedNodes.length === 0) { + if (input.config.optional) { + return undefined; + } + throw Error(`input '${inputName}' is required but was not received`); + } + return resolveInputData(input.extensionData, attachedNodes[0], inputName); + } + + return attachedNodes.map(attachment => + resolveInputData(input.extensionData, attachment, inputName), + ); + }); +} + +/** @internal */ +export function createAppNodeInstance(options: { + spec: AppNodeSpec; + attachments: ReadonlyMap; +}): AppNodeInstance { + const { spec, attachments } = options; + const { id, extension, config, source } = spec; + const extensionData = new Map(); + const extensionDataRefs = new Set>(); + + let parsedConfig: unknown; + try { + parsedConfig = extension.configSchema?.parse(config ?? {}); + } catch (e) { + throw new Error( + `Invalid configuration for extension '${id}'; caused by ${e}`, + ); + } + + try { + const namedOutputs = extension.factory({ + source, + config: parsedConfig, + inputs: resolveInputs(extension.inputs, attachments), + }); + + for (const [name, output] of Object.entries(namedOutputs)) { + const ref = extension.output[name]; + if (!ref) { + throw new Error(`unknown output provided via '${name}'`); + } + if (extensionData.has(ref.id)) { + throw new Error( + `duplicate extension data '${ref.id}' received via output '${name}'`, + ); + } + extensionData.set(ref.id, output); + extensionDataRefs.add(ref); + } + } catch (e) { + throw new Error( + `Failed to instantiate extension '${id}'${ + e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}` + }`, + ); + } + + return { + getDataRefs() { + return extensionDataRefs.values(); + }, + getData(ref: ExtensionDataRef): T | undefined { + return extensionData.get(ref.id) as T | undefined; + }, + }; +} + +/** + * Starting at the provided node, instantiate all reachable nodes in the graph that have not been disabled. + * @internal + */ +export function instantiateAppNodeTree(rootNode: AppNode): void { + function createInstance(node: AppNode): AppNodeInstance | undefined { + if (node.instance) { + return node.instance; + } + if (node.spec.disabled) { + return undefined; + } + + const instantiatedAttachments = new Map< + string, + { id: string; instance: AppNodeInstance }[] + >(); + + for (const [input, children] of node.edges.attachments) { + const instantiatedChildren = children.flatMap(child => { + const childInstance = createInstance(child); + if (!childInstance) { + return []; + } + return [{ id: child.spec.id, instance: childInstance }]; + }); + instantiatedAttachments.set(input, instantiatedChildren); + } + + (node as Mutable).instance = createAppNodeInstance({ + spec: node.spec, + attachments: instantiatedAttachments, + }); + + return node.instance; + } + + createInstance(rootNode); +} diff --git a/packages/frontend-app-api/src/wiring/parameters.test.ts b/packages/frontend-app-api/src/graph/readAppExtensionsConfig.test.ts similarity index 59% rename from packages/frontend-app-api/src/wiring/parameters.test.ts rename to packages/frontend-app-api/src/graph/readAppExtensionsConfig.test.ts index 4ee420f667..91f6992f29 100644 --- a/packages/frontend-app-api/src/wiring/parameters.test.ts +++ b/packages/frontend-app-api/src/graph/readAppExtensionsConfig.test.ts @@ -15,204 +15,16 @@ */ import { ConfigReader } from '@backstage/config'; -import { - createExtensionOverrides, - createPlugin, - Extension, -} from '@backstage/frontend-plugin-api'; import { JsonValue } from '@backstage/types'; import { expandShorthandExtensionParameters, - mergeExtensionParameters, - readAppExtensionParameters, -} from './parameters'; + readAppExtensionsConfig, +} from './readAppExtensionsConfig'; -function makeExt( - id: string, - status: 'disabled' | 'enabled' = 'enabled', - attachId: string = 'root', -) { - return { - id, - attachTo: { id: attachId, input: 'default' }, - disabled: status === 'disabled', - } as Extension; -} - -describe('mergeExtensionParameters', () => { - it('should filter out disabled extension instances', () => { - expect( - mergeExtensionParameters({ - features: [], - builtinExtensions: [makeExt('a', 'disabled')], - parameters: [], - }), - ).toEqual([]); - }); - - it('should pass through extension instances', () => { - const a = makeExt('a'); - const b = makeExt('b'); - expect( - mergeExtensionParameters({ - features: [], - builtinExtensions: [a, b], - parameters: [], - }), - ).toEqual([ - { extension: a, attachTo: { id: 'root', input: 'default' } }, - { extension: b, attachTo: { id: 'root', input: 'default' } }, - ]); - }); - - it('should override attachment points', () => { - const a = makeExt('a'); - const b = makeExt('b'); - const pluginA = createPlugin({ id: 'test', extensions: [a] }); - expect( - mergeExtensionParameters({ - features: [pluginA], - builtinExtensions: [b], - parameters: [ - { - id: 'b', - attachTo: { id: 'derp', input: 'default' }, - }, - ], - }), - ).toEqual([ - { - extension: a, - attachTo: { id: 'root', input: 'default' }, - source: pluginA, - }, - { extension: b, attachTo: { id: 'derp', input: 'default' } }, - ]); - }); - - it('should fully override configuration and duplicate', () => { - const a = makeExt('a'); - const b = makeExt('b'); - const plugin = createPlugin({ id: 'test', extensions: [a, b] }); - expect( - mergeExtensionParameters({ - features: [plugin], - builtinExtensions: [], - parameters: [ - { - id: 'a', - config: { foo: { bar: 1 } }, - }, - { - id: 'b', - config: { foo: { bar: 2 } }, - }, - { - id: 'b', - config: { foo: { qux: 3 } }, - }, - ], - }), - ).toEqual([ - { - extension: a, - attachTo: { id: 'root', input: 'default' }, - source: plugin, - config: { foo: { bar: 1 } }, - }, - { - extension: b, - attachTo: { id: 'root', input: 'default' }, - source: plugin, - config: { foo: { qux: 3 } }, - }, - ]); - }); - - it('should place enabled instances in the order that they were enabled', () => { - const a = makeExt('a', 'disabled'); - const b = makeExt('b', 'disabled'); - expect( - mergeExtensionParameters({ - features: [createPlugin({ id: 'empty', extensions: [] })], - builtinExtensions: [a, b], - parameters: [ - { - id: 'b', - disabled: false, - }, - { - id: 'a', - disabled: false, - }, - ], - }), - ).toEqual([ - { extension: b, attachTo: { id: 'root', input: 'default' } }, - { extension: a, attachTo: { id: 'root', input: 'default' } }, - ]); - }); - - it('should apply extension overrides', () => { - const a = makeExt('a'); - const b = makeExt('b'); - const plugin = createPlugin({ id: 'test', extensions: [a, b] }); - const aOverride = makeExt('a', 'enabled', 'other'); - const bOverride = makeExt('b', 'disabled', 'other'); - const cOverride = makeExt('c'); - - const result = mergeExtensionParameters({ - features: [ - plugin, - createExtensionOverrides({ - extensions: [aOverride, bOverride, cOverride], - }), - ], - builtinExtensions: [], - parameters: [], - }); - - expect(result.length).toBe(2); - expect(result[0].extension).toBe(aOverride); - expect(result[0].attachTo).toEqual({ id: 'other', input: 'default' }); - expect(result[0].config).toEqual(undefined); - expect(result[0].source).toBe(plugin); - - expect(result[1]).toEqual({ - extension: cOverride, - attachTo: { id: 'root', input: 'default' }, - config: undefined, - source: undefined, - }); - }); - - it('should use order from configuration when rather than overrides', () => { - const a = makeExt('a', 'disabled'); - const b = makeExt('b', 'disabled'); - const c = makeExt('c', 'disabled'); - const aOverride = makeExt('c', 'disabled'); - const bOverride = makeExt('b', 'disabled'); - const cOverride = makeExt('a', 'disabled'); - - const result = mergeExtensionParameters({ - features: [ - createPlugin({ id: 'test', extensions: [a, b, c] }), - createExtensionOverrides({ - extensions: [cOverride, bOverride, aOverride], - }), - ], - builtinExtensions: [], - parameters: ['b', 'c', 'a'].map(id => ({ id, disabled: false })), - }); - - expect(result.map(r => r.extension.id)).toEqual(['b', 'c', 'a']); - }); -}); - -describe('readAppExtensionParameters', () => { +describe('readAppExtensionsConfig', () => { it('should disable extension with shorthand notation', () => { expect( - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [{ 'core.router': false }] } }), ), ).toEqual([ @@ -222,7 +34,7 @@ describe('readAppExtensionParameters', () => { }, ]); expect( - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [{ 'core.router': { disabled: true } }] }, }), @@ -239,7 +51,7 @@ describe('readAppExtensionParameters', () => { it('should enable extension with shorthand notation', () => { expect( - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: ['core.router'] } }), ), ).toEqual([ @@ -249,7 +61,7 @@ describe('readAppExtensionParameters', () => { }, ]); expect( - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [{ 'core.router': true }] } }), ), ).toEqual([ @@ -259,7 +71,7 @@ describe('readAppExtensionParameters', () => { }, ]); expect( - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [{ 'core.router': { disabled: false } }] }, }), @@ -274,7 +86,7 @@ describe('readAppExtensionParameters', () => { it('should not allow string keys', () => { expect(() => - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [{ 'core.router': 'some-string' }], @@ -288,7 +100,7 @@ describe('readAppExtensionParameters', () => { it('should not allow invalid keys', () => { expect(() => - readAppExtensionParameters( + readAppExtensionsConfig( new ConfigReader({ app: { extensions: [ diff --git a/packages/frontend-app-api/src/graph/readAppExtensionsConfig.ts b/packages/frontend-app-api/src/graph/readAppExtensionsConfig.ts new file mode 100644 index 0000000000..2b5b51ce92 --- /dev/null +++ b/packages/frontend-app-api/src/graph/readAppExtensionsConfig.ts @@ -0,0 +1,199 @@ +/* + * 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 { Config } from '@backstage/config'; +import { JsonValue } from '@backstage/types'; + +export interface ExtensionParameters { + id: string; + attachTo?: { id: string; input: string }; + disabled?: boolean; + config?: unknown; +} + +const knownExtensionParameters = ['attachTo', 'disabled', 'config']; + +// Since we'll never merge arrays in config the config reader context +// isn't too much of a help. Fall back to manual config reading logic +// as the Config interface makes it quite hard for us otherwise. +/** @internal */ +export function readAppExtensionsConfig( + rootConfig: Config, +): ExtensionParameters[] { + const arr = rootConfig.getOptional('app.extensions'); + if (!Array.isArray(arr)) { + if (arr === undefined) { + return []; + } + // This will throw, and show which part of config had the wrong type + rootConfig.getConfigArray('app.extensions'); + return []; + } + + return arr.map((arrayEntry, arrayIndex) => + expandShorthandExtensionParameters(arrayEntry, arrayIndex), + ); +} + +/** @internal */ +export function expandShorthandExtensionParameters( + arrayEntry: JsonValue, + arrayIndex: number, +): ExtensionParameters { + function errorMsg(msg: string, key?: string, prop?: string) { + return `Invalid extension configuration at app.extensions[${arrayIndex}]${ + key ? `[${key}]` : '' + }${prop ? `.${prop}` : ''}, ${msg}`; + } + + // NOTE(freben): This check is intentionally not complete and doesn't check + // whether letters and digits are used, etc. It's not up to the config reading + // logic to decide what constitutes a valid extension ID; that should be + // decided by the logic that loads and instantiates the extensions. This check + // is just here to catch real mistakes or truly conceptually wrong input. + function assertValidId(id: string) { + if (!id || id !== id.trim()) { + throw new Error( + errorMsg('extension ID must not be empty or contain whitespace'), + ); + } + + if (id.includes('/')) { + let message = `extension ID must not contain slashes; got '${id}'`; + const good = id.split('/')[0]; + if (good) { + message += `, did you mean '${good}'?`; + } + throw new Error(errorMsg(message)); + } + } + + // Example YAML: + // - entity.card.about + if (typeof arrayEntry === 'string') { + assertValidId(arrayEntry); + return { + id: arrayEntry, + disabled: false, + }; + } + + // All remaining cases are single-key objects + if ( + typeof arrayEntry !== 'object' || + arrayEntry === null || + Array.isArray(arrayEntry) + ) { + throw new Error(errorMsg('must be a string or an object')); + } + const keys = Object.keys(arrayEntry); + if (keys.length !== 1) { + const joinedKeys = keys.length ? `'${keys.join("', '")}'` : 'none'; + throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`)); + } + + const id = String(keys[0]); + const value = arrayEntry[id]; + assertValidId(id); + + // This example covers a potentially common mistake in the syntax + // Example YAML: + // - entity.card.about: + if (value === null) { + return { + id, + disabled: false, + }; + } + + // Example YAML: + // - catalog.page.cicd: false + if (typeof value === 'boolean') { + return { + id, + disabled: !value, + }; + } + + // The remaining case is the generic object. Example YAML: + // - tech-radar.page: + // at: core.router/routes + // disabled: false + // config: + // path: /tech-radar + // width: 1500 + // height: 800 + if (typeof value !== 'object' || Array.isArray(value)) { + // We don't mention null here - we don't want people to explicitly enter + // - entity.card.about: null + throw new Error(errorMsg('value must be a boolean or object', id)); + } + + const attachTo = value.attachTo as { id: string; input: string } | undefined; + const disabled = value.disabled; + const config = value.config; + + if (attachTo !== undefined) { + if ( + attachTo === null || + typeof attachTo !== 'object' || + Array.isArray(attachTo) + ) { + throw new Error(errorMsg('must be an object', id, 'attachTo')); + } + if (typeof attachTo.id !== 'string' || attachTo.id === '') { + throw new Error( + errorMsg('must be a non-empty string', id, 'attachTo.id'), + ); + } + if (typeof attachTo.input !== 'string' || attachTo.input === '') { + throw new Error( + errorMsg('must be a non-empty string', id, 'attachTo.input'), + ); + } + } + if (disabled !== undefined && typeof disabled !== 'boolean') { + throw new Error(errorMsg('must be a boolean', id, 'disabled')); + } + if ( + config !== undefined && + (typeof config !== 'object' || config === null || Array.isArray(config)) + ) { + throw new Error(errorMsg('must be an object', id, 'config')); + } + + const unknownKeys = Object.keys(value).filter( + k => !knownExtensionParameters.includes(k), + ); + if (unknownKeys.length > 0) { + throw new Error( + errorMsg( + `unknown parameter; expected one of '${knownExtensionParameters.join( + "', '", + )}'`, + id, + unknownKeys.join(', '), + ), + ); + } + + return { + id, + attachTo, + disabled, + config, + }; +} diff --git a/packages/frontend-app-api/src/graph/resolveAppGraph.test.ts b/packages/frontend-app-api/src/graph/resolveAppGraph.test.ts new file mode 100644 index 0000000000..2b7257446e --- /dev/null +++ b/packages/frontend-app-api/src/graph/resolveAppGraph.test.ts @@ -0,0 +1,166 @@ +/* + * 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 { createExtension } from '@backstage/frontend-plugin-api'; +import { resolveAppGraph } from './resolveAppGraph'; + +const extBaseConfig = { + id: 'test', + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + output: {}, + factory: () => ({}), +}; + +const extension = createExtension(extBaseConfig); + +const baseSpec = { + extension, + attachTo: { id: 'nonexistent', input: 'nonexistent' }, + disabled: false, +}; + +describe('buildAppGraph', () => { + it('should fail to create an empty graph', () => { + expect(() => resolveAppGraph('core', [])).toThrow( + "No root node with id 'core' found in app graph", + ); + }); + + it('should create a graph with only one node', () => { + const graph = resolveAppGraph('core', [{ ...baseSpec, id: 'core' }]); + expect(graph.root).toEqual({ + spec: { ...baseSpec, id: 'core' }, + edges: { attachments: new Map() }, + }); + expect(Array.from(graph.orphans)).toEqual([]); + expect(Array.from(graph.nodes.keys())).toEqual(['core']); + }); + + it('should create a graph', () => { + const graph = resolveAppGraph('b', [ + { ...baseSpec, id: 'a' }, + { ...baseSpec, id: 'b' }, + { ...baseSpec, id: 'c' }, + { ...baseSpec, attachTo: { id: 'b', input: 'x' }, id: 'bx1' }, + { ...baseSpec, attachTo: { id: 'b', input: 'x' }, id: 'bx2' }, + { ...baseSpec, attachTo: { id: 'b', input: 'y' }, id: 'by1' }, + { ...baseSpec, attachTo: { id: 'd', input: 'x' }, id: 'dx1' }, + ]); + + expect(Array.from(graph.nodes.keys())).toEqual([ + 'a', + 'b', + 'c', + 'bx1', + 'bx2', + 'by1', + 'dx1', + ]); + + expect(JSON.parse(JSON.stringify(graph.root))).toMatchInlineSnapshot(` + { + "attachments": { + "x": [ + { + "id": "bx1", + }, + { + "id": "bx2", + }, + ], + "y": [ + { + "id": "by1", + }, + ], + }, + "id": "b", + } + `); + expect(String(graph.root)).toMatchInlineSnapshot(` + " + x [ + + + ] + y [ + + ] + " + `); + + const orphans = Array.from(graph.orphans).map(String); + expect(orphans).toMatchInlineSnapshot(` + [ + "", + "", + "", + ] + `); + }); + + it('should create a graph out of order', () => { + const graph = resolveAppGraph('b', [ + { ...baseSpec, attachTo: { id: 'b', input: 'x' }, id: 'bx2' }, + { ...baseSpec, id: 'a' }, + { ...baseSpec, attachTo: { id: 'b', input: 'y' }, id: 'by1' }, + { ...baseSpec, id: 'b' }, + { ...baseSpec, attachTo: { id: 'b', input: 'x' }, id: 'bx1' }, + { ...baseSpec, id: 'c' }, + { ...baseSpec, attachTo: { id: 'd', input: 'x' }, id: 'dx1' }, + ]); + + expect(Array.from(graph.nodes.keys())).toEqual([ + 'bx2', + 'a', + 'by1', + 'b', + 'bx1', + 'c', + 'dx1', + ]); + + expect(String(graph.root)).toMatchInlineSnapshot(` + " + x [ + + + ] + y [ + + ] + " + `); + + const orphans = Array.from(graph.orphans).map(String); + expect(orphans).toMatchInlineSnapshot(` + [ + "", + "", + "", + ] + `); + }); + + it('throws an error when duplicated extensions are detected', () => { + expect(() => + resolveAppGraph('core', [ + { ...baseSpec, id: 'a' }, + { ...baseSpec, id: 'a' }, + ]), + ).toThrow("Unexpected duplicate extension id 'a'"); + }); +}); diff --git a/packages/frontend-app-api/src/graph/resolveAppGraph.ts b/packages/frontend-app-api/src/graph/resolveAppGraph.ts new file mode 100644 index 0000000000..354b4c53ee --- /dev/null +++ b/packages/frontend-app-api/src/graph/resolveAppGraph.ts @@ -0,0 +1,152 @@ +/* + * 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 { AppGraph, AppNode, AppNodeInstance, AppNodeSpec } from './types'; + +function indent(str: string) { + return str.replace(/^/gm, ' '); +} + +/** @internal */ +class SerializableAppNode implements AppNode { + public readonly spec: AppNodeSpec; + public readonly edges = { + attachedTo: undefined as { node: AppNode; input: string } | undefined, + attachments: new Map(), + }; + public readonly instance?: AppNodeInstance; + + constructor(spec: AppNodeSpec) { + this.spec = spec; + } + + setParent(parent: SerializableAppNode) { + const input = this.spec.attachTo.input; + + this.edges.attachedTo = { node: parent, input }; + + const parentInputEdges = parent.edges.attachments.get(input); + if (parentInputEdges) { + parentInputEdges.push(this); + } else { + parent.edges.attachments.set(input, [this]); + } + } + + toJSON() { + const dataRefs = this.instance && [...this.instance.getDataRefs()]; + return { + id: this.spec.id, + output: + dataRefs && dataRefs.length > 0 + ? dataRefs.map(ref => ref.id) + : undefined, + attachments: + this.edges.attachments.size > 0 + ? Object.fromEntries(this.edges.attachments) + : undefined, + }; + } + + toString(): string { + const dataRefs = this.instance && [...this.instance.getDataRefs()]; + const out = + dataRefs && dataRefs.length > 0 + ? ` out=[${[...dataRefs.keys()].join(', ')}]` + : ''; + + if (this.edges.attachments.size === 0) { + return `<${this.spec.id}${out} />`; + } + + return [ + `<${this.spec.id}${out}>`, + ...[...this.edges.attachments.entries()].map(([k, v]) => + indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\n')), + ), + ``, + ].join('\n'); + } +} + +/** + * Build the app graph by iterating through all node specs and constructing the app + * tree with all attachments in the same order as they appear in the input specs array. + * @internal + */ +export function resolveAppGraph( + rootNodeId: string, + specs: AppNodeSpec[], +): AppGraph { + const nodes = new Map(); + + // A node with the provided rootNodeId must be found in the graph, and it must not be attached to anything + let rootNode: AppNode | undefined = undefined; + + // While iterating through the inputs specs we keep track of all nodes that were created + // before their parent, and attach them later when the parent is created. + // As we find the parents and attach the children, we remove them from this map. This means + // that after iterating through all input specs, this will be a map for each root node. + const orphansByParent = new Map< + string /* parentId */, + SerializableAppNode[] + >(); + + for (const spec of specs) { + // The main check with a more helpful error message happens in resolveAppNodeSpecs + if (nodes.has(spec.id)) { + throw new Error(`Unexpected duplicate extension id '${spec.id}'`); + } + + const node = new SerializableAppNode(spec); + nodes.set(spec.id, node); + + // TODO: For now we simply ignore the attachTo spec of the root node, but it'd be cleaner if we could avoid defining it + if (spec.id === rootNodeId) { + rootNode = node; + } else { + const parent = nodes.get(spec.attachTo.id); + if (parent) { + node.setParent(parent); + } else { + const orphanNodesForParent = orphansByParent.get(spec.attachTo.id); + if (orphanNodesForParent) { + orphanNodesForParent.push(node); + } else { + orphansByParent.set(spec.attachTo.id, [node]); + } + } + } + + const orphanedChildren = orphansByParent.get(spec.id); + if (orphanedChildren) { + orphansByParent.delete(spec.id); + for (const orphan of orphanedChildren) { + orphan.setParent(node); + } + } + } + + if (!rootNode) { + throw new Error(`No root node with id '${rootNodeId}' found in app graph`); + } + + return { + root: rootNode, + nodes, + orphans: Array.from(orphansByParent.values()).flat(), + }; +} diff --git a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts new file mode 100644 index 0000000000..59930968c6 --- /dev/null +++ b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.test.ts @@ -0,0 +1,278 @@ +/* + * 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 { + createExtensionOverrides, + createPlugin, + Extension, +} from '@backstage/frontend-plugin-api'; +import { resolveAppNodeSpecs } from './resolveAppNodeSpecs'; + +function makeExt( + id: string, + status: 'disabled' | 'enabled' = 'enabled', + attachId: string = 'root', +) { + return { + id, + attachTo: { id: attachId, input: 'default' }, + disabled: status === 'disabled', + } as Extension; +} + +describe('resolveAppNodeSpecs', () => { + it('should filter out disabled extension instances', () => { + expect( + resolveAppNodeSpecs({ + features: [], + builtinExtensions: [makeExt('a', 'disabled')], + parameters: [], + }), + ).toEqual([]); + }); + + it('should pass through extension instances', () => { + const a = makeExt('a'); + const b = makeExt('b'); + expect( + resolveAppNodeSpecs({ + features: [], + builtinExtensions: [a, b], + parameters: [], + }), + ).toEqual([ + { + id: 'a', + extension: a, + attachTo: { id: 'root', input: 'default' }, + disabled: false, + }, + { + id: 'b', + extension: b, + attachTo: { id: 'root', input: 'default' }, + disabled: false, + }, + ]); + }); + + it('should override attachment points', () => { + const a = makeExt('a'); + const b = makeExt('b'); + const pluginA = createPlugin({ id: 'test', extensions: [a] }); + expect( + resolveAppNodeSpecs({ + features: [pluginA], + builtinExtensions: [b], + parameters: [ + { + id: 'b', + attachTo: { id: 'derp', input: 'default' }, + }, + ], + }), + ).toEqual([ + { + id: 'a', + extension: a, + attachTo: { id: 'root', input: 'default' }, + source: pluginA, + disabled: false, + }, + { + id: 'b', + extension: b, + attachTo: { id: 'derp', input: 'default' }, + disabled: false, + }, + ]); + }); + + it('should fully override configuration and duplicate', () => { + const a = makeExt('a'); + const b = makeExt('b'); + const plugin = createPlugin({ id: 'test', extensions: [a, b] }); + expect( + resolveAppNodeSpecs({ + features: [plugin], + builtinExtensions: [], + parameters: [ + { + id: 'a', + config: { foo: { bar: 1 } }, + }, + { + id: 'b', + config: { foo: { bar: 2 } }, + }, + { + id: 'b', + config: { foo: { qux: 3 } }, + }, + ], + }), + ).toEqual([ + { + id: 'a', + extension: a, + attachTo: { id: 'root', input: 'default' }, + source: plugin, + config: { foo: { bar: 1 } }, + disabled: false, + }, + { + id: 'b', + extension: b, + attachTo: { id: 'root', input: 'default' }, + source: plugin, + config: { foo: { qux: 3 } }, + disabled: false, + }, + ]); + }); + + it('should place enabled instances in the order that they were enabled', () => { + const a = makeExt('a', 'disabled'); + const b = makeExt('b', 'disabled'); + expect( + resolveAppNodeSpecs({ + features: [createPlugin({ id: 'empty', extensions: [] })], + builtinExtensions: [a, b], + parameters: [ + { + id: 'b', + disabled: false, + }, + { + id: 'a', + disabled: false, + }, + ], + }), + ).toEqual([ + { + id: 'b', + extension: b, + attachTo: { id: 'root', input: 'default' }, + disabled: false, + }, + { + id: 'a', + extension: a, + attachTo: { id: 'root', input: 'default' }, + disabled: false, + }, + ]); + }); + + it('should apply extension overrides', () => { + const a = makeExt('a'); + const b = makeExt('b'); + const plugin = createPlugin({ id: 'test', extensions: [a, b] }); + const aOverride = makeExt('a', 'enabled', 'other'); + const bOverride = makeExt('b', 'disabled', 'other'); + const cOverride = makeExt('c'); + + const result = resolveAppNodeSpecs({ + features: [ + plugin, + createExtensionOverrides({ + extensions: [aOverride, bOverride, cOverride], + }), + ], + builtinExtensions: [], + parameters: [], + }); + + expect(result.length).toBe(2); + expect(result[0].extension).toBe(aOverride); + expect(result[0].attachTo).toEqual({ id: 'other', input: 'default' }); + expect(result[0].config).toEqual(undefined); + expect(result[0].source).toBe(plugin); + + expect(result[1]).toEqual({ + id: 'c', + extension: cOverride, + attachTo: { id: 'root', input: 'default' }, + config: undefined, + source: undefined, + disabled: false, + }); + }); + + it('should use order from configuration when rather than overrides', () => { + const a = makeExt('a', 'disabled'); + const b = makeExt('b', 'disabled'); + const c = makeExt('c', 'disabled'); + const aOverride = makeExt('c', 'disabled'); + const bOverride = makeExt('b', 'disabled'); + const cOverride = makeExt('a', 'disabled'); + + const result = resolveAppNodeSpecs({ + features: [ + createPlugin({ id: 'test', extensions: [a, b, c] }), + createExtensionOverrides({ + extensions: [cOverride, bOverride, aOverride], + }), + ], + builtinExtensions: [], + parameters: ['b', 'c', 'a'].map(id => ({ id, disabled: false })), + }); + + expect(result.map(r => r.extension.id)).toEqual(['b', 'c', 'a']); + }); + + it('throws an error when a forbidden extension is overridden by a plugin', () => { + expect(() => + resolveAppNodeSpecs({ + features: [ + createPlugin({ id: 'test', extensions: [makeExt('forbidden')] }), + ], + builtinExtensions: [], + parameters: [], + forbidden: new Set(['forbidden']), + }), + ).toThrow( + "It is forbidden to override the following extension(s): 'forbidden', which is done by the following plugin(s): 'test'", + ); + }); + + it('throws an error when a forbidden extension is overridden by overrides', () => { + expect(() => + resolveAppNodeSpecs({ + features: [ + createExtensionOverrides({ extensions: [makeExt('forbidden')] }), + ], + builtinExtensions: [], + parameters: [], + forbidden: new Set(['forbidden']), + }), + ).toThrow( + "It is forbidden to override the following extension(s): 'forbidden', which is done by one or more extension overrides", + ); + }); + + it('throws an error when a forbidden extension is parametrized', () => { + expect(() => + resolveAppNodeSpecs({ + features: [], + builtinExtensions: [], + parameters: [{ id: 'forbidden', disabled: false }], + forbidden: new Set(['forbidden']), + }), + ).toThrow("Configuration of the 'forbidden' extension is forbidden"); + }); +}); diff --git a/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts new file mode 100644 index 0000000000..ca3c4fd1ea --- /dev/null +++ b/packages/frontend-app-api/src/graph/resolveAppNodeSpecs.ts @@ -0,0 +1,212 @@ +/* + * 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 { + BackstagePlugin, + Extension, + ExtensionOverrides, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; +import { ExtensionParameters } from './readAppExtensionsConfig'; +import { AppNodeSpec } from './types'; + +/** @internal */ +export function resolveAppNodeSpecs(options: { + features: (BackstagePlugin | ExtensionOverrides)[]; + builtinExtensions: Extension[]; + parameters: Array; + forbidden?: Set; +}): AppNodeSpec[] { + const { builtinExtensions, parameters, forbidden = new Set() } = options; + + const plugins = options.features.filter( + (f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin', + ); + const overrides = options.features.filter( + (f): f is ExtensionOverrides => + f.$$type === '@backstage/ExtensionOverrides', + ); + + const pluginExtensions = plugins.flatMap(source => { + return source.extensions.map(extension => ({ ...extension, source })); + }); + const overrideExtensions = overrides.flatMap( + override => toInternalExtensionOverrides(override).extensions, + ); + + // Prevent core override + if (pluginExtensions.some(({ id }) => forbidden.has(id))) { + const pluginsStr = pluginExtensions + .filter(({ id }) => forbidden.has(id)) + .map(({ source }) => `'${source.id}'`) + .join(', '); + const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); + throw new Error( + `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by the following plugin(s): ${pluginsStr}`, + ); + } + + if (overrideExtensions.some(({ id }) => forbidden.has(id))) { + const forbiddenStr = [...forbidden].map(id => `'${id}'`).join(', '); + throw new Error( + `It is forbidden to override the following extension(s): ${forbiddenStr}, which is done by one or more extension overrides`, + ); + } + const overrideExtensionIds = overrideExtensions.map(({ id }) => id); + if (overrideExtensionIds.length !== new Set(overrideExtensionIds).size) { + const counts = new Map(); + for (const id of overrideExtensionIds) { + counts.set(id, (counts.get(id) ?? 0) + 1); + } + const duplicated = Array.from(counts.entries()) + .filter(([, count]) => count > 1) + .map(([id]) => id); + throw new Error( + `The following extensions had duplicate overrides: ${duplicated.join( + ', ', + )}`, + ); + } + + const configuredExtensions = [ + ...pluginExtensions.map(({ source, ...extension }) => ({ + extension, + params: { + source, + attachTo: extension.attachTo, + disabled: extension.disabled, + config: undefined as unknown, + }, + })), + ...builtinExtensions.map(extension => ({ + extension, + params: { + source: undefined, + attachTo: extension.attachTo, + disabled: extension.disabled, + config: undefined as unknown, + }, + })), + ]; + + // Install all extension overrides + for (const extension of overrideExtensions) { + // Check if our override is overriding an extension that already exists + const index = configuredExtensions.findIndex( + e => e.extension.id === extension.id, + ); + if (index !== -1) { + // Only implementation, attachment point and default disabled status are overridden, the source is kept + configuredExtensions[index].extension = extension; + configuredExtensions[index].params.attachTo = extension.attachTo; + configuredExtensions[index].params.disabled = extension.disabled; + } else { + // Add the extension as a new one when not overriding an existing one + configuredExtensions.push({ + extension, + params: { + source: undefined, + attachTo: extension.attachTo, + disabled: extension.disabled, + config: undefined, + }, + }); + } + } + + const duplicatedExtensionIds = new Set(); + const duplicatedExtensionData = configuredExtensions.reduce< + Record> + >((data, { extension, params }) => { + const extensionId = extension.id; + const extensionData = data?.[extensionId]; + if (extensionData) duplicatedExtensionIds.add(extensionId); + const pluginId = params.source?.id ?? 'internal'; + const pluginCount = extensionData?.[pluginId] ?? 0; + return { + ...data, + [extensionId]: { ...extensionData, [pluginId]: pluginCount + 1 }, + }; + }, {}); + + if (duplicatedExtensionIds.size > 0) { + throw new Error( + `The following extensions are duplicated: ${Array.from( + duplicatedExtensionIds, + ) + .map( + extensionId => + `The extension '${extensionId}' was provided ${Object.keys( + duplicatedExtensionData[extensionId], + ) + .map( + pluginId => + `${duplicatedExtensionData[extensionId][pluginId]} time(s) by the plugin '${pluginId}'`, + ) + .join(' and ')}`, + ) + .join(', ')}`, + ); + } + + for (const overrideParam of parameters) { + const extensionId = overrideParam.id; + + if (forbidden.has(extensionId)) { + throw new Error( + `Configuration of the '${extensionId}' extension is forbidden`, + ); + } + + const existingIndex = configuredExtensions.findIndex( + e => e.extension.id === extensionId, + ); + if (existingIndex !== -1) { + const existing = configuredExtensions[existingIndex]; + if (overrideParam.attachTo) { + existing.params.attachTo = overrideParam.attachTo; + } + if (overrideParam.config) { + // TODO: merge config? + existing.params.config = overrideParam.config; + } + if ( + Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled) + ) { + existing.params.disabled = Boolean(overrideParam.disabled); + if (!existing.params.disabled) { + // bump + configuredExtensions.splice(existingIndex, 1); + configuredExtensions.push(existing); + } + } + } else { + throw new Error(`Extension ${extensionId} does not exist`); + } + } + + return configuredExtensions + .filter(override => !override.params.disabled) + .map(param => ({ + id: param.extension.id, + attachTo: param.params.attachTo, + extension: param.extension, + disabled: param.params.disabled, + source: param.params.source, + config: param.params.config, + })); +} diff --git a/packages/frontend-app-api/src/graph/types.ts b/packages/frontend-app-api/src/graph/types.ts new file mode 100644 index 0000000000..9267f13158 --- /dev/null +++ b/packages/frontend-app-api/src/graph/types.ts @@ -0,0 +1,101 @@ +/* + * 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 { + BackstagePlugin, + Extension, + ExtensionDataRef, +} from '@backstage/frontend-plugin-api'; + +/* +NOTE: These types are marked as @internal for now, but the intention is for this to be a public API in the future. +*/ + +/** + * The specification for this node in the app graph. + * + * @internal + * @remarks + * + * The specifications for a collection of app nodes is all the information needed + * to build the graph and instantiate the nodes. + */ +export interface AppNodeSpec { + readonly id: string; + readonly attachTo: { id: string; input: string }; + readonly extension: Extension; + readonly disabled: boolean; + readonly config?: unknown; + readonly source?: BackstagePlugin; +} + +/** + * The connections from this node to other nodes. + * + * @internal + * @remarks + * + * The app node edges are resolved based on the app node specs, regardless of whether + * adjacent nodes are disabled or not. If no parent attachment is present or + */ +export interface AppNodeEdges { + readonly attachedTo?: { node: AppNode; input: string }; + readonly attachments: ReadonlyMap; +} + +/** + * The instance of this node in the app graph. + * + * @internal + * @remarks + * + * The app node instance is created when the `factory` function of an extension is called. + * Instances will only be present for nodes in the app that are connected to the root + * node and not disabled + */ +export interface AppNodeInstance { + /** Returns a sequence of all extension data refs that were output by this instance */ + getDataRefs(): Iterable>; + /** Get the output data for a single extension data ref */ + getData(ref: ExtensionDataRef): T | undefined; +} + +/** + * + * @internal + */ +export interface AppNode { + /** The specification for how this node should be instantiated */ + readonly spec: AppNodeSpec; + /** The edges from this node to other nodes in the app graph */ + readonly edges: AppNodeEdges; + /** The instance of this node, if it was instantiated */ + readonly instance?: AppNodeInstance; +} + +/** + * The app graph containing all nodes of the app. + * + * @internal + */ +export interface AppGraph { + /** The root node of the app */ + root: AppNode; + /** A map of all nodes in the app by ID, including orphaned or disabled nodes */ + nodes: ReadonlyMap; + /** A sequence of all nodes with a parent that is not reachable from the app root node */ + orphans: Iterable; +} diff --git a/packages/frontend-app-api/src/routing/RouteResolver.test.ts b/packages/frontend-app-api/src/routing/RouteResolver.test.ts index e37ee14929..81e9d10be4 100644 --- a/packages/frontend-app-api/src/routing/RouteResolver.test.ts +++ b/packages/frontend-app-api/src/routing/RouteResolver.test.ts @@ -24,7 +24,7 @@ import { } from '@backstage/frontend-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; import { RouteResolver } from './RouteResolver'; -import { MATCH_ALL_ROUTE } from './extractRouteInfoFromInstanceTree'; +import { MATCH_ALL_ROUTE } from './extractRouteInfoFromAppNode'; const rest = { element: null, diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts similarity index 96% rename from packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts rename to packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts index 0de33b50f2..c6b025e8b3 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.test.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.test.ts @@ -16,7 +16,7 @@ import React from 'react'; import { BackstagePlugin } from '@backstage/core-plugin-api'; -import { extractRouteInfoFromInstanceTree } from './extractRouteInfoFromInstanceTree'; +import { extractRouteInfoFromAppNode } from './extractRouteInfoFromAppNode'; import { AnyRouteRefParams, Extension, @@ -27,8 +27,12 @@ import { createPlugin, createRouteRef, } from '@backstage/frontend-plugin-api'; -import { createInstances } from '../wiring/createApp'; import { MockConfigApi } from '@backstage/test-utils'; +import { createAppGraph } from '../graph'; +import { Core } from '../extensions/Core'; +import { CoreRoutes } from '../extensions/CoreRoutes'; +import { CoreNav } from '../extensions/CoreNav'; +import { CoreLayout } from '../extensions/CoreLayout'; const ref1 = createRouteRef(); const ref2 = createRouteRef(); @@ -58,12 +62,12 @@ function createTestExtension(options: { element: coreExtensionData.reactElement, }), }, - factory({ bind }) { - bind({ + factory() { + return { path: options.path, routeRef: options.routeRef, element: React.createElement('div'), - }); + }; }, }); } @@ -73,12 +77,13 @@ function routeInfoFromExtensions(extensions: Extension[]) { id: 'test', extensions, }); - const { coreInstance } = createInstances({ + const graph = createAppGraph({ config: new MockConfigApi({}), + builtinExtensions: [Core, CoreRoutes, CoreNav, CoreLayout], features: [plugin], }); - return extractRouteInfoFromInstanceTree(coreInstance); + return extractRouteInfoFromAppNode(graph.root); } function sortedEntries(map: Map): [RouteRef, T][] { diff --git a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts similarity index 91% rename from packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts rename to packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts index 0bf9c2d1dd..71991ee44e 100644 --- a/packages/frontend-app-api/src/routing/extractRouteInfoFromInstanceTree.ts +++ b/packages/frontend-app-api/src/routing/extractRouteInfoFromAppNode.ts @@ -15,10 +15,10 @@ */ import { RouteRef, coreExtensionData } from '@backstage/frontend-plugin-api'; -import { ExtensionInstance } from '../wiring/createExtensionInstance'; // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toLegacyPlugin } from '../wiring/createApp'; import { BackstageRouteObject } from './types'; +import { AppNode } from '../graph'; // We always add a child that matches all subroutes but without any route refs. This makes // sure that we're always able to match each route no matter how deep the navigation goes. @@ -41,7 +41,7 @@ export function joinPaths(...paths: string[]): string { return normalized; } -export function extractRouteInfoFromInstanceTree(core: ExtensionInstance): { +export function extractRouteInfoFromAppNode(node: AppNode): { routePaths: Map; routeParents: Map; routeObjects: BackstageRouteObject[]; @@ -56,17 +56,17 @@ export function extractRouteInfoFromInstanceTree(core: ExtensionInstance): { const routeObjects = new Array(); function visit( - current: ExtensionInstance, + current: AppNode, collectedPath?: string, foundRefForCollectedPath: boolean = false, parentRef?: RouteRef, candidateParentRef?: RouteRef, parentObj?: BackstageRouteObject, ) { - const routePath = current - .getData(coreExtensionData.routePath) + const routePath = current.instance + ?.getData(coreExtensionData.routePath) ?.replace(/^\//, ''); - const routeRef = current.getData(coreExtensionData.routeRef); + const routeRef = current.instance?.getData(coreExtensionData.routeRef); const parentChildren = parentObj?.children ?? routeObjects; let currentObj = parentObj; @@ -124,12 +124,12 @@ export function extractRouteInfoFromInstanceTree(core: ExtensionInstance): { routeParents.set(routeRef, newParentRef); currentObj?.routeRefs.add(routeRef); - if (current.source) { - currentObj?.plugins.add(toLegacyPlugin(current.source)); + if (current.spec.source) { + currentObj?.plugins.add(toLegacyPlugin(current.spec.source)); } } - for (const children of current.attachments.values()) { + for (const children of current.edges.attachments.values()) { for (const child of children) { visit( child, @@ -143,7 +143,7 @@ export function extractRouteInfoFromInstanceTree(core: ExtensionInstance): { } } - visit(core); + visit(node); return { routePaths, routeParents, routeObjects }; } diff --git a/packages/frontend-app-api/src/wiring/createApp.test.tsx b/packages/frontend-app-api/src/wiring/createApp.test.tsx index de574bc6a9..e9fb48f957 100644 --- a/packages/frontend-app-api/src/wiring/createApp.test.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.test.tsx @@ -15,123 +15,15 @@ */ import { - createExtension, - createExtensionOverrides, createPageExtension, createPlugin, - createRouteRef, createThemeExtension, } from '@backstage/frontend-plugin-api'; -import { createApp, createInstances } from './createApp'; import { screen, waitFor } from '@testing-library/react'; +import { createApp } from './createApp'; import { MockConfigApi, renderWithEffects } from '@backstage/test-utils'; import React from 'react'; -const extBaseConfig = { - id: 'test', - attachTo: { id: 'root', input: 'default' }, - output: {}, - factory() {}, -}; - -describe('createInstances', () => { - it('throws an error when a core extension is parametrized', () => { - const config = new MockConfigApi({ - app: { - extensions: [ - { - core: {}, - }, - ], - }, - }); - const features = [ - createPlugin({ - id: 'plugin', - extensions: [], - }), - ]; - expect(() => createInstances({ config, features })).toThrow( - "A 'core' extension configuration was detected, but the core extension is not configurable", - ); - }); - - it('throws an error when a core extension is overridden', () => { - const config = new MockConfigApi({}); - const features = [ - createPlugin({ - id: 'plugin', - extensions: [ - createExtension({ - id: 'core', - attachTo: { id: 'core.routes', input: 'route' }, - inputs: {}, - output: {}, - factory() {}, - }), - ], - }), - ]; - expect(() => createInstances({ config, features })).toThrow( - "The following plugin(s) are overriding the 'core' extension which is forbidden: plugin", - ); - }); - - it('throws an error when duplicated extensions are detected', () => { - const config = new MockConfigApi({}); - - const ExtensionA = createPageExtension({ - id: 'A', - defaultPath: '/', - routeRef: createRouteRef(), - loader: async () =>

, - }); - - const ExtensionB = createPageExtension({ - id: 'B', - defaultPath: '/', - routeRef: createRouteRef(), - loader: async () =>
Extension B
, - }); - - const PluginA = createPlugin({ - id: 'A', - extensions: [ExtensionA, ExtensionA], - }); - - const PluginB = createPlugin({ - id: 'B', - extensions: [ExtensionA, ExtensionB, ExtensionB], - }); - - const features = [PluginA, PluginB]; - - expect(() => createInstances({ config, features })).toThrow( - "The following extensions are duplicated: The extension 'A' was provided 2 time(s) by the plugin 'A' and 1 time(s) by the plugin 'B', The extension 'B' was provided 2 time(s) by the plugin 'B'", - ); - }); - - it('throws an error when duplicated extension overrides are detected', () => { - expect(() => - createInstances({ - config: new MockConfigApi({}), - features: [ - createExtensionOverrides({ - extensions: [ - createExtension({ ...extBaseConfig, id: 'a' }), - createExtension({ ...extBaseConfig, id: 'a' }), - createExtension({ ...extBaseConfig, id: 'b' }), - ], - }), - createExtensionOverrides({ - extensions: [createExtension({ ...extBaseConfig, id: 'b' })], - }), - ], - }), - ).toThrow('The following extensions had duplicate overrides: a, b'); - }); -}); - describe('createApp', () => { it('should allow themes to be installed', async () => { const app = createApp({ @@ -161,90 +53,6 @@ describe('createApp', () => { await expect(screen.findByText('Derp')).resolves.toBeInTheDocument(); }); - it('should log an app', () => { - const { coreInstance } = createInstances({ - config: new MockConfigApi({}), - features: [], - }); - - expect(String(coreInstance)).toMatchInlineSnapshot(` - " - root [ - - content [ - - ] - nav [ - - ] - - ] - themes [ - - - ] - " - `); - }); - - it('should serialize an app as JSON', () => { - const { coreInstance } = createInstances({ - config: new MockConfigApi({}), - features: [], - }); - - expect(JSON.parse(JSON.stringify(coreInstance))).toMatchInlineSnapshot(` - { - "attachments": { - "root": [ - { - "attachments": { - "content": [ - { - "id": "core.routes", - "output": [ - "core.reactElement", - ], - }, - ], - "nav": [ - { - "id": "core.nav", - "output": [ - "core.reactElement", - ], - }, - ], - }, - "id": "core.layout", - "output": [ - "core.reactElement", - ], - }, - ], - "themes": [ - { - "id": "themes.light", - "output": [ - "core.theme", - ], - }, - { - "id": "themes.dark", - "output": [ - "core.theme", - ], - }, - ], - }, - "id": "core", - "output": [ - "core.reactElement", - ], - } - `); - }); - it('should deduplicate features keeping the last received one', async () => { const duplicatedFeatureId = 'test'; const app = createApp({ diff --git a/packages/frontend-app-api/src/wiring/createApp.tsx b/packages/frontend-app-api/src/wiring/createApp.tsx index e770a6b4c1..4f7817314a 100644 --- a/packages/frontend-app-api/src/wiring/createApp.tsx +++ b/packages/frontend-app-api/src/wiring/createApp.tsx @@ -28,15 +28,6 @@ import { Core } from '../extensions/Core'; import { CoreRoutes } from '../extensions/CoreRoutes'; import { CoreLayout } from '../extensions/CoreLayout'; import { CoreNav } from '../extensions/CoreNav'; -import { - createExtensionInstance, - ExtensionInstance, -} from './createExtensionInstance'; -import { - ExtensionInstanceParameters, - mergeExtensionParameters, - readAppExtensionParameters, -} from './parameters'; import { AnyApiFactory, ApiHolder, @@ -86,7 +77,7 @@ import { import { BrowserRouter, Route } from 'react-router-dom'; import { SidebarItem } from '@backstage/core-components'; import { DarkTheme, LightTheme } from '../extensions/themes'; -import { extractRouteInfoFromInstanceTree } from '../routing/extractRouteInfoFromInstanceTree'; +import { extractRouteInfoFromAppNode } from '../routing/extractRouteInfoFromAppNode'; import { getOrCreateGlobalSingleton } from '@backstage/version-bridge'; import { appLanguageApiRef, @@ -96,6 +87,16 @@ import { AppRouteBinder } from '../routing'; import { RoutingProvider } from '../routing/RoutingProvider'; import { resolveRouteBindings } from '../routing/resolveRouteBindings'; import { collectRouteIds } from '../routing/collectRouteIds'; +import { AppNode, createAppGraph } from '../graph'; + +const builtinExtensions = [ + Core, + CoreRoutes, + CoreNav, + CoreLayout, + LightTheme, + DarkTheme, +]; /** @public */ export interface ExtensionTreeNode { @@ -116,20 +117,38 @@ export function createExtensionTree(options: { config: Config; }): ExtensionTree { const features = getAvailableFeatures(options.config); - const { instances } = createInstances({ + const graph = createAppGraph({ features, + builtinExtensions, config: options.config, }); + function convertNode(node?: AppNode): ExtensionTreeNode | undefined { + return ( + node && { + id: node.spec.id, + getData(ref: ExtensionDataRef): T | undefined { + return node.instance?.getData(ref); + }, + } + ); + } + return { getExtension(id: string): ExtensionTreeNode | undefined { - return instances.get(id); + return convertNode(graph.nodes.get(id)); }, getExtensionAttachments( id: string, inputName: string, ): ExtensionTreeNode[] { - return instances.get(id)?.attachments.get(inputName) ?? []; + return ( + graph.nodes + .get(id) + ?.edges.attachments.get(inputName) + ?.map(convertNode) + .filter((node): node is ExtensionTreeNode => Boolean(node)) ?? [] + ); }, getRootRoutes(): JSX.Element[] { return this.getExtensionAttachments('core.routes', 'routes').map(node => { @@ -179,94 +198,6 @@ export function createExtensionTree(options: { }; } -/** - * @internal - */ -export function createInstances(options: { - features: (BackstagePlugin | ExtensionOverrides)[]; - config: Config; -}) { - const builtinExtensions = [ - Core, - CoreRoutes, - CoreNav, - CoreLayout, - LightTheme, - DarkTheme, - ]; - - // pull in default extension instance from discovered packages - // apply config to adjust default extension instances and add more - const extensionParams = mergeExtensionParameters({ - features: options.features, - builtinExtensions, - parameters: readAppExtensionParameters(options.config), - }); - - // TODO: validate the config of all extension instances - // We do it at this point to ensure that merging (if any) of config has already happened - - // Create attachment map so that we can look attachments up during instance creation - const attachmentMap = new Map< - string, - Map - >(); - for (const instanceParams of extensionParams) { - const extensionId = instanceParams.attachTo.id; - const pointId = instanceParams.attachTo.input; - let pointMap = attachmentMap.get(extensionId); - if (!pointMap) { - pointMap = new Map(); - attachmentMap.set(extensionId, pointMap); - } - - let instances = pointMap.get(pointId); - if (!instances) { - instances = []; - pointMap.set(pointId, instances); - } - - instances.push(instanceParams); - } - - const instances = new Map(); - - function createInstance( - instanceParams: ExtensionInstanceParameters, - ): ExtensionInstance { - const extensionId = instanceParams.extension.id; - const existingInstance = instances.get(extensionId); - if (existingInstance) { - return existingInstance; - } - - const attachments = new Map( - Array.from(attachmentMap.get(extensionId)?.entries() ?? []).map( - ([inputName, attachmentConfigs]) => { - return [inputName, attachmentConfigs.map(createInstance)]; - }, - ), - ); - - const newInstance = createExtensionInstance({ - extension: instanceParams.extension, - source: instanceParams.source, - config: instanceParams.config, - attachments, - }); - - instances.set(extensionId, newInstance); - - return newInstance; - } - - const coreInstance = createInstance( - extensionParams.find(p => p.extension.id === 'core')!, - ); - - return { coreInstance, instances }; -} - function deduplicateFeatures( allFeatures: (BackstagePlugin | ExtensionOverrides)[], ): (BackstagePlugin | ExtensionOverrides)[] { @@ -316,8 +247,9 @@ export function createApp(options: { ...(options.features ?? []), ]); - const { coreInstance } = createInstances({ + const appGraph = createAppGraph({ features: allFeatures, + builtinExtensions, config, }); @@ -330,11 +262,11 @@ export function createApp(options: { const routeIds = collectRouteIds(allFeatures); const App = () => ( - + {/* TODO: set base path using the logic from AppRouter */} - {coreInstance.getData(coreExtensionData.reactElement)} + {appGraph.root.instance!.getData( + coreExtensionData.reactElement, + )} @@ -424,22 +358,19 @@ function createLegacyAppContext(plugins: BackstagePlugin[]): AppContext { }; } -function createApiHolder( - coreExtension: ExtensionInstance, - configApi: ConfigApi, -): ApiHolder { +function createApiHolder(core: AppNode, configApi: ConfigApi): ApiHolder { const factoryRegistry = new ApiFactoryRegistry(); const pluginApis = - coreExtension.attachments + core.edges.attachments .get('apis') - ?.map(e => e.getData(coreExtensionData.apiFactory)) + ?.map(e => e.instance?.getData(coreExtensionData.apiFactory)) .filter((x): x is AnyApiFactory => !!x) ?? []; const themeExtensions = - coreExtension.attachments + core.edges.attachments .get('themes') - ?.map(e => e.getData(coreExtensionData.theme)) + ?.map(e => e.instance?.getData(coreExtensionData.theme)) .filter((x): x is AppTheme => !!x) ?? []; for (const factory of [...defaultApis, ...pluginApis]) { diff --git a/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts b/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts deleted file mode 100644 index 6e69e99cb7..0000000000 --- a/packages/frontend-app-api/src/wiring/createExtensionInstance.test.ts +++ /dev/null @@ -1,456 +0,0 @@ -/* - * 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 { - createExtension, - createExtensionDataRef, - createExtensionInput, - createSchemaFromZod, -} from '@backstage/frontend-plugin-api'; -import { createExtensionInstance } from './createExtensionInstance'; - -const testDataRef = createExtensionDataRef('test'); -const otherDataRef = createExtensionDataRef('other'); -const inputMirrorDataRef = createExtensionDataRef('mirror'); - -const simpleExtension = createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test: testDataRef, - other: otherDataRef.optional(), - }, - configSchema: createSchemaFromZod(z => - z.object({ - output: z.string().default('test'), - other: z.number().optional(), - }), - ), - factory({ bind, config }) { - bind({ test: config.output, other: config.other }); - }, -}); - -describe('createExtensionInstance', () => { - it('should create a simple extension instance', () => { - const attachments = new Map(); - const instance = createExtensionInstance({ - attachments, - config: undefined, - extension: simpleExtension, - }); - - expect(instance.id).toBe('core.test'); - expect(instance.attachments).toBe(attachments); - expect(instance.getData(testDataRef)).toEqual('test'); - }); - - it('should create an extension with different kind of inputs', () => { - const attachments = new Map([ - [ - 'optionalSingletonPresent', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'optionalSingletonPresent' }, - extension: simpleExtension, - }), - ], - ], - [ - 'singleton', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'singleton', other: 2 }, - extension: simpleExtension, - }), - ], - ], - [ - 'many', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many2', other: 3 }, - extension: simpleExtension, - }), - ], - ], - ]); - const instance = createExtensionInstance({ - attachments, - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - optionalSingletonPresent: createExtensionInput( - { - test: testDataRef, - other: otherDataRef.optional(), - }, - { singleton: true, optional: true }, - ), - optionalSingletonMissing: createExtensionInput( - { - test: testDataRef, - other: otherDataRef.optional(), - }, - { singleton: true, optional: true }, - ), - singleton: createExtensionInput( - { - test: testDataRef, - other: otherDataRef.optional(), - }, - { singleton: true }, - ), - many: createExtensionInput({ - test: testDataRef, - other: otherDataRef.optional(), - }), - }, - output: { - inputMirror: inputMirrorDataRef, - }, - factory({ bind, inputs }) { - bind({ inputMirror: inputs }); - }, - }), - }); - - expect(instance.id).toBe('core.test'); - expect(instance.attachments).toBe(attachments); - expect(instance.getData(inputMirrorDataRef)).toEqual({ - optionalSingletonPresent: { test: 'optionalSingletonPresent' }, - singleton: { test: 'singleton', other: 2 }, - many: [{ test: 'many1' }, { test: 'many2', other: 3 }], - }); - }); - - it('should refuse to create an extension with invalid config', () => { - expect(() => - createExtensionInstance({ - attachments: new Map(), - config: { other: 'not-a-number' }, - extension: simpleExtension, - }), - ).toThrow( - "Invalid configuration for extension 'core.test'; caused by Error: Expected number, received string at 'other'", - ); - }); - - it('should forward extension factory errors', () => { - expect(() => - createExtensionInstance({ - attachments: new Map(), - config: { other: 'not-a-number' }, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: {}, - factory() { - const error = new Error('NOPE'); - error.name = 'NopeError'; - throw error; - }, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test'; caused by NopeError: NOPE", - ); - }); - - it('should refuse to create an instance with duplicate output', () => { - const attachments = new Map(); - expect(() => - createExtensionInstance({ - attachments, - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test1: testDataRef, - test2: testDataRef, - }, - factory({ bind }) { - bind({ test1: 'test', test2: 'test2' }); - }, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', duplicate extension data 'test' received via output 'test2'", - ); - }); - - it('should refuse to create an instance with disconnected output data', () => { - const attachments = new Map(); - expect(() => - createExtensionInstance({ - attachments, - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: { - test: testDataRef, - }, - factory({ bind }) { - bind({ nonexistent: 'test' } as any); - }, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', unknown output provided via 'nonexistent'", - ); - }); - - it('should refuse to create an instance with missing required input', () => { - expect(() => - createExtensionInstance({ - attachments: new Map(), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', input 'singleton' is required but was not received", - ); - }); - - it('should refuse to create an instance with undeclared inputs', () => { - expect(() => - createExtensionInstance({ - attachments: new Map([ - [ - 'declared', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - ], - ], - [ - 'undeclared', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - ], - ], - ]), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - declared: createExtensionInput({ - test: testDataRef, - }), - }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', received undeclared input 'undeclared' from extension 'core.test'", - ); - }); - - it('should refuse to create an instance with multiple undeclared inputs', () => { - expect(() => - createExtensionInstance({ - attachments: new Map([ - [ - 'undeclared1', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - ], - ], - [ - 'undeclared2', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - ], - ], - ]), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', received undeclared inputs 'undeclared1' from extension 'core.test' and 'undeclared2' from extensions 'core.test', 'core.test'", - ); - }); - - it('should refuse to create an instance with multiple inputs for required singleton', () => { - expect(() => - createExtensionInstance({ - attachments: new Map([ - [ - 'singleton', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many2' }, - extension: simpleExtension, - }), - ], - ], - ]), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', expected exactly one 'singleton' input but received multiple: 'core.test', 'core.test'", - ); - }); - - it('should refuse to create an instance with multiple inputs for optional singleton', () => { - expect(() => - createExtensionInstance({ - attachments: new Map([ - [ - 'singleton', - [ - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many1' }, - extension: simpleExtension, - }), - createExtensionInstance({ - attachments: new Map(), - config: { output: 'many2' }, - extension: simpleExtension, - }), - ], - ], - ]), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - test: testDataRef, - }, - { singleton: true, optional: true }, - ), - }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', expected at most one 'singleton' input but received multiple: 'core.test', 'core.test'", - ); - }); - - it('should refuse to create an instance with multiple inputs that did not provide required data', () => { - expect(() => - createExtensionInstance({ - attachments: new Map([ - [ - 'singleton', - [ - createExtensionInstance({ - attachments: new Map(), - config: undefined, - extension: simpleExtension, - }), - ], - ], - ]), - config: undefined, - extension: createExtension({ - id: 'core.test', - attachTo: { id: 'ignored', input: 'ignored' }, - inputs: { - singleton: createExtensionInput( - { - other: otherDataRef, - }, - { singleton: true }, - ), - }, - output: {}, - factory() {}, - }), - }), - ).toThrow( - "Failed to instantiate extension 'core.test', input 'singleton' did not receive required extension data 'other' from extension 'core.test'", - ); - }); -}); diff --git a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts b/packages/frontend-app-api/src/wiring/createExtensionInstance.ts deleted file mode 100644 index 767329da7f..0000000000 --- a/packages/frontend-app-api/src/wiring/createExtensionInstance.ts +++ /dev/null @@ -1,227 +0,0 @@ -/* - * 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 { - AnyExtensionDataMap, - AnyExtensionInputMap, - BackstagePlugin, - Extension, - ExtensionDataRef, -} from '@backstage/frontend-plugin-api'; -import mapValues from 'lodash/mapValues'; - -/** @internal */ -export interface ExtensionInstance { - readonly $$type: '@backstage/ExtensionInstance'; - - readonly id: string; - /** - * Get concrete value for the given extension data reference. Returns undefined if no value is available. - */ - getData(ref: ExtensionDataRef): T | undefined; - /** - * Maps input names to the actual instances given to them. - */ - readonly attachments: Map; - - readonly source?: BackstagePlugin; -} - -function resolveInputData( - dataMap: AnyExtensionDataMap, - attachment: ExtensionInstance, - inputName: string, -) { - return mapValues(dataMap, ref => { - const value = attachment.getData(ref); - if (value === undefined && !ref.config.optional) { - throw new Error( - `input '${inputName}' did not receive required extension data '${ref.id}' from extension '${attachment.id}'`, - ); - } - return value; - }); -} - -function resolveInputs( - inputMap: AnyExtensionInputMap, - attachments: Map, -) { - const undeclaredAttachments = Array.from(attachments.entries()).filter( - ([inputName]) => inputMap[inputName] === undefined, - ); - // TODO: Make this a warning rather than an error - if (undeclaredAttachments.length > 0) { - throw new Error( - `received undeclared input${ - undeclaredAttachments.length > 1 ? 's' : '' - } ${undeclaredAttachments - .map( - ([k, exts]) => - `'${k}' from extension${exts.length > 1 ? 's' : ''} '${exts - .map(e => e.id) - .join("', '")}'`, - ) - .join(' and ')}`, - ); - } - - return mapValues(inputMap, (input, inputName) => { - const attachedInstances = attachments.get(inputName) ?? []; - if (input.config.singleton) { - if (attachedInstances.length > 1) { - throw Error( - `expected ${ - input.config.optional ? 'at most' : 'exactly' - } one '${inputName}' input but received multiple: '${attachedInstances - .map(e => e.id) - .join("', '")}'`, - ); - } else if (attachedInstances.length === 0) { - if (input.config.optional) { - return undefined; - } - throw Error(`input '${inputName}' is required but was not received`); - } - return resolveInputData( - input.extensionData, - attachedInstances[0], - inputName, - ); - } - - return attachedInstances.map(attachment => - resolveInputData(input.extensionData, attachment, inputName), - ); - }); -} - -function indent(str: string) { - return str.replace(/^/gm, ' '); -} - -class ExtensionInstanceImpl implements ExtensionInstance { - readonly $$type = '@backstage/ExtensionInstance'; - - readonly id: string; - readonly #extensionData: Map; - readonly attachments: Map; - readonly source?: BackstagePlugin; - - constructor( - id: string, - extensionData: Map, - attachments: Map, - source: BackstagePlugin | undefined, - ) { - this.id = id; - this.#extensionData = extensionData; - this.attachments = attachments; - this.source = source; - } - - getData(ref: ExtensionDataRef): T | undefined { - return this.#extensionData.get(ref.id) as T | undefined; - } - - toJSON() { - return { - id: this.id, - output: - this.#extensionData.size > 0 - ? [...this.#extensionData.keys()] - : undefined, - attachments: - this.attachments.size > 0 - ? Object.fromEntries(this.attachments) - : undefined, - }; - } - - toString() { - const out = - this.#extensionData.size > 0 - ? ` out=[${[...this.#extensionData.keys()].join(', ')}]` - : ''; - - if (this.attachments.size === 0) { - return `<${this.id}${out} />`; - } - - return [ - `<${this.id}${out}>`, - ...[...this.attachments.entries()].map(([k, v]) => - indent([`${k} [`, ...v.map(e => indent(e.toString())), `]`].join('\n')), - ), - ``, - ].join('\n'); - } -} - -/** @internal */ -export function createExtensionInstance(options: { - extension: Extension; - config: unknown; - source?: BackstagePlugin; - attachments: Map; -}): ExtensionInstance { - const { extension, config, source, attachments } = options; - const extensionData = new Map(); - - let parsedConfig: unknown; - try { - parsedConfig = extension.configSchema?.parse(config ?? {}); - } catch (e) { - throw new Error( - `Invalid configuration for extension '${extension.id}'; caused by ${e}`, - ); - } - - try { - extension.factory({ - source, - config: parsedConfig, - bind: namedOutputs => { - for (const [name, output] of Object.entries(namedOutputs)) { - const ref = extension.output[name]; - if (!ref) { - throw new Error(`unknown output provided via '${name}'`); - } - if (extensionData.has(ref.id)) { - throw new Error( - `duplicate extension data '${ref.id}' received via output '${name}'`, - ); - } - extensionData.set(ref.id, output); - } - }, - inputs: resolveInputs(extension.inputs, attachments), - }); - } catch (e) { - throw new Error( - `Failed to instantiate extension '${extension.id}'${ - e.name === 'Error' ? `, ${e.message}` : `; caused by ${e}` - }`, - ); - } - - return new ExtensionInstanceImpl( - options.extension.id, - extensionData, - attachments, - source, - ); -} diff --git a/packages/frontend-app-api/src/wiring/parameters.ts b/packages/frontend-app-api/src/wiring/parameters.ts deleted file mode 100644 index 911f12f416..0000000000 --- a/packages/frontend-app-api/src/wiring/parameters.ts +++ /dev/null @@ -1,397 +0,0 @@ -/* - * 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 { Config } from '@backstage/config'; -import { - BackstagePlugin, - Extension, - ExtensionOverrides, -} from '@backstage/frontend-plugin-api'; -// eslint-disable-next-line @backstage/no-relative-monorepo-imports -import { toInternalExtensionOverrides } from '../../../frontend-plugin-api/src/wiring/createExtensionOverrides'; -import { JsonValue } from '@backstage/types'; - -export interface ExtensionParameters { - id: string; - attachTo?: { id: string; input: string }; - disabled?: boolean; - config?: unknown; -} - -const knownExtensionParameters = ['attachTo', 'disabled', 'config']; - -// Since we'll never merge arrays in config the config reader context -// isn't too much of a help. Fall back to manual config reading logic -// as the Config interface makes it quite hard for us otherwise. -/** @internal */ -export function readAppExtensionParameters( - rootConfig: Config, -): ExtensionParameters[] { - const arr = rootConfig.getOptional('app.extensions'); - if (!Array.isArray(arr)) { - if (arr === undefined) { - return []; - } - // This will throw, and show which part of config had the wrong type - rootConfig.getConfigArray('app.extensions'); - return []; - } - - return arr.map((arrayEntry, arrayIndex) => - expandShorthandExtensionParameters(arrayEntry, arrayIndex), - ); -} - -/** @internal */ -export function expandShorthandExtensionParameters( - arrayEntry: JsonValue, - arrayIndex: number, -): ExtensionParameters { - function errorMsg(msg: string, key?: string, prop?: string) { - return `Invalid extension configuration at app.extensions[${arrayIndex}]${ - key ? `[${key}]` : '' - }${prop ? `.${prop}` : ''}, ${msg}`; - } - - // NOTE(freben): This check is intentionally not complete and doesn't check - // whether letters and digits are used, etc. It's not up to the config reading - // logic to decide what constitutes a valid extension ID; that should be - // decided by the logic that loads and instantiates the extensions. This check - // is just here to catch real mistakes or truly conceptually wrong input. - function assertValidId(id: string) { - if (!id || id !== id.trim()) { - throw new Error( - errorMsg('extension ID must not be empty or contain whitespace'), - ); - } - - if (id.includes('/')) { - let message = `extension ID must not contain slashes; got '${id}'`; - const good = id.split('/')[0]; - if (good) { - message += `, did you mean '${good}'?`; - } - throw new Error(errorMsg(message)); - } - } - - // Example YAML: - // - entity.card.about - if (typeof arrayEntry === 'string') { - assertValidId(arrayEntry); - return { - id: arrayEntry, - disabled: false, - }; - } - - // All remaining cases are single-key objects - if ( - typeof arrayEntry !== 'object' || - arrayEntry === null || - Array.isArray(arrayEntry) - ) { - throw new Error(errorMsg('must be a string or an object')); - } - const keys = Object.keys(arrayEntry); - if (keys.length !== 1) { - const joinedKeys = keys.length ? `'${keys.join("', '")}'` : 'none'; - throw new Error(errorMsg(`must have exactly one key, got ${joinedKeys}`)); - } - - const id = String(keys[0]); - const value = arrayEntry[id]; - assertValidId(id); - - // This example covers a potentially common mistake in the syntax - // Example YAML: - // - entity.card.about: - if (value === null) { - return { - id, - disabled: false, - }; - } - - // Example YAML: - // - catalog.page.cicd: false - if (typeof value === 'boolean') { - return { - id, - disabled: !value, - }; - } - - // The remaining case is the generic object. Example YAML: - // - tech-radar.page: - // at: core.router/routes - // disabled: false - // config: - // path: /tech-radar - // width: 1500 - // height: 800 - if (typeof value !== 'object' || Array.isArray(value)) { - // We don't mention null here - we don't want people to explicitly enter - // - entity.card.about: null - throw new Error(errorMsg('value must be a boolean or object', id)); - } - - const attachTo = value.attachTo as { id: string; input: string } | undefined; - const disabled = value.disabled; - const config = value.config; - - if (attachTo !== undefined) { - if ( - attachTo === null || - typeof attachTo !== 'object' || - Array.isArray(attachTo) - ) { - throw new Error(errorMsg('must be an object', id, 'attachTo')); - } - if (typeof attachTo.id !== 'string' || attachTo.id === '') { - throw new Error( - errorMsg('must be a non-empty string', id, 'attachTo.id'), - ); - } - if (typeof attachTo.input !== 'string' || attachTo.input === '') { - throw new Error( - errorMsg('must be a non-empty string', id, 'attachTo.input'), - ); - } - } - if (disabled !== undefined && typeof disabled !== 'boolean') { - throw new Error(errorMsg('must be a boolean', id, 'disabled')); - } - if ( - config !== undefined && - (typeof config !== 'object' || config === null || Array.isArray(config)) - ) { - throw new Error(errorMsg('must be an object', id, 'config')); - } - - const unknownKeys = Object.keys(value).filter( - k => !knownExtensionParameters.includes(k), - ); - if (unknownKeys.length > 0) { - throw new Error( - errorMsg( - `unknown parameter; expected one of '${knownExtensionParameters.join( - "', '", - )}'`, - id, - unknownKeys.join(', '), - ), - ); - } - - return { - id, - attachTo, - disabled, - config, - }; -} - -export interface ExtensionInstanceParameters { - extension: Extension; - source?: BackstagePlugin; - attachTo: { id: string; input: string }; - config?: unknown; -} - -/** @internal */ -export function mergeExtensionParameters(options: { - features: (BackstagePlugin | ExtensionOverrides)[]; - builtinExtensions: Extension[]; - parameters: Array; -}): ExtensionInstanceParameters[] { - const { builtinExtensions, parameters } = options; - - const plugins = options.features.filter( - (f): f is BackstagePlugin => f.$$type === '@backstage/BackstagePlugin', - ); - const overrides = options.features.filter( - (f): f is ExtensionOverrides => - f.$$type === '@backstage/ExtensionOverrides', - ); - - const pluginExtensions = plugins.flatMap(source => { - return source.extensions.map(extension => ({ ...extension, source })); - }); - const overrideExtensions = overrides.flatMap( - override => toInternalExtensionOverrides(override).extensions, - ); - - // Prevent core override - if (pluginExtensions.some(({ id }) => id === 'core')) { - const pluginIds = pluginExtensions - .filter(({ id }) => id === 'core') - .map(({ source }) => source.id); - throw new Error( - `The following plugin(s) are overriding the 'core' extension which is forbidden: ${pluginIds.join( - ',', - )}`, - ); - } - - if (overrideExtensions.some(({ id }) => id === 'root')) { - throw new Error( - `An extension override is overriding the 'root' extension which is forbidden`, - ); - } - const overrideExtensionIds = overrideExtensions.map(({ id }) => id); - if (overrideExtensionIds.length !== new Set(overrideExtensionIds).size) { - const counts = new Map(); - for (const id of overrideExtensionIds) { - counts.set(id, (counts.get(id) ?? 0) + 1); - } - const duplicated = Array.from(counts.entries()) - .filter(([, count]) => count > 1) - .map(([id]) => id); - throw new Error( - `The following extensions had duplicate overrides: ${duplicated.join( - ', ', - )}`, - ); - } - - const configuredExtensions = [ - ...pluginExtensions.map(({ source, ...extension }) => ({ - extension, - params: { - source, - attachTo: extension.attachTo, - disabled: extension.disabled, - config: undefined as unknown, - }, - })), - ...builtinExtensions.map(extension => ({ - extension, - params: { - source: undefined, - attachTo: extension.attachTo, - disabled: extension.disabled, - config: undefined as unknown, - }, - })), - ]; - - // Install all extension overrides - for (const extension of overrideExtensions) { - // Check if our override is overriding an extension that already exists - const index = configuredExtensions.findIndex( - e => e.extension.id === extension.id, - ); - if (index !== -1) { - // Only implementation, attachment point and default disabled status are overridden, the source is kept - configuredExtensions[index].extension = extension; - configuredExtensions[index].params.attachTo = extension.attachTo; - configuredExtensions[index].params.disabled = extension.disabled; - } else { - // Add the extension as a new one when not overriding an existing one - configuredExtensions.push({ - extension, - params: { - source: undefined, - attachTo: extension.attachTo, - disabled: extension.disabled, - config: undefined, - }, - }); - } - } - - const duplicatedExtensionIds = new Set(); - const duplicatedExtensionData = configuredExtensions.reduce< - Record> - >((data, { extension, params }) => { - const extensionId = extension.id; - const extensionData = data?.[extensionId]; - if (extensionData) duplicatedExtensionIds.add(extensionId); - const pluginId = params.source?.id ?? 'internal'; - const pluginCount = extensionData?.[pluginId] ?? 0; - return { - ...data, - [extensionId]: { ...extensionData, [pluginId]: pluginCount + 1 }, - }; - }, {}); - - if (duplicatedExtensionIds.size > 0) { - throw new Error( - `The following extensions are duplicated: ${Array.from( - duplicatedExtensionIds, - ) - .map( - extensionId => - `The extension '${extensionId}' was provided ${Object.keys( - duplicatedExtensionData[extensionId], - ) - .map( - pluginId => - `${duplicatedExtensionData[extensionId][pluginId]} time(s) by the plugin '${pluginId}'`, - ) - .join(' and ')}`, - ) - .join(', ')}`, - ); - } - - for (const overrideParam of parameters) { - const extensionId = overrideParam.id; - - // Prevent core parametrization - if (extensionId === 'core') { - throw new Error( - "A 'core' extension configuration was detected, but the core extension is not configurable", - ); - } - - const existingIndex = configuredExtensions.findIndex( - e => e.extension.id === extensionId, - ); - if (existingIndex !== -1) { - const existing = configuredExtensions[existingIndex]; - if (overrideParam.attachTo) { - existing.params.attachTo = overrideParam.attachTo; - } - if (overrideParam.config) { - // TODO: merge config? - existing.params.config = overrideParam.config; - } - if ( - Boolean(existing.params.disabled) !== Boolean(overrideParam.disabled) - ) { - existing.params.disabled = Boolean(overrideParam.disabled); - if (!existing.params.disabled) { - // bump - configuredExtensions.splice(existingIndex, 1); - configuredExtensions.push(existing); - } - } - } else { - throw new Error(`Extension ${extensionId} does not exist`); - } - } - - return configuredExtensions - .filter(override => !override.params.disabled) - .map(param => ({ - extension: param.extension, - attachTo: param.params.attachTo, - source: param.params.source, - config: param.params.config, - })); -} diff --git a/packages/frontend-plugin-api/CHANGELOG.md b/packages/frontend-plugin-api/CHANGELOG.md index 8fa9a49726..1a432e19de 100644 --- a/packages/frontend-plugin-api/CHANGELOG.md +++ b/packages/frontend-plugin-api/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/frontend-plugin-api +## 0.3.0-next.0 + +### Minor Changes + +- 68fc9dc60e: Added `RouteRef`, `SubRouteRef`, `ExternalRouteRef`, and related types. All exports from this package that previously relied on the types with the same name from `@backstage/core-plugin-api` now use the new types instead. To convert and existing legacy route ref to be compatible with the APIs from this package, use the `convertLegacyRouteRef` utility from `@backstage/core-plugin-api/alpha`. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 6af88a05ff: Improve the extension boundary component and create a default extension suspense component. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/types@1.1.1 + ## 0.2.0 ### Minor Changes diff --git a/packages/frontend-plugin-api/api-report.md b/packages/frontend-plugin-api/api-report.md index ac8d164fae..250ce5f676 100644 --- a/packages/frontend-plugin-api/api-report.md +++ b/packages/frontend-plugin-api/api-report.md @@ -170,10 +170,9 @@ export interface CreateExtensionOptions< // (undocumented) factory(options: { source?: BackstagePlugin; - bind(values: Expand>): void; config: TConfig; inputs: Expand>; - }): void; + }): Expand>; // (undocumented) id: string; // (undocumented) @@ -313,13 +312,12 @@ export interface Extension { // (undocumented) factory(options: { source?: BackstagePlugin; - bind(values: ExtensionInputValues): void; config: TConfig; inputs: Record< string, undefined | Record | Array> >; - }): void; + }): ExtensionDataValues; // (undocumented) id: string; // (undocumented) diff --git a/packages/frontend-plugin-api/package.json b/packages/frontend-plugin-api/package.json index 528d86e0d1..37229dcff8 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.2.0", + "version": "0.3.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx index 704cafda2d..7b27c79360 100644 --- a/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx +++ b/packages/frontend-plugin-api/src/components/ExtensionBoundary.test.tsx @@ -66,8 +66,8 @@ const wrapInBoundaryExtension = (element: JSX.Element) => { path: coreExtensionData.routePath, routeRef: coreExtensionData.routeRef.optional(), }, - factory({ bind, source }) { - bind({ + factory({ source }) { + return { routeRef, path: '/', element: ( @@ -75,7 +75,7 @@ const wrapInBoundaryExtension = (element: JSX.Element) => { {element} ), - }); + }; }, }); }; diff --git a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts index f728261072..7b6dded022 100644 --- a/packages/frontend-plugin-api/src/extensions/createApiExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createApiExtension.ts @@ -58,12 +58,11 @@ export function createApiExtension< output: { api: coreExtensionData.apiFactory, }, - factory({ bind, config, inputs }) { + factory({ config, inputs }) { if (typeof factory === 'function') { - bind({ api: factory({ config, inputs }) }); - } else { - bind({ api: factory }); + return { api: factory({ config, inputs }) }; } + return { api: factory }; }, }); } diff --git a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx index ee38dda016..84fbe5ffee 100644 --- a/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createNavItemExtension.tsx @@ -41,14 +41,12 @@ export function createNavItemExtension(options: { output: { navTarget: coreExtensionData.navTarget, }, - factory: ({ bind, config }) => { - bind({ - navTarget: { - title: config.title, - icon, - routeRef, - }, - }); - }, + factory: ({ config }) => ({ + navTarget: { + title: config.title, + icon, + routeRef, + }, + }), }); } diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx index 37d97e7621..81843558da 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.test.tsx @@ -20,7 +20,6 @@ import { useAnalytics } from '@backstage/core-plugin-api'; import { waitFor } from '@testing-library/react'; import { PortableSchema } from '../schema'; import { - ExtensionInputValues, coreExtensionData, createExtensionInput, createPlugin, @@ -127,16 +126,14 @@ describe('createPageExtension', () => { loader: async () =>
Component
, }); - extension.factory({ - bind: (values: ExtensionInputValues) => - renderWithEffects( - wrapInTestApp(values.element as unknown as JSX.Element), - ), + const output = extension.factory({ source: createPlugin({ id: 'plugin ' }), config: { path: '/' }, inputs: {}, }); + renderWithEffects(wrapInTestApp(output.element as unknown as JSX.Element)); + await waitFor(() => expect(captureEvent).toHaveBeenCalledWith( '_ROUTABLE-EXTENSION-RENDERED', diff --git a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx index f05182149b..f5e258a1e4 100644 --- a/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx +++ b/packages/frontend-plugin-api/src/extensions/createPageExtension.tsx @@ -75,14 +75,14 @@ export function createPageExtension< path: coreExtensionData.routePath, routeRef: coreExtensionData.routeRef.optional(), }, - factory({ bind, config, inputs, source }) { + factory({ config, inputs, source }) { const ExtensionComponent = lazy(() => options .loader({ config, inputs }) .then(element => ({ default: () => element })), ); - bind({ + return { path: config.path, routeRef: options.routeRef, element: ( @@ -90,7 +90,7 @@ export function createPageExtension< ), - }); + }; }, }); } diff --git a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts index 38763688a4..d41121d51c 100644 --- a/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts +++ b/packages/frontend-plugin-api/src/extensions/createThemeExtension.ts @@ -25,8 +25,6 @@ export function createThemeExtension(theme: AppTheme) { output: { theme: coreExtensionData.theme, }, - factory({ bind }) { - bind({ theme }); - }, + factory: () => ({ theme }), }); } diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts index 757bcd7e3d..071272d58f 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.test.ts @@ -24,71 +24,211 @@ function unused(..._any: any[]) {} describe('createExtension', () => { it('should create an extension with a simple output', () => { - const extension = createExtension({ + const baseConfig = { id: 'test', attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, }, - factory({ bind }) { - bind({ + }; + const extension = createExtension({ + ...baseConfig, + factory() { + return { foo: 'bar', - }); - bind({ - // @ts-expect-error - foo: 3, - }); - bind({ - // @ts-expect-error - bar: 'bar', - }); - // @ts-expect-error - bind({}); - // @ts-expect-error - bind(); - // @ts-expect-error - bind('bar'); + }; }, }); expect(extension.id).toBe('test'); + + // When declared as an error function without a block the TypeScript errors + // are a more specific and will point at the property that is problematic. + createExtension({ + ...baseConfig, + factory: () => ({ + // @ts-expect-error + foo: 3, + }), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + ({ + bar: 'bar', + }), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + ({}), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + undefined, + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + 'bar', + }); + + // When declared as a function with a block the TypeScript error will instead + // be tied to the factory function declaration itself, but the error messages + // is still helpful and points to part of the return type that is problematic. + createExtension({ + ...baseConfig, + // @ts-expect-error + factory() { + return { + foo: 3, + }; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory() { + return { + bar: 'bar', + }; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory() { + return {}; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory() { + return {}; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory() { + return 'bar'; + }, + }); + + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => { + return { + foo: 3, + }; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => { + return { + bar: 'bar', + }; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => { + return {}; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => { + return {}; + }, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => { + return 'bar'; + }, + }); }); it('should create an extension with a some optional output', () => { - const extension = createExtension({ + const baseConfig = { id: 'test', attachTo: { id: 'root', input: 'default' }, output: { foo: stringData, bar: stringData.optional(), }, - factory({ bind }) { - bind({ - foo: 'bar', - }); - bind({ - foo: 'bar', - bar: 'baz', - }); - bind({ - // @ts-expect-error - foo: 3, - }); - bind({ - foo: 'bar', - // @ts-expect-error - bar: 3, - }); - // @ts-expect-error - bind({ bar: 'bar' }); - // @ts-expect-error - bind({}); - // @ts-expect-error - bind(); - // @ts-expect-error - bind('bar'); - }, + }; + const extension = createExtension({ + ...baseConfig, + factory: () => ({ + foo: 'bar', + }), }); expect(extension.id).toBe('test'); + + createExtension({ + ...baseConfig, + factory: () => ({ + foo: 'bar', + bar: 'baz', + }), + }); + createExtension({ + ...baseConfig, + factory: () => ({ + // @ts-expect-error + foo: 3, + }), + }); + createExtension({ + ...baseConfig, + factory: () => ({ + foo: 'bar', + // @ts-expect-error + bar: 3, + }), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + ({ bar: 'bar' }), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + ({}), + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + undefined, + }); + createExtension({ + ...baseConfig, + // @ts-expect-error + factory: () => {}, + }); + createExtension({ + ...baseConfig, + factory: () => + // @ts-expect-error + 'bar', + }); }); it('should create an extension with input', () => { @@ -110,7 +250,7 @@ describe('createExtension', () => { output: { foo: stringData, }, - factory({ bind, inputs }) { + factory({ inputs }) { const a1: string = inputs.mixed?.[0].required; // @ts-expect-error const a2: number = inputs.mixed?.[0].required; @@ -141,9 +281,9 @@ describe('createExtension', () => { const d4: number | undefined = inputs.onlyOptional?.[0].optional; unused(d1, d2, d3, d4); - bind({ + return { foo: 'bar', - }); + }; }, }); expect(extension.id).toBe('test'); diff --git a/packages/frontend-plugin-api/src/wiring/createExtension.ts b/packages/frontend-plugin-api/src/wiring/createExtension.ts index fa24a24797..ff011a5875 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtension.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtension.ts @@ -81,10 +81,9 @@ export interface CreateExtensionOptions< configSchema?: PortableSchema; factory(options: { source?: BackstagePlugin; - bind(values: Expand>): void; config: TConfig; inputs: Expand>; - }): void; + }): Expand>; } /** @public */ @@ -98,13 +97,12 @@ export interface Extension { configSchema?: PortableSchema; factory(options: { source?: BackstagePlugin; - bind(values: ExtensionInputValues): void; config: TConfig; inputs: Record< string, undefined | Record | Array> >; - }): void; + }): ExtensionDataValues; } /** @public */ @@ -120,12 +118,11 @@ export function createExtension< disabled: options.disabled ?? false, $$type: '@backstage/Extension', inputs: options.inputs ?? {}, - factory({ bind, config, inputs }) { + factory({ inputs, ...rest }) { // TODO: Simplify this, but TS wouldn't infer the input type for some reason return options.factory({ - bind, - config, inputs: inputs as Expand>, + ...rest, }); }, }; diff --git a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts index fa430a7834..eb69f954e4 100644 --- a/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createExtensionOverrides.test.ts @@ -39,7 +39,7 @@ describe('createExtensionOverrides', () => { id: 'a', attachTo: { id: 'core', input: 'apis' }, output: {}, - factory() {}, + factory: () => ({}), }), ], }), @@ -72,7 +72,7 @@ describe('createExtensionOverrides', () => { id: 'a', attachTo: { id: 'core', input: 'apis' }, output: {}, - factory() {}, + factory: () => ({}), }), ], }); diff --git a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts index 7f73c45544..42648fcd96 100644 --- a/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createPlugin.test.ts @@ -34,8 +34,8 @@ const TechRadarPage = createExtension({ output: { name: nameExtensionDataRef, }, - factory({ bind }) { - bind({ name: 'TechRadar' }); + factory() { + return { name: 'TechRadar' }; }, }); @@ -48,8 +48,8 @@ const CatalogPage = createExtension({ configSchema: createSchemaFromZod(z => z.object({ name: z.string().default('Catalog') }), ), - factory({ bind, config }) { - bind({ name: config.name }); + factory({ config }) { + return { name: config.name }; }, }); @@ -62,8 +62,8 @@ const TechDocsAddon = createExtension({ configSchema: createSchemaFromZod(z => z.object({ name: z.string().default('TechDocsAddon') }), ), - factory({ bind, config }) { - bind({ name: config.name }); + factory({ config }) { + return { name: config.name }; }, }); @@ -78,8 +78,8 @@ const TechDocsPage = createExtension({ output: { name: nameExtensionDataRef, }, - factory({ bind, inputs }) { - bind({ name: `TechDocs-${inputs.addons.map(n => n.name).join('-')}` }); + factory({ inputs }) { + return { name: `TechDocs-${inputs.addons.map(n => n.name).join('-')}` }; }, }); @@ -94,12 +94,12 @@ const outputExtension = createExtension({ output: { element: coreExtensionData.reactElement, }, - factory({ bind, inputs }) { - bind({ + factory({ inputs }) { + return { element: React.createElement('span', {}, [ `Names: ${inputs.names.map(n => n.name).join(', ')}`, ]), - }); + }; }, }); diff --git a/packages/integration-react/CHANGELOG.md b/packages/integration-react/CHANGELOG.md index f26a0e631b..d7e2289443 100644 --- a/packages/integration-react/CHANGELOG.md +++ b/packages/integration-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/integration-react +## 1.1.21-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + ## 1.1.20 ### Patch Changes diff --git a/packages/integration-react/package.json b/packages/integration-react/package.json index f5f77aa775..d2fe31eabc 100644 --- a/packages/integration-react/package.json +++ b/packages/integration-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/integration-react", "description": "Frontend package for managing integrations towards external systems", - "version": "1.1.20", + "version": "1.1.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/integration/src/azure/DefaultAzureDevOpsCredentialsProvider.test.ts b/packages/integration/src/azure/DefaultAzureDevOpsCredentialsProvider.test.ts index 8f66d5742d..6e171eff77 100644 --- a/packages/integration/src/azure/DefaultAzureDevOpsCredentialsProvider.test.ts +++ b/packages/integration/src/azure/DefaultAzureDevOpsCredentialsProvider.test.ts @@ -49,6 +49,19 @@ const MockedDefaultAzureCredential = DefaultAzureCredential as jest.MockedClass< jest.mock('@azure/identity'); describe('DefaultAzureDevOpsCredentialProvider', () => { + const fromAzureDevOpsCredential = jest.spyOn( + CachedAzureDevOpsCredentialsProvider, + 'fromAzureDevOpsCredential', + ); + const fromTokenCredential = jest.spyOn( + CachedAzureDevOpsCredentialsProvider, + 'fromTokenCredential', + ); + const fromPersonalAccessTokenCredential = jest.spyOn( + CachedAzureDevOpsCredentialsProvider, + 'fromPersonalAccessTokenCredential', + ); + const buildProvider = (azureIntegrations: AzureIntegrationConfigLike[]) => DefaultAzureDevOpsCredentialsProvider.fromIntegrations( ScmIntegrations.fromConfig( @@ -61,8 +74,7 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ); beforeEach(() => { - jest.resetAllMocks(); - + jest.clearAllMocks(); MockedClientSecretCredential.prototype.getToken.mockImplementation(() => Promise.resolve({ expiresOnTimestamp: DateTime.local().plus({ days: 1 }).toSeconds(), @@ -83,18 +95,6 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { token: 'fake-default-azure-credential-token', } as AccessToken), ); - - jest.spyOn( - CachedAzureDevOpsCredentialsProvider, - 'fromAzureDevOpsCredential', - ); - - jest.spyOn(CachedAzureDevOpsCredentialsProvider, 'fromTokenCredential'); - - jest.spyOn( - CachedAzureDevOpsCredentialsProvider, - 'fromPersonalAccessTokenCredential', - ); }); describe('fromIntegrations', () => { @@ -111,9 +111,7 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential, - ).toHaveBeenCalledTimes(1); + expect(fromAzureDevOpsCredential).toHaveBeenCalledTimes(1); }); it('Should create a single credential provider per credential', async () => { @@ -134,9 +132,7 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential, - ).toHaveBeenCalledTimes(2); + expect(fromAzureDevOpsCredential).toHaveBeenCalledTimes(2); }); it('Should create a default credential provider for Azure DevOps when no credential is specified', async () => { @@ -148,13 +144,11 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledTimes(1); + expect(fromTokenCredential).toHaveBeenCalledTimes(1); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledWith(new DefaultAzureCredential()); + expect(fromTokenCredential).toHaveBeenCalledWith( + new DefaultAzureCredential(), + ); }); it('Should create a default credential provider for Azure DevOps when no default credential is specified', async () => { @@ -173,13 +167,11 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledTimes(2); + expect(fromTokenCredential).toHaveBeenCalledTimes(2); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledWith(new DefaultAzureCredential()); + expect(fromTokenCredential).toHaveBeenCalledWith( + new DefaultAzureCredential(), + ); }); it('Should not create a default credential provider for Azure DevOps when another default credential is specified', async () => { @@ -197,13 +189,11 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledTimes(1); + expect(fromTokenCredential).toHaveBeenCalledTimes(1); - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledWith(expect.any(ClientSecretCredential)); + expect(fromTokenCredential).toHaveBeenCalledWith( + expect.any(ClientSecretCredential), + ); }); it('Should not create a default credential provider for on-premise Azure DevOps server when no credential is specified', async () => { @@ -215,18 +205,12 @@ describe('DefaultAzureDevOpsCredentialProvider', () => { ]); expect(provider).toBeDefined(); - expect( - CachedAzureDevOpsCredentialsProvider.fromAzureDevOpsCredential, - ).toHaveBeenCalledTimes(0); + expect(fromAzureDevOpsCredential).toHaveBeenCalledTimes(0); // expect 1 call because the Azure integration adds a default integration for dev.azure.com when it is not configured - expect( - CachedAzureDevOpsCredentialsProvider.fromTokenCredential, - ).toHaveBeenCalledTimes(1); + expect(fromTokenCredential).toHaveBeenCalledTimes(1); - expect( - CachedAzureDevOpsCredentialsProvider.fromPersonalAccessTokenCredential, - ).toHaveBeenCalledTimes(0); + expect(fromPersonalAccessTokenCredential).toHaveBeenCalledTimes(0); }); }); diff --git a/packages/repo-tools/CHANGELOG.md b/packages/repo-tools/CHANGELOG.md index 75ee910172..18ef451c79 100644 --- a/packages/repo-tools/CHANGELOG.md +++ b/packages/repo-tools/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/repo-tools +## 0.4.0-next.0 + +### Minor Changes + +- 4e36abef14: Remove support for the deprecated `--experimental-type-build` option for `package build`. +- 6694b369a3: Adds a new command `schema openapi test` that performs runtime validation of your OpenAPI specs using your test data. Under the hood, we're using Optic to perform this check, really cool work by them! + + To use this new command, you will have to run `yarn add @useoptic/optic` in the root of your repo. + +### Patch Changes + +- Updated dependencies + - @backstage/cli-node@0.2.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/errors@1.2.3 + ## 0.3.5 ### Patch Changes diff --git a/packages/repo-tools/cli-report.md b/packages/repo-tools/cli-report.md index 12d71f3be4..be1e7ffceb 100644 --- a/packages/repo-tools/cli-report.md +++ b/packages/repo-tools/cli-report.md @@ -73,6 +73,8 @@ Commands: verify [paths...] generate [paths...] lint [options] [paths...] + test [options] [paths...] + init help [command] ``` @@ -85,6 +87,15 @@ Options: -h, --help ``` +### `backstage-repo-tools schema openapi init` + +``` +Usage: backstage-repo-tools schema openapi init [options] + +Options: + -h, --help +``` + ### `backstage-repo-tools schema openapi lint` ``` @@ -95,6 +106,16 @@ Options: -h, --help ``` +### `backstage-repo-tools schema openapi test` + +``` +Usage: backstage-repo-tools schema openapi test [options] [paths...] + +Options: + --update + -h, --help +``` + ### `backstage-repo-tools schema openapi verify` ``` diff --git a/packages/repo-tools/package.json b/packages/repo-tools/package.json index fa398e598e..663695a764 100644 --- a/packages/repo-tools/package.json +++ b/packages/repo-tools/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/repo-tools", "description": "CLI for Backstage repo tooling ", - "version": "0.3.5", + "version": "0.4.0-next.0", "publishConfig": { "access": "public" }, @@ -43,7 +43,7 @@ "@stoplight/spectral-formatters": "^1.1.0", "@stoplight/spectral-functions": "^1.7.2", "@stoplight/spectral-parsers": "^1.0.2", - "@stoplight/spectral-rulesets": "^1.16.0", + "@stoplight/spectral-rulesets": "^1.18.0", "@stoplight/spectral-runtime": "^1.1.2", "@stoplight/types": "^13.14.0", "chalk": "^4.0.0", @@ -56,6 +56,7 @@ "lodash": "^4.17.21", "minimatch": "^5.1.1", "p-limit": "^3.0.2", + "portfinder": "^1.0.32", "ts-node": "^10.0.0", "yaml-diff-patch": "^2.0.0" }, @@ -65,12 +66,14 @@ "@types/is-glob": "^4.0.2", "@types/mock-fs": "^4.13.0", "@types/node": "^18.17.8", + "@types/prettier": "^2.0.0", "mock-fs": "^5.2.0" }, "peerDependencies": { "@microsoft/api-extractor-model": "*", "@microsoft/tsdoc": "*", "@microsoft/tsdoc-config": "*", + "@useoptic/optic": "^0.50.7", "prettier": "^2.8.1", "typescript": "> 3.0.0" }, diff --git a/packages/repo-tools/src/commands/api-reports/api-extractor.ts b/packages/repo-tools/src/commands/api-reports/api-extractor.ts index 13b6acb8fd..ce3e6f5b20 100644 --- a/packages/repo-tools/src/commands/api-reports/api-extractor.ts +++ b/packages/repo-tools/src/commands/api-reports/api-extractor.ts @@ -304,7 +304,6 @@ async function findPackageEntryPoints(packageDirs: string[]): Promise< Array<{ packageDir: string; name: string; - usesExperimentalTypeBuild?: boolean; }> > { return Promise.all( @@ -317,9 +316,6 @@ async function findPackageEntryPoints(packageDirs: string[]): Promise< getPackageExportNames(pkg)?.map(name => ({ packageDir, name })) ?? { packageDir, name: 'index', - usesExperimentalTypeBuild: pkg.scripts?.build?.includes( - '--experimental-type-build', - ), } ); }), @@ -367,11 +363,7 @@ export async function runApiExtraction({ } const warnings = new Array(); - for (const { - packageDir, - name, - usesExperimentalTypeBuild, - } of packageEntryPoints) { + for (const { packageDir, name } of packageEntryPoints) { console.log(`## Processing ${packageDir}`); const noBail = Array.isArray(allowWarnings) ? allowWarnings.some(aw => aw === packageDir || minimatch(packageDir, aw)) @@ -511,7 +503,6 @@ export async function runApiExtraction({ // The root index entrypoint is only allowed @public exports, while /alpha and /beta only allow @alpha and @beta. if ( validateReleaseTags && - !usesExperimentalTypeBuild && fs.pathExistsSync(extractorConfig.reportFilePath) ) { if (['index', 'alpha', 'beta'].includes(name)) { diff --git a/packages/repo-tools/src/commands/index.ts b/packages/repo-tools/src/commands/index.ts index c429a253f8..4f9ea87708 100644 --- a/packages/repo-tools/src/commands/index.ts +++ b/packages/repo-tools/src/commands/index.ts @@ -49,6 +49,17 @@ function registerSchemaCommand(program: Command) { 'Fail on any linting severity messages, not just errors.', ) .action(lazy(() => import('./openapi/lint').then(m => m.bulkCommand))); + + openApiCommand + .command('test [paths...]') + .description('Test OpenAPI schemas against written tests') + .option('--update', 'Update the spec on failure.') + .action(lazy(() => import('./openapi/test').then(m => m.bulkCommand))); + + openApiCommand + .command('init ') + .description('Creates any config needed for the test command.') + .action(lazy(() => import('./openapi/test/init').then(m => m.default))); } export function registerCommands(program: Command) { diff --git a/packages/repo-tools/src/commands/openapi/runner.ts b/packages/repo-tools/src/commands/openapi/runner.ts index baffb93808..cc354daaaa 100644 --- a/packages/repo-tools/src/commands/openapi/runner.ts +++ b/packages/repo-tools/src/commands/openapi/runner.ts @@ -18,21 +18,40 @@ import { resolvePackagePaths } from '../../lib/paths'; import pLimit from 'p-limit'; import { relative as relativePath } from 'path'; import { paths as cliPaths } from '../../lib/paths'; +import portFinder from 'portfinder'; export async function runner( paths: string[], - command: (dir: string) => Promise, + command: (dir: string, options?: { port: number }) => Promise, + options?: { + concurrencyLimit: number; + startingPort?: number; + }, ) { const packages = await resolvePackagePaths({ paths }); - const limit = pLimit(5); - + const limit = pLimit(options?.concurrencyLimit ?? 5); + let port = + options?.startingPort && + (await portFinder.getPortPromise({ + // Prevent collisions with optic which runs 8000->8999 + port: options.startingPort, + stopPort: options.startingPort + 1_000, + })); const resultsList = await Promise.all( packages.map(pkg => limit(async () => { let resultText = ''; try { + if (port) + port = + options?.startingPort && + (await portFinder.getPortPromise({ + // Prevent collisions with optic which runs 8000->8999 + port: port + 1, + stopPort: options.startingPort + 1_000, + })); console.log(`## Processing ${pkg}`); - await command(pkg); + await command(pkg, port ? { port } : undefined); } catch (err) { resultText = err.message; } diff --git a/packages/repo-tools/src/commands/openapi/test/index.ts b/packages/repo-tools/src/commands/openapi/test/index.ts new file mode 100644 index 0000000000..47b553aaef --- /dev/null +++ b/packages/repo-tools/src/commands/openapi/test/index.ts @@ -0,0 +1,118 @@ +/* + * 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 fs from 'fs-extra'; +import { join } from 'path'; +import chalk from 'chalk'; +import { runner } from '../runner'; +import { YAML_SCHEMA_PATH } from '../constants'; +import { paths as cliPaths } from '../../../lib/paths'; +import { exec } from '../../../lib/exec'; + +async function test( + directoryPath: string, + { port }: { port: number }, + options?: { update?: boolean }, +) { + const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); + if (!(await fs.pathExists(openapiPath))) { + return; + } + const opticConfigFilePath = join(directoryPath, 'optic.yml'); + if (!(await fs.pathExists(opticConfigFilePath))) { + return; + } + let opticLocation = ''; + try { + opticLocation = ( + await exec(`yarn bin optic`, [], { cwd: cliPaths.ownRoot }) + ).stdout as string; + } catch (err) { + throw new Error( + `Failed to find an Optic CLI installation, ensure that you have @useoptic/optic installed in the root of your repo. If not, run yarn add @useoptic/optic from the root of your repo.`, + ); + } + try { + await exec( + `${opticLocation.trim()} capture`, + [ + YAML_SCHEMA_PATH, + '--server-override', + `http://localhost:${port}`, + options?.update ? '--update' : '', + ], + { + cwd: directoryPath, + env: { + ...process.env, + PORT: `${port}`, + }, + }, + ); + } catch (err) { + // Optic outputs the actual results to stdout, but that will not be added to the message by default. + err.message = err.stderr + err.stdout; + err.message = (err.message as string) + .split('\n') + .map(e => e.replace(/.{1} Sending requests to server/, '')) + // Remove any lines that are emitted during processing and only show output. + .filter(e => !e.includes('PASS')) + .filter(e => e.trim()) + .join('\n'); + throw err; + } + if ( + (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) && + options?.update + ) { + await exec(`yarn prettier`, ['--write', openapiPath]); + } +} + +export async function bulkCommand( + paths: string[] = [], + options: { update?: boolean }, +): Promise { + const resultsList = await runner( + paths, + (dir, runnerOptions) => test(dir, runnerOptions!, options), + { + concurrencyLimit: 1, + startingPort: 9_000, + }, + ); + + let failed = false; + for (const { relativeDir, resultText } of resultsList) { + if (resultText) { + console.log(); + console.log( + chalk.red( + `OpenAPI runtime validation against tests failed in ${relativeDir}:`, + ), + ); + console.log(resultText.trimStart()); + + failed = true; + } + } + + if (failed) { + process.exit(1); + } else { + console.log(chalk.green('Verified all specifications against test data.')); + } +} diff --git a/packages/repo-tools/src/commands/openapi/test/init.ts b/packages/repo-tools/src/commands/openapi/test/init.ts new file mode 100644 index 0000000000..13c3aeb2b6 --- /dev/null +++ b/packages/repo-tools/src/commands/openapi/test/init.ts @@ -0,0 +1,90 @@ +/* + * 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 fs from 'fs-extra'; +import { join } from 'path'; +import { YAML_SCHEMA_PATH } from './../constants'; + +import { paths as cliPaths } from '../../../lib/paths'; +import { runner } from '../runner'; +import chalk from 'chalk'; +import { exec } from '../../../lib/exec'; + +const ROUTER_TEST_PATHS = [ + 'src/service/router.test.ts', + 'src/service/createRouter.test.ts', +]; + +async function init(directoryPath: string) { + const openapiPath = join(directoryPath, YAML_SCHEMA_PATH); + if (!(await fs.pathExists(openapiPath))) { + throw new Error( + `You do not have an OpenAPI YAML file at ${openapiPath}. Please create one and retry this command. If you already have existing test cases for your router, see 'backstage-repo-tools schema openapi test --update'`, + ); + } + const opticConfigFilePath = join(directoryPath, 'optic.yml'); + if (!(await fs.pathExists(opticConfigFilePath))) { + throw new Error(`This directory already has an optic.yml file. Exiting.`); + } + await fs.writeFile( + opticConfigFilePath, + `ruleset: + - breaking-changes +capture: + ${YAML_SCHEMA_PATH}: + # 🔧 Runnable example with simple get requests. + # Run with "PORT=3000 optic capture ${YAML_SCHEMA_PATH} --update interactive" in '${directoryPath}' + # You can change the server and the 'requests' section to experiment + server: + # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates. + url: http://localhost:3000 + requests: + # â„šī¸ Requests should be sent to the Optic proxy, the address of which is injected into 'run.command's env as OPTIC_PROXY (or the value of 'run.proxy_variable'). + run: + # 🔧 Specify a command that will generate traffic + command: yarn backstage-cli package test --no-watch ${ROUTER_TEST_PATHS.map( + e => `"${e}"`, + ).join(',')} + `, + ); + if (await cliPaths.resolveTargetRoot('node_modules/.bin/prettier')) { + await exec(`yarn prettier`, ['--write', opticConfigFilePath]); + } +} + +export default async function initCommand(paths: string[] = []) { + const resultsList = await runner(paths, dir => init(dir), { + concurrencyLimit: 5, + }); + + let failed = false; + for (const { relativeDir, resultText } of resultsList) { + if (resultText) { + console.log(); + console.log( + chalk.red(`Failed to initialize ${relativeDir} for OpenAPI commands.`), + ); + console.log(resultText.trimStart()); + + failed = true; + } + } + + if (failed) { + process.exit(1); + } else { + console.log(chalk.green(`All directories have already been configured.`)); + } +} diff --git a/packages/repo-tools/src/lib/exec.ts b/packages/repo-tools/src/lib/exec.ts new file mode 100644 index 0000000000..7f1bfe2302 --- /dev/null +++ b/packages/repo-tools/src/lib/exec.ts @@ -0,0 +1,33 @@ +/* + * 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 { promisify } from 'util'; +import { ExecOptions, exec as execCb } from 'child_process'; + +const execPromise = promisify(execCb); + +export const exec = ( + command: string, + options: string[] = [], + execOptions?: ExecOptions, +) => { + return execPromise( + [ + command, + ...options.filter(e => e).map(e => (e.startsWith('-') ? e : `"${e}"`)), + ].join(' '), + execOptions, + ); +}; diff --git a/packages/techdocs-cli-embedded-app/CHANGELOG.md b/packages/techdocs-cli-embedded-app/CHANGELOG.md index 7a935bc85c..67b125c2ed 100644 --- a/packages/techdocs-cli-embedded-app/CHANGELOG.md +++ b/packages/techdocs-cli-embedded-app/CHANGELOG.md @@ -1,5 +1,24 @@ # techdocs-cli-embedded-app +## 0.2.88-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/app-defaults@1.4.5-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + ## 0.2.87 ### Patch Changes diff --git a/packages/techdocs-cli-embedded-app/package.json b/packages/techdocs-cli-embedded-app/package.json index 8cd74ea71a..0b3e3d2020 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.87", + "version": "0.2.88-next.0", "private": true, "backstage": { "role": "frontend" diff --git a/packages/techdocs-cli/CHANGELOG.md b/packages/techdocs-cli/CHANGELOG.md index 1ab192f698..95895a7620 100644 --- a/packages/techdocs-cli/CHANGELOG.md +++ b/packages/techdocs-cli/CHANGELOG.md @@ -1,5 +1,16 @@ # @techdocs/cli +## 1.6.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + ## 1.6.0 ### Minor Changes diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index 7b97f13e3f..691e42a232 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -1,7 +1,7 @@ { "name": "@techdocs/cli", "description": "Utility CLI for managing TechDocs sites in Backstage.", - "version": "1.6.0", + "version": "1.6.1-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/test-utils/CHANGELOG.md b/packages/test-utils/CHANGELOG.md index 213877cfb0..9580b4855f 100644 --- a/packages/test-utils/CHANGELOG.md +++ b/packages/test-utils/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/test-utils +## 1.4.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + ## 1.4.4 ### Patch Changes diff --git a/packages/test-utils/package.json b/packages/test-utils/package.json index 28aa1715e6..35a434dd8f 100644 --- a/packages/test-utils/package.json +++ b/packages/test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/test-utils", "description": "Utilities to test Backstage plugins and apps.", - "version": "1.4.4", + "version": "1.4.5-next.0", "publishConfig": { "access": "public" }, diff --git a/packages/theme/CHANGELOG.md b/packages/theme/CHANGELOG.md index 69c88c79ab..b2583c299a 100644 --- a/packages/theme/CHANGELOG.md +++ b/packages/theme/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/theme +## 0.4.4-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + ## 0.4.3 ### Patch Changes diff --git a/packages/theme/package.json b/packages/theme/package.json index 77109aef87..7aed800cbc 100644 --- a/packages/theme/package.json +++ b/packages/theme/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/theme", "description": "material-ui theme for use with Backstage.", - "version": "0.4.3", + "version": "0.4.4-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/packages/version-bridge/CHANGELOG.md b/packages/version-bridge/CHANGELOG.md index bffaff7177..7e7a708be1 100644 --- a/packages/version-bridge/CHANGELOG.md +++ b/packages/version-bridge/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/version-bridge +## 1.0.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + ## 1.0.6 ### Patch Changes diff --git a/packages/version-bridge/package.json b/packages/version-bridge/package.json index caf196f497..4cb6119071 100644 --- a/packages/version-bridge/package.json +++ b/packages/version-bridge/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/version-bridge", "description": "Utilities used by @backstage packages to support multiple concurrent versions", - "version": "1.0.6", + "version": "1.0.7-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/adr-backend/CHANGELOG.md b/plugins/adr-backend/CHANGELOG.md index 16446bee2a..5bfae0013c 100644 --- a/plugins/adr-backend/CHANGELOG.md +++ b/plugins/adr-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-adr-backend +## 0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-adr-common@0.2.16 + - @backstage/plugin-search-common@1.2.7 + ## 0.4.3 ### Patch Changes diff --git a/plugins/adr-backend/package.json b/plugins/adr-backend/package.json index ffc5740aaf..b94c0f858f 100644 --- a/plugins/adr-backend/package.json +++ b/plugins/adr-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr-backend", - "version": "0.4.3", + "version": "0.4.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/adr/CHANGELOG.md b/plugins/adr/CHANGELOG.md index ea9f9519b0..79443b1988 100644 --- a/plugins/adr/CHANGELOG.md +++ b/plugins/adr/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-adr +## 0.6.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-adr-common@0.2.16 + - @backstage/plugin-search-common@1.2.7 + ## 0.6.8 ### Patch Changes diff --git a/plugins/adr/package.json b/plugins/adr/package.json index 86e07013bf..47cb0da80f 100644 --- a/plugins/adr/package.json +++ b/plugins/adr/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-adr", - "version": "0.6.8", + "version": "0.6.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake-backend/CHANGELOG.md b/plugins/airbrake-backend/CHANGELOG.md index d10077998b..225310011a 100644 --- a/plugins/airbrake-backend/CHANGELOG.md +++ b/plugins/airbrake-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-airbrake-backend +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + ## 0.3.3 ### Patch Changes diff --git a/plugins/airbrake-backend/package.json b/plugins/airbrake-backend/package.json index dda7f4e28d..f5732264c6 100644 --- a/plugins/airbrake-backend/package.json +++ b/plugins/airbrake-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake-backend", - "version": "0.3.3", + "version": "0.3.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/airbrake/CHANGELOG.md b/plugins/airbrake/CHANGELOG.md index 725d9f883a..38fa84f797 100644 --- a/plugins/airbrake/CHANGELOG.md +++ b/plugins/airbrake/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-airbrake +## 0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/dev-utils@1.0.23-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.3.25 ### Patch Changes diff --git a/plugins/airbrake/package.json b/plugins/airbrake/package.json index 4e6476ecea..ec5baa8acc 100644 --- a/plugins/airbrake/package.json +++ b/plugins/airbrake/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-airbrake", - "version": "0.3.25", + "version": "0.3.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/allure/CHANGELOG.md b/plugins/allure/CHANGELOG.md index 7117a5e22a..83bb766318 100644 --- a/plugins/allure/CHANGELOG.md +++ b/plugins/allure/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-allure +## 0.1.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.41 ### Patch Changes diff --git a/plugins/allure/package.json b/plugins/allure/package.json index 628eea6651..003d7fa853 100644 --- a/plugins/allure/package.json +++ b/plugins/allure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-allure", "description": "A Backstage plugin that integrates with Allure", - "version": "0.1.41", + "version": "0.1.42-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga/CHANGELOG.md b/plugins/analytics-module-ga/CHANGELOG.md index 72c42c20fb..fc3b9c7c4c 100644 --- a/plugins/analytics-module-ga/CHANGELOG.md +++ b/plugins/analytics-module-ga/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-analytics-module-ga +## 0.1.35-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + ## 0.1.34 ### Patch Changes diff --git a/plugins/analytics-module-ga/package.json b/plugins/analytics-module-ga/package.json index 44d922ed7a..878a28d382 100644 --- a/plugins/analytics-module-ga/package.json +++ b/plugins/analytics-module-ga/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga", - "version": "0.1.34", + "version": "0.1.35-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-ga4/CHANGELOG.md b/plugins/analytics-module-ga4/CHANGELOG.md index 366fc6e16f..43be5517d4 100644 --- a/plugins/analytics-module-ga4/CHANGELOG.md +++ b/plugins/analytics-module-ga4/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-analytics-module-ga4 +## 0.1.6-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + ## 0.1.5 ### Patch Changes diff --git a/plugins/analytics-module-ga4/package.json b/plugins/analytics-module-ga4/package.json index 705a430424..c6811cbb2d 100644 --- a/plugins/analytics-module-ga4/package.json +++ b/plugins/analytics-module-ga4/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-ga4", - "version": "0.1.5", + "version": "0.1.6-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/analytics-module-newrelic-browser/CHANGELOG.md b/plugins/analytics-module-newrelic-browser/CHANGELOG.md index 2d7e7ea467..dba2d14fd2 100644 --- a/plugins/analytics-module-newrelic-browser/CHANGELOG.md +++ b/plugins/analytics-module-newrelic-browser/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-analytics-module-newrelic-browser +## 0.0.4-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/config@1.1.1 + ## 0.0.3 ### Patch Changes diff --git a/plugins/analytics-module-newrelic-browser/package.json b/plugins/analytics-module-newrelic-browser/package.json index 06b3cf5e02..fcd1f67658 100644 --- a/plugins/analytics-module-newrelic-browser/package.json +++ b/plugins/analytics-module-newrelic-browser/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-analytics-module-newrelic-browser", - "version": "0.0.3", + "version": "0.0.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/apache-airflow/CHANGELOG.md b/plugins/apache-airflow/CHANGELOG.md index d245b155f6..435030cde4 100644 --- a/plugins/apache-airflow/CHANGELOG.md +++ b/plugins/apache-airflow/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-apache-airflow +## 0.2.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + ## 0.2.16 ### Patch Changes diff --git a/plugins/apache-airflow/package.json b/plugins/apache-airflow/package.json index e87179784f..26fa686c88 100644 --- a/plugins/apache-airflow/package.json +++ b/plugins/apache-airflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apache-airflow", - "version": "0.2.16", + "version": "0.2.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs-module-protoc-gen-doc/CHANGELOG.md b/plugins/api-docs-module-protoc-gen-doc/CHANGELOG.md index 883ed270fa..b31c53f633 100644 --- a/plugins/api-docs-module-protoc-gen-doc/CHANGELOG.md +++ b/plugins/api-docs-module-protoc-gen-doc/CHANGELOG.md @@ -1,5 +1,11 @@ # @backstage/plugin-api-docs-module-protoc-gen-doc +## 0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. + ## 0.1.4 ### Patch Changes diff --git a/plugins/api-docs-module-protoc-gen-doc/package.json b/plugins/api-docs-module-protoc-gen-doc/package.json index 80fe6645d7..4f038aaaec 100644 --- a/plugins/api-docs-module-protoc-gen-doc/package.json +++ b/plugins/api-docs-module-protoc-gen-doc/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs-module-protoc-gen-doc", "description": "Additional functionalities for the api-docs plugin that renders the output of the protoc-gen-doc", - "version": "0.1.4", + "version": "0.1.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 5cefe7b94b..7e0ac7c44d 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-api-docs +## 0.9.13-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.9.12 ### Patch Changes diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 53832682af..182380b6f6 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-api-docs", "description": "A Backstage plugin that helps represent API entities in the frontend", - "version": "0.9.12", + "version": "0.9.13-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.test.tsx b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.test.tsx index fa698ded58..6d0b191d68 100644 --- a/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.test.tsx +++ b/plugins/api-docs/src/components/ApiExplorerPage/DefaultApiExplorerPage.test.tsx @@ -52,6 +52,10 @@ describe('DefaultApiExplorerPage', () => { kind: 'API', metadata: { name: 'Entity1', + annotations: { + 'backstage.io/view-url': 'viewurl', + 'backstage.io/edit-url': 'editurl', + }, }, spec: { type: 'openapi' }, }, @@ -63,6 +67,11 @@ describe('DefaultApiExplorerPage', () => { getEntityFacets: async () => ({ facets: { 'relations.ownedBy': [] }, }), + queryEntities: async () => ({ + items: [], + pageInfo: {}, + totalItems: 0, + }), }; const configApi: ConfigApi = new ConfigReader({ @@ -152,37 +161,39 @@ describe('DefaultApiExplorerPage', () => { it('should render the default actions of an item in the grid', async () => { await renderWrapped(); - expect(await screen.findByText(/All apis \(1\)/)).toBeInTheDocument(); - expect(await screen.findByTitle(/View/)).toBeInTheDocument(); - expect(await screen.findByTitle(/View/)).toBeInTheDocument(); - expect(await screen.findByTitle(/Edit/)).toBeInTheDocument(); - expect(await screen.findByTitle(/Add to favorites/)).toBeInTheDocument(); + await waitFor(() => { + expect(screen.getByText(/All apis \(1\)/)).toBeInTheDocument(); + }); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /view/i })).toBeInTheDocument(); + }); + expect(screen.getByRole('button', { name: /edit/i })).toBeInTheDocument(); + expect(screen.getByTitle(/Add to favorites/)).toBeInTheDocument(); }); it('should render the custom actions of an item passed as prop', async () => { const actions: TableProps['actions'] = [ - () => { - return { - icon: () => , - tooltip: 'Foo Action', - disabled: false, - onClick: () => jest.fn(), - }; + { + icon: () => , + tooltip: 'Foo Action', + disabled: false, + onClick: jest.fn(), }, - () => { - return { - icon: () => , - tooltip: 'Bar Action', - disabled: true, - onClick: () => jest.fn(), - }; + { + icon: () => , + tooltip: 'Bar Action', + disabled: true, + onClick: jest.fn(), }, ]; await renderWrapped(); - expect(await screen.findByText(/All apis \(1\)/)).toBeInTheDocument(); - expect(await screen.findByTitle(/Foo Action/)).toBeInTheDocument(); - expect(await screen.findByTitle(/Bar Action/)).toBeInTheDocument(); - expect((await screen.findByTitle(/Bar Action/)).firstChild).toBeDisabled(); + await waitFor(() => { + expect(screen.getByText(/All apis \(1\)/)).toBeInTheDocument(); + }); + expect(screen.getByTitle(/Foo Action/)).toBeInTheDocument(); + expect(screen.getByTitle(/Bar Action/)).toBeInTheDocument(); + expect(screen.getByTitle(/Bar Action/).firstChild).toBeDisabled(); }); }); diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx index 64452b7191..3e2fbd1078 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx @@ -17,11 +17,10 @@ import AsyncApi from '@asyncapi/react-component'; import '@asyncapi/react-component/styles/default.css'; import { makeStyles, alpha, darken } from '@material-ui/core/styles'; -import { BackstageTheme } from '@backstage/theme'; import React from 'react'; import { useTheme } from '@material-ui/core'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ root: { fontFamily: 'inherit', '& .bg-white': { diff --git a/plugins/apollo-explorer/CHANGELOG.md b/plugins/apollo-explorer/CHANGELOG.md index a502d30e7d..9a405a026c 100644 --- a/plugins/apollo-explorer/CHANGELOG.md +++ b/plugins/apollo-explorer/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-apollo-explorer +## 0.1.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/apollo-explorer/package.json b/plugins/apollo-explorer/package.json index 6af85a2784..436d7dbc37 100644 --- a/plugins/apollo-explorer/package.json +++ b/plugins/apollo-explorer/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-apollo-explorer", - "version": "0.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-backend/CHANGELOG.md b/plugins/app-backend/CHANGELOG.md index 991c1defc5..acd36cac83 100644 --- a/plugins/app-backend/CHANGELOG.md +++ b/plugins/app-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-app-backend +## 0.3.55-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-app-node@0.1.7-next.0 + ## 0.3.54 ### Patch Changes diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index cda5301de8..0b3556c239 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-backend", "description": "A Backstage backend plugin that serves the Backstage frontend app", - "version": "0.3.54", + "version": "0.3.55-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/app-node/CHANGELOG.md b/plugins/app-node/CHANGELOG.md index 30bfaecc96..5cfa6b7aa2 100644 --- a/plugins/app-node/CHANGELOG.md +++ b/plugins/app-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-app-node +## 0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/app-node/package.json b/plugins/app-node/package.json index 915c5a0881..ac892910a5 100644 --- a/plugins/app-node/package.json +++ b/plugins/app-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-app-node", "description": "Node.js library for the app plugin", - "version": "0.1.6", + "version": "0.1.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md index 9cf2657b6b..a96202ef8a 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gcp-iap-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-gcp-iap-provider +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/auth-backend-module-gcp-iap-provider/package.json b/plugins/auth-backend-module-gcp-iap-provider/package.json index 85ddf5aaba..473dc7e3b6 100644 --- a/plugins/auth-backend-module-gcp-iap-provider/package.json +++ b/plugins/auth-backend-module-gcp-iap-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gcp-iap-provider", "description": "A GCP IAP auth provider module for the Backstage auth backend", - "version": "0.2.0", + "version": "0.2.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-github-provider/CHANGELOG.md b/plugins/auth-backend-module-github-provider/CHANGELOG.md index 80f47d6c2b..6b82880afb 100644 --- a/plugins/auth-backend-module-github-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-github-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-github-provider +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/auth-backend-module-github-provider/package.json b/plugins/auth-backend-module-github-provider/package.json index 85b6927ba5..9164f6fe2c 100644 --- a/plugins/auth-backend-module-github-provider/package.json +++ b/plugins/auth-backend-module-github-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-github-provider", "description": "The github-provider backend module for the auth plugin.", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md index aad16984c2..400bbbfbbf 100644 --- a/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-gitlab-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-gitlab-provider +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/auth-backend-module-gitlab-provider/package.json b/plugins/auth-backend-module-gitlab-provider/package.json index 85fb4b77df..735d241cc9 100644 --- a/plugins/auth-backend-module-gitlab-provider/package.json +++ b/plugins/auth-backend-module-gitlab-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-gitlab-provider", "description": "The gitlab-provider backend module for the auth plugin.", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-google-provider/CHANGELOG.md b/plugins/auth-backend-module-google-provider/CHANGELOG.md index 4b78ea58fa..2445a0adf1 100644 --- a/plugins/auth-backend-module-google-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-google-provider/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-auth-backend-module-google-provider +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/auth-backend-module-google-provider/package.json b/plugins/auth-backend-module-google-provider/package.json index 7332f0c5e9..ac0678caf7 100644 --- a/plugins/auth-backend-module-google-provider/package.json +++ b/plugins/auth-backend-module-google-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-google-provider", "description": "A Google auth provider module for the Backstage auth backend", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md index 4d982911e7..a45f3e21cc 100644 --- a/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-microsoft-provider/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-auth-backend-module-microsoft-provider +## 0.1.2-next.0 + +### Patch Changes + +- 2817115d09: Removed `prompt=consent` from start method to fix #20641 +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-microsoft-provider/api-report.md b/plugins/auth-backend-module-microsoft-provider/api-report.md index 60192c5cbc..21bf77d545 100644 --- a/plugins/auth-backend-module-microsoft-provider/api-report.md +++ b/plugins/auth-backend-module-microsoft-provider/api-report.md @@ -15,7 +15,10 @@ export const authModuleMicrosoftProvider: () => BackendFeature; // @public (undocumented) export const microsoftAuthenticator: OAuthAuthenticator< - PassportOAuthAuthenticatorHelper, + { + helper: PassportOAuthAuthenticatorHelper; + domainHint: string | undefined; + }, PassportProfile >; diff --git a/plugins/auth-backend-module-microsoft-provider/config.d.ts b/plugins/auth-backend-module-microsoft-provider/config.d.ts index 5f1fa958e5..aa78f72271 100644 --- a/plugins/auth-backend-module-microsoft-provider/config.d.ts +++ b/plugins/auth-backend-module-microsoft-provider/config.d.ts @@ -21,11 +21,12 @@ export interface Config { microsoft?: { [authEnv: string]: { clientId: string; + tenantId: string; /** * @visibility secret */ - tenantId: string; clientSecret: string; + domainHint?: string; callbackUrl?: string; }; }; diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 16c10667fe..907ba3f16f 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-microsoft-provider", "description": "The microsoft-provider backend module for the auth plugin.", - "version": "0.1.0", + "version": "0.1.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts index ba1d75adf8..9687c7ce1a 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts @@ -30,8 +30,9 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); const tenantId = config.getString('tenantId'); + const domainHint = config.getOptionalString('domainHint'); - return PassportOAuthAuthenticatorHelper.from( + const helper = PassportOAuthAuthenticatorHelper.from( new MicrosoftStrategy( { clientID: clientId, @@ -55,19 +56,30 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ }, ), ); + + return { + helper, + domainHint, + }; }, - async start(input, helper) { - return helper.start(input, { + async start(input, ctx) { + const options: Record = { accessType: 'offline', - }); + }; + + if (ctx.domainHint !== undefined) { + options.domain_hint = ctx.domainHint; + } + + return ctx.helper.start(input, options); }, - async authenticate(input, helper) { - return helper.authenticate(input); + async authenticate(input, ctx) { + return ctx.helper.authenticate(input); }, - async refresh(input, helper) { - return helper.refresh(input); + async refresh(input, ctx) { + return ctx.helper.refresh(input); }, }); diff --git a/plugins/auth-backend-module-microsoft-provider/src/module.test.ts b/plugins/auth-backend-module-microsoft-provider/src/module.test.ts index 2cfedc5d5e..21f9a7e845 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/module.test.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/module.test.ts @@ -21,7 +21,7 @@ import request from 'supertest'; import { authModuleMicrosoftProvider } from './module'; describe('authModuleMicrosoftProvider', () => { - it('should start', async () => { + it('should start without domain hint', async () => { const { server } = await startTestBackend({ features: [ authPlugin, @@ -77,4 +77,63 @@ describe('authModuleMicrosoftProvider', () => { nonce: decodeURIComponent(nonceCookie.value), }); }); + + it('should start with domain hint', async () => { + const { server } = await startTestBackend({ + features: [ + authPlugin, + authModuleMicrosoftProvider, + mockServices.rootConfig.factory({ + data: { + app: { + baseUrl: 'http://localhost:3000', + }, + auth: { + providers: { + microsoft: { + development: { + clientId: 'another-client-id', + clientSecret: 'another-client-secret', + tenantId: 'another-tenant-id', + domainHint: 'somedomain', + }, + }, + }, + }, + }, + }), + ], + }); + + const agent = request.agent(server); + + const res = await agent.get('/api/auth/microsoft/start?env=development'); + + expect(res.status).toEqual(302); + + const nonceCookie = agent.jar.getCookie('microsoft-nonce', { + domain: 'localhost', + path: '/api/auth/microsoft/handler', + script: false, + secure: false, + }); + expect(nonceCookie).toBeDefined(); + + const startUrl = new URL(res.get('location')); + expect(startUrl.origin).toBe('https://login.microsoftonline.com'); + expect(startUrl.pathname).toBe('/another-tenant-id/oauth2/v2.0/authorize'); + expect(Object.fromEntries(startUrl.searchParams)).toEqual({ + response_type: 'code', + scope: 'user.read', + client_id: 'another-client-id', + redirect_uri: `http://localhost:${server.port()}/api/auth/microsoft/handler/frame`, + state: expect.any(String), + domain_hint: 'somedomain', + }); + + expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ + env: 'development', + nonce: decodeURIComponent(nonceCookie.value), + }); + }); }); diff --git a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md index 3920726928..5236bdcd6c 100644 --- a/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-oauth2-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-oauth2-provider +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.3 ### Patch Changes diff --git a/plugins/auth-backend-module-oauth2-provider/package.json b/plugins/auth-backend-module-oauth2-provider/package.json index dda9aca87a..33dfd5582f 100644 --- a/plugins/auth-backend-module-oauth2-provider/package.json +++ b/plugins/auth-backend-module-oauth2-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oauth2-provider", "description": "The oauth2-provider backend module for the auth plugin.", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md index 3d24e48a89..ba7b20ca6a 100644 --- a/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md +++ b/plugins/auth-backend-module-pinniped-provider/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend-module-pinniped-provider +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/auth-backend-module-pinniped-provider/package.json b/plugins/auth-backend-module-pinniped-provider/package.json index 013fd6c49f..dcbd48c84f 100644 --- a/plugins/auth-backend-module-pinniped-provider/package.json +++ b/plugins/auth-backend-module-pinniped-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-pinniped-provider", "description": "The pinniped-provider backend module for the auth plugin.", - "version": "0.1.0", + "version": "0.1.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index d682f21c26..bff9f727fe 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,30 @@ # @backstage/plugin-auth-backend +## 0.20.0-next.0 + +### Minor Changes + +- bdf08ad04a: Adds the StaticTokenIssuer and StaticKeyStore, an alternative token issuer that can be used to sign the Authorization header using a predefined public/private key pair. + +### Patch Changes + +- 96c4f54bf6: Reverted the Microsoft auth provider to the previous implementation. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-backend-module-gcp-iap-provider@0.2.1-next.0 + - @backstage/plugin-auth-backend-module-github-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-gitlab-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-google-provider@0.1.4-next.0 + - @backstage/plugin-auth-backend-module-oauth2-provider@0.1.4-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.19.3 ### Patch Changes diff --git a/plugins/auth-backend/README.md b/plugins/auth-backend/README.md index ba0042c6f9..ce7525fb6b 100644 --- a/plugins/auth-backend/README.md +++ b/plugins/auth-backend/README.md @@ -158,6 +158,10 @@ To try out SAML, you can use the mock identity provider: [How to add an auth provider](https://github.com/backstage/backstage/blob/master/docs/auth/add-auth-provider.md) +## Token issuers + +[Configuring token issuers](https://github.com/backstage/backstage/blob/master/docs/auth/index.md) + ## Links - [The Backstage homepage](https://backstage.io) diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 41536f2a3f..6372c2ee60 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -538,9 +538,9 @@ export const providers: Readonly<{ | undefined, ) => AuthProviderFactory_2; resolvers: Readonly<{ - emailMatchingUserEntityProfileEmail: () => SignInResolver_2; - emailLocalPartMatchingUserEntityName: () => SignInResolver_2; - emailMatchingUserEntityAnnotation: () => SignInResolver_2; + emailLocalPartMatchingUserEntityName: () => SignInResolver; + emailMatchingUserEntityProfileEmail: () => SignInResolver; + emailMatchingUserEntityAnnotation(): SignInResolver; }>; }>; oauth2: Readonly<{ diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index f8953e1588..54fad12939 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -43,7 +43,7 @@ export interface Config { /** To control how to store JWK data in auth-backend */ keyStore?: { - provider?: 'database' | 'memory' | 'firestore'; + provider?: 'database' | 'memory' | 'firestore' | 'static'; firestore?: { /** The host to connect to */ host?: string; @@ -65,6 +65,21 @@ export interface Config { /** Timeout used for database operations. Defaults to 10000ms */ timeout?: number; }; + static?: { + /** Must be declared at least once and the first one will be used for signing */ + keys: Array<{ + /** Path to the public key file in the SPKI format */ + publicKeyFile: string; + /** Path to the matching private key file in the PKCS#8 format */ + privateKeyFile: string; + /** id to uniquely identify this key within the JWK set */ + keyId: string; + /** JWS "alg" (Algorithm) Header Parameter value. Defaults to ES256. + * Must match the algorithm used to generate the keys in the provided files + */ + algorithm?: string; + }>; + }; }; /** @@ -179,6 +194,18 @@ export interface Config { }; }; /** @visibility frontend */ + microsoft?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + tenantId: string; + callbackUrl?: string; + }; + }; + /** @visibility frontend */ onelogin?: { [authEnv: string]: { clientId: string; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 61e7993ab3..113fa8a027 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend", "description": "A Backstage backend plugin that handles authentication", - "version": "0.19.3", + "version": "0.20.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -42,7 +42,6 @@ "@backstage/plugin-auth-backend-module-github-provider": "workspace:^", "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^", "@backstage/plugin-auth-backend-module-google-provider": "workspace:^", - "@backstage/plugin-auth-backend-module-microsoft-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", diff --git a/plugins/auth-backend/src/identity/KeyStores.ts b/plugins/auth-backend/src/identity/KeyStores.ts index cc35c0d84e..7242530c57 100644 --- a/plugins/auth-backend/src/identity/KeyStores.ts +++ b/plugins/auth-backend/src/identity/KeyStores.ts @@ -18,12 +18,12 @@ import { pickBy } from 'lodash'; import { LoggerService } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; - import { AuthDatabase } from '../database/AuthDatabase'; import { DatabaseKeyStore } from './DatabaseKeyStore'; import { FirestoreKeyStore } from './FirestoreKeyStore'; import { MemoryKeyStore } from './MemoryKeyStore'; import { KeyStore } from './types'; +import { StaticKeyStore } from './StaticKeyStore'; type Options = { logger: LoggerService; @@ -75,6 +75,10 @@ export class KeyStores { return keyStore; } + if (provider === 'static') { + await StaticKeyStore.fromConfig(config); + } + throw new Error(`Unknown KeyStore provider: ${provider}`); } } diff --git a/plugins/auth-backend/src/identity/StaticKeyStore.test.ts b/plugins/auth-backend/src/identity/StaticKeyStore.test.ts new file mode 100644 index 0000000000..26ec9e4358 --- /dev/null +++ b/plugins/auth-backend/src/identity/StaticKeyStore.test.ts @@ -0,0 +1,113 @@ +/* + * 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 { StaticKeyStore } from './StaticKeyStore'; +import { AnyJWK } from './types'; +import { ConfigReader } from '@backstage/config'; +import { createMockDirectory } from '@backstage/backend-test-utils'; + +const privateKey = ` +-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR8Ja2ppMEgOm1KeY +Kpje00U1luybndt6yC263vcgeKqhRANCAAS+slUrS9JXgtHB1RcDnmlveuu4H3Zm +hQRjvYdO+Mg/3FJss6FaExESTzhPSr3X+be/exarkTMchbDXNEdCKwpn +-----END PRIVATE KEY----- +`.trim(); + +const publicKey = ` +-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvrJVK0vSV4LRwdUXA55pb3rruB92 +ZoUEY72HTvjIP9xSbLOhWhMREk84T0q91/m3v3sWq5EzHIWw1zRHQisKZw== +-----END PUBLIC KEY----- +`.trim(); + +describe('StaticKeyStore', () => { + let config: ConfigReader; + const sourceDir = createMockDirectory(); + + beforeAll(() => { + sourceDir.setContent({ + 'public.pem': publicKey, + 'private.pem': privateKey, + }); + + const publicKeyPath = sourceDir.resolve('public.pem'); + const privateKeyPath = sourceDir.resolve('private.pem'); + config = new ConfigReader({ + auth: { + keyStore: { + static: { + keys: [ + { + publicKeyFile: publicKeyPath, + privateKeyFile: privateKeyPath, + keyId: '1', + algorithm: 'ES256', + }, + { + publicKeyFile: publicKeyPath, + privateKeyFile: privateKeyPath, + keyId: '2', + algorithm: 'ES256', + }, + ], + }, + }, + }, + }); + }); + + it('should provide keys from disk', async () => { + const staticKeyStore = await StaticKeyStore.fromConfig(config); + const keys = await staticKeyStore.listKeys(); + expect(keys.items.length).toEqual(2); + expect(keys.items[0].key).toMatchObject({ + kid: '1', + alg: 'ES256', + }); + expect(keys.items[1].key).toMatchObject({ + kid: '2', + alg: 'ES256', + }); + + const pk = staticKeyStore.getPrivateKey('1'); + expect(pk).toMatchObject({ + kid: '1', + alg: 'ES256', + }); + expect(pk.d).toBeDefined(); + }); + + it('should not allow users to add keys', async () => { + const staticKeyStore = await StaticKeyStore.fromConfig(config); + + const key: AnyJWK = { + use: 'sig', + alg: 'ES256', + kid: '1', + kty: '1', + }; + expect(() => staticKeyStore.addKey(key)).toThrow( + 'Cannot add keys to the static key store', + ); + }); + + it('should not allow users to remove keys', async () => { + const staticKeyStore = await StaticKeyStore.fromConfig(config); + expect(() => staticKeyStore.removeKeys(['1'])).toThrow( + 'Cannot remove keys from the static key store', + ); + }); +}); diff --git a/plugins/auth-backend/src/identity/StaticKeyStore.ts b/plugins/auth-backend/src/identity/StaticKeyStore.ts new file mode 100644 index 0000000000..13b9faf771 --- /dev/null +++ b/plugins/auth-backend/src/identity/StaticKeyStore.ts @@ -0,0 +1,178 @@ +/* + * 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 { AnyJWK, KeyStore, StoredKey } from './types'; +import { exportJWK, importPKCS8, importSPKI, JWK } from 'jose'; +import { KeyLike } from 'jose/dist/types/types'; +import { promises as fs } from 'fs'; +import { Config } from '@backstage/config'; + +export type KeyPair = { + publicKey: JWK; + privateKey: JWK; +}; + +export type StaticKeyConfig = { + publicKeyFile: string; + privateKeyFile: string; + keyId: string; + algorithm: string; +}; + +const DEFAULT_ALGORITHM = 'ES256'; + +/** + * Key store that loads predefined public/private key pairs from disk + * + * The private key should be represented using the PKCS#8 format, + * while the public key should be in the SPKI format. + * + * @remarks + * + * You can generate a public and private key pair, using + * openssl: + * + * Generate a private key using the ES256 algorithm + * ```sh + * openssl ecparam -name prime256v1 -genkey -out private.ec.key + * ``` + * Convert it to PKCS#8 format + * ```sh + * openssl pkcs8 -topk8 -inform PEM -outform PEM -nocrypt -in private.ec.key -out private.key + * ``` + * Extract the public key + * ```sh + * openssl ec -inform PEM -outform PEM -pubout -in private.key -out public.key + * ``` + * + * Provide the paths to private.key and public.key as the respective + * private and public key paths in the StaticKeyStore.create(...) method. + */ +export class StaticKeyStore implements KeyStore { + private readonly keyPairs: KeyPair[]; + private readonly createdAt: Date; + + private constructor(keyPairs: KeyPair[]) { + if (keyPairs.length === 0) { + throw new Error('Should provide at least one key pair'); + } + + this.keyPairs = keyPairs; + this.createdAt = new Date(); + } + + public static async fromConfig(config: Config): Promise { + const keyConfigs = config + .getConfigArray('auth.keyStore.static.keys') + .map(c => { + const staticKeyConfig: StaticKeyConfig = { + publicKeyFile: c.getString('publicKeyFile'), + privateKeyFile: c.getString('privateKeyFile'), + keyId: c.getString('keyId'), + algorithm: c.getOptionalString('algorithm') ?? DEFAULT_ALGORITHM, + }; + + return staticKeyConfig; + }); + + const keyPairs = await Promise.all( + keyConfigs.map(async k => await this.loadKeyPair(k)), + ); + + return new StaticKeyStore(keyPairs); + } + + addKey(_key: AnyJWK): Promise { + throw new Error('Cannot add keys to the static key store'); + } + + listKeys(): Promise<{ items: StoredKey[] }> { + const keys = this.keyPairs.map(k => this.keyPairToStoredKey(k)); + return Promise.resolve({ items: keys }); + } + + getPrivateKey(keyId: string): JWK { + const keyPair = this.keyPairs.find(k => k.publicKey.kid === keyId); + if (keyPair === undefined) { + throw new Error(`Could not find key with keyId: ${keyId}`); + } + + return keyPair.privateKey; + } + + removeKeys(_kids: string[]): Promise { + throw new Error('Cannot remove keys from the static key store'); + } + + private keyPairToStoredKey(keyPair: KeyPair): StoredKey { + const publicKey = { + ...keyPair.publicKey, + use: 'sig', + }; + + return { + key: publicKey as AnyJWK, + createdAt: this.createdAt, + }; + } + + private static async loadKeyPair(options: StaticKeyConfig): Promise { + const algorithm = options.algorithm; + const keyId = options.keyId; + const publicKey = await this.loadPublicKeyFromFile( + options.publicKeyFile, + keyId, + algorithm, + ); + const privateKey = await this.loadPrivateKeyFromFile( + options.privateKeyFile, + keyId, + algorithm, + ); + + return { publicKey, privateKey }; + } + + private static async loadPublicKeyFromFile( + path: string, + keyId: string, + algorithm: string, + ): Promise { + return this.loadKeyFromFile(path, keyId, algorithm, importSPKI); + } + + private static async loadPrivateKeyFromFile( + path: string, + keyId: string, + algorithm: string, + ): Promise { + return this.loadKeyFromFile(path, keyId, algorithm, importPKCS8); + } + + private static async loadKeyFromFile( + path: string, + keyId: string, + algorithm: string, + importer: (content: string, algorithm: string) => Promise, + ): Promise { + const content = await fs.readFile(path, { encoding: 'utf8', flag: 'r' }); + const key = await importer(content, algorithm); + const jwk = await exportJWK(key); + jwk.kid = keyId; + jwk.alg = algorithm; + + return jwk; + } +} diff --git a/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts b/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts new file mode 100644 index 0000000000..3e5d659d54 --- /dev/null +++ b/plugins/auth-backend/src/identity/StaticTokenIssuer.test.ts @@ -0,0 +1,115 @@ +/* + * 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 { getVoidLogger } from '@backstage/backend-common'; +import { StaticTokenIssuer } from './StaticTokenIssuer'; +import { createLocalJWKSet, jwtVerify } from 'jose'; +import { stringifyEntityRef } from '@backstage/catalog-model'; +import { StaticKeyStore } from './StaticKeyStore'; + +const logger = getVoidLogger(); +const entityRef = stringifyEntityRef({ + kind: 'User', + namespace: 'default', + name: 'name', +}); + +describe('StaticTokenIssuer', () => { + it('should issue valid tokens signed by the first listed key', async () => { + const staticKeyStore = { + listKeys: () => { + return Promise.resolve({ + items: [ + { + createdAt: new Date(), + key: { + kty: 'EC', + x: 'vrJVK0vSV4LRwdUXA55pb3rruB92ZoUEY72HTvjIP9w', + y: 'UmyzoVoTERJPOE9Kvdf5t797FquRMxyFsNc0R0IrCmc', + crv: 'P-256', + kid: '1', + alg: 'ES256', + use: 'sig', + }, + }, + { + createdAt: new Date(), + key: { + kty: 'EC', + x: 'vrJVK0vSV4LRwdUXA55pb3rruB92ZoUEY72HTvjIP9w', + y: 'UmyzoVoTERJPOE9Kvdf5t797FquRMxyFsNc0R0IrCmc', + crv: 'P-256', + kid: '2', + alg: 'ES256', + use: 'sig', + }, + }, + ], + }); + }, + getPrivateKey: (kid: string) => { + expect(kid).toEqual('1'); + return { + kty: 'EC', + x: 'vrJVK0vSV4LRwdUXA55pb3rruB92ZoUEY72HTvjIP9w', + y: 'UmyzoVoTERJPOE9Kvdf5t797FquRMxyFsNc0R0IrCmc', + crv: 'P-256', + d: 'R8Ja2ppMEgOm1KeYKpje00U1luybndt6yC263vcgeKo', + kid: '1', + alg: 'ES256', + }; + }, + }; + + const keyDurationSeconds = 86400; + const options = { + logger, + issuer: 'my-issuer', + sessionExpirationSeconds: keyDurationSeconds, + }; + const issuer = new StaticTokenIssuer( + options, + staticKeyStore as unknown as StaticKeyStore, + ); + const token = await issuer.issueToken({ + claims: { + sub: entityRef, + ent: [entityRef], + 'x-fancy-claim': 'my special claim', + aud: 'this value will be overridden', + }, + }); + + const { keys } = await issuer.listPublicKeys(); + const keyStore = createLocalJWKSet({ keys: keys }); + const verifyResult = await jwtVerify(token, keyStore); + expect(verifyResult.protectedHeader).toEqual({ + kid: '1', + alg: 'ES256', + }); + expect(verifyResult.payload).toEqual({ + iss: 'my-issuer', + aud: 'backstage', + sub: entityRef, + ent: [entityRef], + 'x-fancy-claim': 'my special claim', + iat: expect.any(Number), + exp: expect.any(Number), + }); + expect(verifyResult.payload.exp).toBe( + verifyResult.payload.iat! + keyDurationSeconds, + ); + }); +}); diff --git a/plugins/auth-backend/src/identity/StaticTokenIssuer.ts b/plugins/auth-backend/src/identity/StaticTokenIssuer.ts new file mode 100644 index 0000000000..41dc96e71c --- /dev/null +++ b/plugins/auth-backend/src/identity/StaticTokenIssuer.ts @@ -0,0 +1,104 @@ +/* + * 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 { AnyJWK, TokenIssuer, TokenParams } from './types'; +import { SignJWT, importJWK, JWK } from 'jose'; +import { parseEntityRef } from '@backstage/catalog-model'; +import { AuthenticationError } from '@backstage/errors'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { StaticKeyStore } from './StaticKeyStore'; + +const MS_IN_S = 1000; + +export type Config = { + publicKeyFile: string; + privateKeyFile: string; + keyId: string; + algorithm?: string; +}; + +export type Options = { + logger: LoggerService; + /** Value of the issuer claim in issued tokens */ + issuer: string; + /** Expiration time of the JWT in seconds */ + sessionExpirationSeconds: number; +}; + +/** + * A token issuer that issues tokens from predefined + * public/private key pair stored in the static key store. + */ +export class StaticTokenIssuer implements TokenIssuer { + private readonly issuer: string; + private readonly logger: LoggerService; + private readonly keyStore: StaticKeyStore; + private readonly sessionExpirationSeconds: number; + + public constructor(options: Options, keyStore: StaticKeyStore) { + this.issuer = options.issuer; + this.logger = options.logger; + this.sessionExpirationSeconds = options.sessionExpirationSeconds; + this.keyStore = keyStore; + } + + public async issueToken(params: TokenParams): Promise { + const key = await this.getSigningKey(); + + // TODO: code shared with TokenFactory.ts + const iss = this.issuer; + const { sub, ent, ...additionalClaims } = params.claims; + const aud = 'backstage'; + const iat = Math.floor(Date.now() / MS_IN_S); + const exp = iat + this.sessionExpirationSeconds; + + // Validate that the subject claim is a valid EntityRef + try { + parseEntityRef(sub); + } catch (error) { + throw new Error( + '"sub" claim provided by the auth resolver is not a valid EntityRef.', + ); + } + + this.logger.info(`Issuing token for ${sub}, with entities ${ent ?? []}`); + + if (!key.alg) { + throw new AuthenticationError('No algorithm was provided in the key'); + } + + return new SignJWT({ ...additionalClaims, iss, sub, ent, aud, iat, exp }) + .setProtectedHeader({ alg: key.alg, kid: key.kid }) + .setIssuer(iss) + .setAudience(aud) + .setSubject(sub) + .setIssuedAt(iat) + .setExpirationTime(exp) + .sign(await importJWK(key)); + } + + private async getSigningKey(): Promise { + const { items: keys } = await this.keyStore.listKeys(); + if (keys.length >= 1) { + return this.keyStore.getPrivateKey(keys[0].key.kid); + } + throw new Error('Keystore should hold at least 1 key'); + } + + public async listPublicKeys(): Promise<{ keys: AnyJWK[] }> { + const { items: keys } = await this.keyStore.listKeys(); + return { keys: keys.map(({ key }) => key) }; + } +} diff --git a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts new file mode 100644 index 0000000000..dee37f0f6b --- /dev/null +++ b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts @@ -0,0 +1,90 @@ +/* + * 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 { FakeMicrosoftAPI } from './fake'; + +describe('FakeMicrosoftAPI', () => { + const api = new FakeMicrosoftAPI(); + + describe('#token', () => { + it('exchanges auth codes', () => { + const { access_token } = api.token( + new URLSearchParams({ + grant_type: 'authorization_code', + code: api.generateAuthCode('User.Read'), + }), + ); + + expect(api.tokenHasScope(access_token, 'User.Read')).toBe(true); + }); + + it('supports scopes for the first requested audience only', () => { + const { access_token } = api.token( + new URLSearchParams({ + grant_type: 'authorization_code', + code: api.generateAuthCode('someaudience/somescope User.Read'), + }), + ); + + expect(api.tokenHasScope(access_token, 'User.Read')).toBe(false); + }); + + it('special openid scopes do not count towards the 1-audience limit', () => { + const { access_token } = api.token( + new URLSearchParams({ + grant_type: 'authorization_code', + code: api.generateAuthCode('openid offline_access User.Read'), + }), + ); + + expect(api.tokenHasScope(access_token, 'User.Read')).toBe(true); + }); + + it('refreshes tokens', () => { + const { access_token } = api.token( + new URLSearchParams({ + grant_type: 'refresh_token', + refresh_token: api.generateRefreshToken( + 'email openid profile User.Read', + ), + }), + ); + + expect( + api.tokenHasScope(access_token, 'email openid profile User.Read'), + ).toBe(true); + }); + it('requires `openid` scope for ID token', () => { + const { id_token } = api.token( + new URLSearchParams({ + grant_type: 'authorization_code', + code: api.generateAuthCode('User.Read'), + }), + ); + + expect(id_token).toBeUndefined(); + }); + it('requires `offline_access` scope for refresh token', () => { + const { refresh_token } = api.token( + new URLSearchParams({ + grant_type: 'authorization_code', + code: api.generateAuthCode('User.Read'), + }), + ); + + expect(refresh_token).toBeUndefined(); + }); + }); +}); diff --git a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts new file mode 100644 index 0000000000..9b3ca09c57 --- /dev/null +++ b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts @@ -0,0 +1,126 @@ +/* + * 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 { decodeJwt } from 'jose'; + +type Claims = { aud: string; scp: string }; + +export class FakeMicrosoftAPI { + generateAccessToken(scope: string): string { + return this.tokenWithClaims(this.allClaimsForScope(scope)).access_token; + } + generateAuthCode(scope: string): string { + return this.encodeClaims(this.allClaimsForScope(scope)); + } + generateRefreshToken(scope: string): string { + return this.encodeClaims(this.allClaimsForScope(scope)); + } + token(formData: URLSearchParams): { + access_token: string; + scope: string; + refresh_token?: string; + id_token?: string; + } { + const scopeParameter = formData.get('scope'); + const claims = + (scopeParameter && this.allClaimsForScope(scopeParameter)) ?? + formData.get('grant_type') === 'refresh_token' + ? this.decodeClaims(formData.get('refresh_token')!) + : this.decodeClaims(formData.get('code')!); + return { + ...this.tokenWithClaims(claims), + ...(this.hasScope(claims, 'offline_access') && { + refresh_token: this.encodeClaims(claims), + }), + ...(this.hasScope(claims, 'openid') && { + id_token: 'header.e30K.microsoft', + }), + }; + } + tokenHasScope(token: string, scope: string): boolean { + const { aud, scp } = decodeJwt(token); + return this.hasScope({ aud: aud as string, scp: scp as string }, scope); + } + private tokenWithClaims(claims: Claims): { + access_token: string; + scope: string; + } { + const filteredClaims = { + ...claims, + scp: claims.scp + .split(' ') + .filter(s => s !== 'offline_access') + .join(' '), + }; + return { + access_token: `header.${Buffer.from( + JSON.stringify(filteredClaims), + ).toString('base64')}.signature`, + scope: this.scopeFromClaims(filteredClaims), + }; + } + private allClaimsForScope(scope: string): Claims { + const scopes = scope.split(' ').map(this.parseScope); + const firstAudience = scopes + .map(({ aud }) => aud) + .find(aud => aud !== 'openid'); + return { + aud: firstAudience ?? '00000003-0000-0000-c000-000000000000', + scp: scopes + .filter(({ aud }) => aud === 'openid' || aud === firstAudience) + .map(({ scp }) => scp) + .join(' '), + }; + } + // auth codes and refresh tokens in this fake system are base64-encoded JSON + // strings of claims + private encodeClaims(claims: Claims): string { + return Buffer.from(JSON.stringify(claims)).toString('base64'); + } + private decodeClaims(encoded: string): Claims { + return JSON.parse(Buffer.from(encoded, 'base64').toString()); + } + private hasScope(claims: Claims, scope: string): boolean { + return this.scopeFromClaims(claims).includes(scope); + } + private parseScope(s: string): Claims { + if (s.includes('/')) { + const [aud, scp] = s.split('/'); + return { aud, scp }; + } + switch (s) { + case 'email': + case 'openid': + case 'offline_access': + case 'profile': { + return { aud: 'openid', scp: s }; + } + default: + return { aud: '00000003-0000-0000-c000-000000000000', scp: s }; + } + } + private scopeFromClaims(claims: Claims): string { + return claims.scp + .split(' ') + .map(this.parseScope) + .map(({ aud, scp }) => + aud === 'openid' || + claims.aud === '00000003-0000-0000-c000-000000000000' + ? scp + : `${claims.aud}/${scp}`, + ) + .join(' '); + } +} diff --git a/plugins/auth-backend/src/providers/microsoft/provider.test.ts b/plugins/auth-backend/src/providers/microsoft/provider.test.ts new file mode 100644 index 0000000000..7bbfd085d6 --- /dev/null +++ b/plugins/auth-backend/src/providers/microsoft/provider.test.ts @@ -0,0 +1,450 @@ +/* + * 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 { microsoft } from './provider'; +import { getVoidLogger } from '@backstage/backend-common'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { AuthProviderRouteHandlers, AuthResolverContext } from '../types'; +import express from 'express'; +import crypto from 'crypto'; +import { FakeMicrosoftAPI } from './__testUtils__/fake'; + +describe('MicrosoftAuthProvider', () => { + const nonce = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 16 bytes of zeros in base64 + const state = Buffer.from( + `nonce=${encodeURIComponent(nonce)}&env=development`, + ).toString('hex'); + const mockBackstageToken = `header.${Buffer.from( + JSON.stringify({ sub: 'user:default/mock' }), + 'utf8', + ).toString('base64')}.backstage`; + + const server = setupServer(); + const microsoftApi = new FakeMicrosoftAPI(); + let provider: AuthProviderRouteHandlers; + let response: jest.Mocked; + + setupRequestMockHandlers(server); + + beforeEach(() => { + provider = microsoft.create({ + signIn: { + resolver: microsoft.resolvers.emailMatchingUserEntityAnnotation(), + }, + })({ + providerId: 'microsoft', + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + globalConfig: { + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + }, + config: new ConfigReader({ + development: { + tenantId: 'tenantId', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + }), + logger: getVoidLogger(), + resolverContext: { + issueToken: jest.fn(), + findCatalogUser: jest.fn(), + signInWithCatalogUser: async _ => ({ + token: mockBackstageToken, + }), + } as AuthResolverContext, + }) as AuthProviderRouteHandlers; + + server.use( + rest.post( + 'https://login.microsoftonline.com/tenantId/oauth2/v2.0/token', + async (req, res, ctx) => { + return res( + ctx.json({ + ...microsoftApi.token(new URLSearchParams(await req.text())), + token_type: 'Bearer', + expires_in: 123, + ext_expires_in: 123, + }), + ); + }, + ), + rest.get('https://graph.microsoft.com/v1.0/me/', (req, res, ctx) => { + if ( + !microsoftApi.tokenHasScope( + req.headers.get('authorization')!.replace(/^Bearer /, ''), + 'User.Read', + ) + ) { + return res(ctx.status(403)); + } + return res( + ctx.json({ + id: 'conrad', + displayName: 'Conrad', + surname: 'Ribas', + givenName: 'Francisco', + mail: 'conrad@example.com', + }), + ); + }), + rest.get( + 'https://graph.microsoft.com/v1.0/me/photos/*', + async (req, res, ctx) => { + if ( + !microsoftApi.tokenHasScope( + req.headers.get('authorization')!.replace(/^Bearer /, ''), + 'User.Read', + ) + ) { + return res(ctx.status(403)); + } + const imageBuffer = new Uint8Array([104, 111, 119, 100, 121]).buffer; + return res( + ctx.set('Content-Length', imageBuffer.byteLength.toString()), + ctx.set('Content-Type', 'image/jpeg'), + ctx.body(imageBuffer), + ); + }, + ), + ); + response = { + cookie: jest.fn(), + end: jest.fn(), + json: jest.fn(), + setHeader: jest.fn(), + status: jest.fn(), + } as unknown as jest.Mocked; + response.status.mockReturnValue(response); + }); + + describe('#start', () => { + const randomBytes = jest.spyOn( + crypto, + 'randomBytes', + ) as unknown as jest.MockedFunction<(size: number) => Buffer>; + + afterEach(() => { + randomBytes.mockRestore(); + }); + + it('redirects to authorize URL', async () => { + randomBytes.mockReturnValue(Buffer.from(nonce, 'base64')); + + await provider.start( + { + query: { + env: 'development', + scope: 'email openid profile User.Read', + }, + } as unknown as express.Request, + response, + ); + + expect(response.setHeader).toHaveBeenCalledWith( + 'Location', + 'https://login.microsoftonline.com/tenantId/oauth2/v2.0/authorize' + + '?response_type=code' + + `&redirect_uri=${encodeURIComponent( + 'http://backstage.test/api/auth/microsoft/handler/frame', + )}` + + `&scope=${encodeURIComponent('email openid profile User.Read')}` + + `&state=${state}` + + '&client_id=clientId', + ); + }); + }); + + describe('#handle', () => { + it('returns provider info and profile with photo data', async () => { + await provider.frameHandler( + { + query: { + env: 'development', + code: microsoftApi.generateAuthCode( + 'email openid profile User.Read', + ), + state, + }, + cookies: { + 'microsoft-nonce': nonce, + }, + } as unknown as express.Request, + response, + ); + + expect(response.end).toHaveBeenCalledWith( + expect.stringContaining( + encodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + response: { + providerInfo: { + accessToken: microsoftApi.generateAccessToken( + 'email openid profile User.Read', + ), + scope: 'email openid profile User.Read', + expiresInSeconds: 123, + idToken: 'header.e30K.microsoft', + }, + profile: { + email: 'conrad@example.com', + picture: 'data:image/jpeg;base64,aG93ZHk=', + displayName: 'Conrad', + }, + backstageIdentity: { + token: mockBackstageToken, + identity: { + type: 'user', + userEntityRef: 'user:default/mock', + ownershipEntityRefs: [], + }, + }, + }, + }), + ), + ), + ); + }); + + it('returns access token for non-microsoft graph scope', async () => { + await provider.frameHandler( + { + query: { + env: 'development', + code: microsoftApi.generateAuthCode('aks-audience/user.read'), + state, + }, + cookies: { + 'microsoft-nonce': nonce, + }, + } as unknown as express.Request, + response, + ); + + expect(response.end).toHaveBeenCalledWith( + expect.stringContaining( + encodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + response: { + providerInfo: { + accessToken: microsoftApi.generateAccessToken( + 'aks-audience/user.read', + ), + scope: 'aks-audience/user.read', + expiresInSeconds: 123, + }, + profile: {}, + }, + }), + ), + ), + ); + }); + + it('sets refresh token', async () => { + await provider.frameHandler( + { + query: { + env: 'development', + code: microsoftApi.generateAuthCode( + 'email offline_access openid profile User.Read', + ), + state, + }, + cookies: { + 'microsoft-nonce': nonce, + }, + } as unknown as express.Request, + response, + ); + + expect(response.cookie).toHaveBeenCalledWith( + 'microsoft-refresh-token', + microsoftApi.generateRefreshToken( + 'email offline_access openid profile User.Read', + ), + { + domain: 'backstage.test', + httpOnly: true, + maxAge: 86400000000, + path: '/api/auth/microsoft', + sameSite: 'lax', + secure: false, + }, + ); + }); + + it('omits photo data when fetching it fails', async () => { + server.use( + rest.get('https://graph.microsoft.com/v1.0/me/photos/*', (_, res) => + res.networkError('remote hung up'), + ), + ); + + await provider.frameHandler( + { + query: { + env: 'development', + code: microsoftApi.generateAuthCode( + 'email openid profile User.Read', + ), + state, + }, + cookies: { + 'microsoft-nonce': nonce, + }, + } as unknown as express.Request, + response, + ); + + expect(response.end).toHaveBeenCalledWith( + expect.stringContaining( + encodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + response: { + providerInfo: { + accessToken: microsoftApi.generateAccessToken( + 'email openid profile User.Read', + ), + scope: 'email openid profile User.Read', + expiresInSeconds: 123, + idToken: 'header.e30K.microsoft', + }, + profile: { + email: 'conrad@example.com', + displayName: 'Conrad', + }, + backstageIdentity: { + token: mockBackstageToken, + identity: { + type: 'user', + userEntityRef: 'user:default/mock', + ownershipEntityRefs: [], + }, + }, + }, + }), + ), + ), + ); + }); + }); + + describe('#refresh', () => { + it('returns provider info and profile with photo data', async () => { + await provider.refresh!( + { + query: { + env: 'development', + scope: 'email openid profile User.Read', + }, + header: jest.fn(_ => 'XMLHttpRequest'), + cookies: { + 'microsoft-refresh-token': microsoftApi.generateRefreshToken( + 'email openid profile User.Read', + ), + }, + get: jest.fn(), + } as unknown as express.Request, + response, + ); + + expect(response.json).toHaveBeenCalledWith( + expect.objectContaining({ + providerInfo: { + accessToken: microsoftApi.generateAccessToken( + 'email openid profile User.Read', + ), + scope: 'email openid profile User.Read', + expiresInSeconds: 123, + idToken: 'header.e30K.microsoft', + }, + profile: { + email: 'conrad@example.com', + picture: 'data:image/jpeg;base64,aG93ZHk=', + displayName: 'Conrad', + }, + }), + ); + }); + + it('returns access token for non-microsoft graph scope', async () => { + await provider.refresh!( + { + query: { + env: 'development', + scope: 'aks-audience/user.read', + }, + header: jest.fn(_ => 'XMLHttpRequest'), + cookies: { + 'microsoft-refresh-token': microsoftApi.generateRefreshToken( + 'aks-audience/user.read', + ), + }, + get: jest.fn(), + } as unknown as express.Request, + response, + ); + + expect(response.json).toHaveBeenCalledWith({ + providerInfo: { + accessToken: microsoftApi.generateAccessToken( + 'aks-audience/user.read', + ), + expiresInSeconds: 123, + scope: 'aks-audience/user.read', + }, + profile: {}, + }); + }); + + it('returns backstage identity', async () => { + await provider.refresh!( + { + query: { + env: 'development', + scope: 'email openid profile User.Read', + }, + header: jest.fn(_ => 'XMLHttpRequest'), + cookies: { + 'microsoft-refresh-token': microsoftApi.generateRefreshToken( + 'email openid profile User.Read', + ), + }, + get: jest.fn(), + } as unknown as express.Request, + response, + ); + + expect(response.json).toHaveBeenCalledWith( + expect.objectContaining({ + backstageIdentity: expect.objectContaining({ + token: mockBackstageToken, + }), + }), + ); + }); + }); +}); diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 8947c7432b..8fc8459f10 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -14,25 +14,218 @@ * limitations under the License. */ -import { SignInResolver, AuthHandler } from '../types'; -import { OAuthResult } from '../../lib/oauth'; +import express from 'express'; +import passport from 'passport'; +import { Strategy as MicrosoftStrategy } from 'passport-microsoft'; +import { + encodeState, + OAuthAdapter, + OAuthEnvironmentHandler, + OAuthHandlers, + OAuthProviderOptions, + OAuthRefreshRequest, + OAuthResponse, + OAuthResult, + OAuthStartRequest, +} from '../../lib/oauth'; +import { + executeFetchUserProfileStrategy, + executeFrameHandlerStrategy, + executeRedirectStrategy, + executeRefreshTokenStrategy, + makeProfileInfo, + PassportDoneCallback, +} from '../../lib/passport'; +import { + AuthHandler, + OAuthStartResponse, + SignInResolver, + AuthResolverContext, +} from '../types'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { - commonSignInResolvers, - createOAuthProviderFactory, -} from '@backstage/plugin-auth-node'; -import { - adaptLegacyOAuthHandler, - adaptLegacyOAuthSignInResolver, - adaptOAuthSignInResolverToLegacy, -} from '../../lib/legacy'; -import { - microsoftAuthenticator, - microsoftSignInResolvers, -} from '@backstage/plugin-auth-backend-module-microsoft-provider'; + commonByEmailLocalPartResolver, + commonByEmailResolver, +} from '../resolvers'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import fetch from 'node-fetch'; +import { decodeJwt } from 'jose'; +import { Profile as PassportProfile } from 'passport'; +import { BACKSTAGE_SESSION_EXPIRATION } from '../../lib/session'; + +type PrivateInfo = { + refreshToken: string; +}; + +type Options = OAuthProviderOptions & { + signInResolver?: SignInResolver; + authHandler: AuthHandler; + logger: LoggerService; + resolverContext: AuthResolverContext; + authorizationUrl?: string; + tokenUrl?: string; +}; + +export class MicrosoftAuthProvider implements OAuthHandlers { + private readonly _strategy: MicrosoftStrategy; + private readonly signInResolver?: SignInResolver; + private readonly authHandler: AuthHandler; + private readonly logger: LoggerService; + private readonly resolverContext: AuthResolverContext; + + constructor(options: Options) { + this.signInResolver = options.signInResolver; + this.authHandler = options.authHandler; + this.logger = options.logger; + this.resolverContext = options.resolverContext; + + this._strategy = new MicrosoftStrategy( + { + clientID: options.clientId, + clientSecret: options.clientSecret, + callbackURL: options.callbackUrl, + authorizationURL: options.authorizationUrl, + tokenURL: options.tokenUrl, + passReqToCallback: false, + skipUserProfile: ( + accessToken: string, + done: (err: unknown, skip: boolean) => void, + ) => { + done(null, this.skipUserProfile(accessToken)); + }, + }, + ( + accessToken: any, + refreshToken: any, + params: any, + fullProfile: passport.Profile, + done: PassportDoneCallback, + ) => { + done(undefined, { fullProfile, accessToken, params }, { refreshToken }); + }, + ); + } + + private skipUserProfile = (accessToken: string): boolean => { + const { aud, scp } = decodeJwt(accessToken); + const hasGraphReadScope = + aud === '00000003-0000-0000-c000-000000000000' && + (scp as string) + .split(' ') + .map(s => s.toLowerCase()) + .includes('user.read'); + return !hasGraphReadScope; + }; + + async start(req: OAuthStartRequest): Promise { + return await executeRedirectStrategy(req, this._strategy, { + scope: req.scope, + state: encodeState(req.state), + }); + } + + async handler(req: express.Request) { + const { result, privateInfo } = await executeFrameHandlerStrategy< + OAuthResult, + PrivateInfo + >(req, this._strategy); + + return { + response: await this.handleResult(result), + refreshToken: privateInfo.refreshToken, + }; + } + + async refresh(req: OAuthRefreshRequest) { + const { accessToken, refreshToken, params } = + await executeRefreshTokenStrategy( + this._strategy, + req.refreshToken, + req.scope, + ); + + return { + response: await this.handleResult({ + params, + accessToken, + ...(!this.skipUserProfile(accessToken) && { + fullProfile: await executeFetchUserProfileStrategy( + this._strategy, + accessToken, + ), + }), + }), + refreshToken, + }; + } + + private async handleResult(result: { + fullProfile?: PassportProfile; + params: { + id_token?: string; + scope: string; + expires_in: number; + }; + accessToken: string; + refreshToken?: string; + }): Promise { + let profile = {}; + if (result.fullProfile) { + const photo = await this.getUserPhoto(result.accessToken); + result.fullProfile.photos = photo ? [{ value: photo }] : undefined; + ({ profile } = await this.authHandler( + result as OAuthResult, + this.resolverContext, + )); + } + + const expiresInSeconds = + result.params.expires_in === undefined + ? BACKSTAGE_SESSION_EXPIRATION + : Math.min(result.params.expires_in, BACKSTAGE_SESSION_EXPIRATION); + + return { + providerInfo: { + accessToken: result.accessToken, + scope: result.params.scope, + expiresInSeconds, + ...{ idToken: result.params.id_token }, + }, + profile, + ...(result.fullProfile && + this.signInResolver && { + backstageIdentity: await this.signInResolver( + { result: result as OAuthResult, profile }, + this.resolverContext, + ), + }), + }; + } + + private async getUserPhoto(accessToken: string): Promise { + try { + const res = await fetch( + 'https://graph.microsoft.com/v1.0/me/photos/48x48/$value', + { + headers: { + Authorization: `Bearer ${accessToken}`, + }, + }, + ); + const data = await res.buffer(); + + return `data:image/jpeg;base64,${data.toString('base64')}`; + } catch (error) { + this.logger.warn( + `Could not retrieve user profile photo from Microsoft Graph API: ${error}`, + ); + return undefined; + } + } +} /** - * Auth provider integration for GitLab auth + * Auth provider integration for Microsoft auth * * @public */ @@ -48,21 +241,75 @@ export const microsoft = createAuthProviderIntegration({ * Configure sign-in for this provider, without it the provider can not be used to sign users in. */ signIn?: { + /** + * Maps an auth result to a Backstage identity for the user. + */ resolver: SignInResolver; }; }) { - return createOAuthProviderFactory({ - authenticator: microsoftAuthenticator, - profileTransform: adaptLegacyOAuthHandler(options?.authHandler), - signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), - }); + return ({ providerId, globalConfig, config, logger, resolverContext }) => + OAuthEnvironmentHandler.mapConfig(config, envConfig => { + const clientId = envConfig.getString('clientId'); + const clientSecret = envConfig.getString('clientSecret'); + const tenantId = envConfig.getString('tenantId'); + + const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); + const callbackUrl = + customCallbackUrl || + `${globalConfig.baseUrl}/${providerId}/handler/frame`; + const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`; + const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile ?? {}, params.id_token), + }); + + const provider = new MicrosoftAuthProvider({ + clientId, + clientSecret, + callbackUrl, + authorizationUrl, + tokenUrl, + authHandler, + signInResolver: options?.signIn?.resolver, + logger, + resolverContext, + }); + + return OAuthAdapter.fromConfig(globalConfig, provider, { + providerId, + callbackUrl, + }); + }); + }, + resolvers: { + /** + * Looks up the user by matching their email local part to the entity name. + */ + emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, + /** + * Looks up the user by matching their email to the entity email. + */ + emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, + /** + * Looks up the user by matching their email to the `microsoft.com/email` annotation. + */ + emailMatchingUserEntityAnnotation(): SignInResolver { + return async (info, ctx) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('Microsoft profile contained no email'); + } + + return ctx.signInWithCatalogUser({ + annotations: { + 'microsoft.com/email': profile.email, + }, + }); + }; + }, }, - resolvers: adaptOAuthSignInResolverToLegacy({ - emailLocalPartMatchingUserEntityName: - commonSignInResolvers.emailLocalPartMatchingUserEntityName(), - emailMatchingUserEntityProfileEmail: - commonSignInResolvers.emailMatchingUserEntityProfileEmail(), - emailMatchingUserEntityAnnotation: - microsoftSignInResolvers.emailMatchingUserEntityAnnotation(), - }), }); diff --git a/plugins/auth-backend/src/service/router.ts b/plugins/auth-backend/src/service/router.ts index e3bfc1c3f2..320870b3ee 100644 --- a/plugins/auth-backend/src/service/router.ts +++ b/plugins/auth-backend/src/service/router.ts @@ -38,6 +38,9 @@ import { Minimatch } from 'minimatch'; import { CatalogAuthResolverContext } from '../lib/resolvers'; import { AuthDatabase } from '../database/AuthDatabase'; import { BACKSTAGE_SESSION_EXPIRATION } from '../lib/session'; +import { TokenIssuer } from '../identity/types'; +import { StaticTokenIssuer } from '../identity/StaticTokenIssuer'; +import { StaticKeyStore } from '../identity/StaticKeyStore'; /** @public */ export type ProviderFactories = { [s: string]: AuthProviderFactory }; @@ -75,22 +78,34 @@ export async function createRouter( const authUrl = await discovery.getExternalBaseUrl('auth'); const authDb = AuthDatabase.create(database); + const sessionExpirationSeconds = BACKSTAGE_SESSION_EXPIRATION; + const keyStore = await KeyStores.fromConfig(config, { logger, database: authDb, }); - const keyDurationSeconds = BACKSTAGE_SESSION_EXPIRATION; - - const tokenIssuer = new TokenFactory({ - issuer: authUrl, - keyStore, - keyDurationSeconds, - logger: logger.child({ component: 'token-factory' }), - algorithm: - tokenFactoryAlgorithm ?? - config.getOptionalString('auth.identityTokenAlgorithm'), - }); + let tokenIssuer: TokenIssuer; + if (keyStore instanceof StaticKeyStore) { + tokenIssuer = new StaticTokenIssuer( + { + logger: logger.child({ component: 'token-factory' }), + issuer: authUrl, + sessionExpirationSeconds: sessionExpirationSeconds, + }, + keyStore as StaticKeyStore, + ); + } else { + tokenIssuer = new TokenFactory({ + issuer: authUrl, + keyStore, + keyDurationSeconds: sessionExpirationSeconds, + logger: logger.child({ component: 'token-factory' }), + algorithm: + tokenFactoryAlgorithm ?? + config.getOptionalString('auth.identityTokenAlgorithm'), + }); + } const secret = config.getOptionalString('auth.session.secret'); if (secret) { router.use(cookieParser(secret)); diff --git a/plugins/auth-node/CHANGELOG.md b/plugins/auth-node/CHANGELOG.md index b20ddd7697..d8953c8c94 100644 --- a/plugins/auth-node/CHANGELOG.md +++ b/plugins/auth-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-auth-node +## 0.4.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.4.0 ### Minor Changes diff --git a/plugins/auth-node/package.json b/plugins/auth-node/package.json index bf2257c942..3eb7a31ca7 100644 --- a/plugins/auth-node/package.json +++ b/plugins/auth-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-node", - "version": "0.4.0", + "version": "0.4.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops-backend/CHANGELOG.md b/plugins/azure-devops-backend/CHANGELOG.md index 605bc9c81c..12d2b37d5d 100644 --- a/plugins/azure-devops-backend/CHANGELOG.md +++ b/plugins/azure-devops-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-azure-devops-backend +## 0.4.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-devops-common@0.3.1 + ## 0.4.3 ### Patch Changes diff --git a/plugins/azure-devops-backend/package.json b/plugins/azure-devops-backend/package.json index 0186a9268b..218c58f665 100644 --- a/plugins/azure-devops-backend/package.json +++ b/plugins/azure-devops-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops-backend", - "version": "0.4.3", + "version": "0.4.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/CHANGELOG.md b/plugins/azure-devops/CHANGELOG.md index 1277fa0f1e..398d741652 100644 --- a/plugins/azure-devops/CHANGELOG.md +++ b/plugins/azure-devops/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-azure-devops +## 0.3.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 361bb34d8e: Consolidated getting the annotation values into a single function to help with future changes +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-azure-devops-common@0.3.1 + ## 0.3.7 ### Patch Changes diff --git a/plugins/azure-devops/package.json b/plugins/azure-devops/package.json index 7b5d8ee97a..a09b75ac9e 100644 --- a/plugins/azure-devops/package.json +++ b/plugins/azure-devops/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-devops", - "version": "0.3.7", + "version": "0.3.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx b/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx index ad278e3fc4..02a61a9a9a 100644 --- a/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx +++ b/plugins/azure-devops/src/components/EntityPageAzurePipelines/EntityPageAzurePipelines.tsx @@ -16,14 +16,14 @@ import { BuildTable } from '../BuildTable/BuildTable'; import React from 'react'; -import { getAnnotationFromEntity } from '../../utils/getAnnotationFromEntity'; +import { getAnnotationValuesFromEntity } from '../../utils/getAnnotationValuesFromEntity'; import { useBuildRuns } from '../../hooks/useBuildRuns'; import { useEntity } from '@backstage/plugin-catalog-react'; export const EntityPageAzurePipelines = (props: { defaultLimit?: number }) => { const { entity } = useEntity(); - const { project, repo, definition } = getAnnotationFromEntity(entity); + const { project, repo, definition } = getAnnotationValuesFromEntity(entity); const { items, loading, error } = useBuildRuns( project, diff --git a/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx b/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx index d06726e929..420fe6ecf4 100644 --- a/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx +++ b/plugins/azure-devops/src/components/ReadmeCard/ReadmeCard.tsx @@ -23,11 +23,9 @@ import { ErrorPanel, } from '@backstage/core-components'; import { useEntity } from '@backstage/plugin-catalog-react'; -import { useProjectRepoFromEntity } from '../../hooks'; -import { useApi } from '@backstage/core-plugin-api'; import React from 'react'; -import { azureDevOpsApiRef } from '../../api'; -import useAsync from 'react-use/lib/useAsync'; + +import { useReadme } from '../../hooks'; const useStyles = makeStyles(theme => ({ readMe: { @@ -85,18 +83,9 @@ const ReadmeCardError = ({ error }: ErrorProps) => { export const ReadmeCard = (props: Props) => { const classes = useStyles(); - const api = useApi(azureDevOpsApiRef); const { entity } = useEntity(); - const { project, repo } = useProjectRepoFromEntity(entity); - const { loading, error, value } = useAsync( - () => - api.getReadme({ - project, - repo, - }), - [api, project, repo, entity], - ); + const { loading, error, item: value } = useReadme(entity); if (loading) { return ; diff --git a/plugins/azure-devops/src/constants.ts b/plugins/azure-devops/src/constants.ts index 95e5be4930..aba0ec093a 100644 --- a/plugins/azure-devops/src/constants.ts +++ b/plugins/azure-devops/src/constants.ts @@ -16,6 +16,7 @@ export const AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION = 'dev.azure.com/build-definition'; +export const AZURE_DEVOPS_HOST_ORG_ANNOTATION = 'dev.azure.com/host-org'; export const AZURE_DEVOPS_PROJECT_ANNOTATION = 'dev.azure.com/project'; export const AZURE_DEVOPS_REPO_ANNOTATION = 'dev.azure.com/project-repo'; export const AZURE_DEVOPS_DEFAULT_TOP: number = 10; diff --git a/plugins/azure-devops/src/hooks/index.ts b/plugins/azure-devops/src/hooks/index.ts index a3864c21f7..ab1a3c9998 100644 --- a/plugins/azure-devops/src/hooks/index.ts +++ b/plugins/azure-devops/src/hooks/index.ts @@ -16,8 +16,8 @@ export * from './useAllTeams'; export * from './useDashboardPullRequests'; -export * from './useProjectRepoFromEntity'; export * from './usePullRequests'; export * from './useRepoBuilds'; export * from './useUserEmail'; export * from './useUserTeamIds'; +export * from './useReadme'; diff --git a/plugins/azure-devops/src/hooks/useGitTags.test.tsx b/plugins/azure-devops/src/hooks/useGitTags.test.tsx new file mode 100644 index 0000000000..1a5620e16e --- /dev/null +++ b/plugins/azure-devops/src/hooks/useGitTags.test.tsx @@ -0,0 +1,130 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderHook, waitFor } from '@testing-library/react'; +import { useGitTags } from './useGitTags'; +import { Entity } from '@backstage/catalog-model'; +import { GitTag } from '@backstage/plugin-azure-devops-common'; +import { TestApiProvider } from '@backstage/test-utils'; +import { AzureDevOpsApi, azureDevOpsApiRef } from '../api'; + +describe('useGitTags', () => { + const azureDevOpsApiMock = { + getGitTags: jest.fn(), + }; + const azureDevOpsApi = + azureDevOpsApiMock as Partial as AzureDevOpsApi; + + const Wrapper = (props: { children?: React.ReactNode }) => ( + + {props.children} + + ); + + it('should provide an array of GitTag', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const tags: GitTag[] = [ + { + objectId: 'tag-1', + peeledObjectId: 'tag-1', + name: 'tag-1', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + commitLink: 'https://dev.azure.com/org/project/repo/commit-sha-1', + }, + { + objectId: 'tag-2', + peeledObjectId: 'tag-2', + name: 'tag-2', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + commitLink: 'https://dev.azure.com/org/project/repo/commit-sha-2', + }, + { + objectId: 'tag-3', + peeledObjectId: 'tag-3', + name: 'tag-3', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + commitLink: 'https://dev.azure.com/org/project/repo/commit-sha-3', + }, + ]; + azureDevOpsApiMock.getGitTags.mockResolvedValue({ items: tags }); + const { result } = renderHook(() => useGitTags(entity), { + wrapper: Wrapper, + }); + + expect(result.current.loading).toEqual(true); + + await waitFor(() => { + expect(result.current).toEqual({ + error: undefined, + items: tags, + loading: false, + }); + }); + }); + + it('should return throw when annotation missing', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + }, + }; + + expect(() => + renderHook(() => useGitTags(entity), { + wrapper: Wrapper, + }), + ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + }); + + it('should return throw when annotation invalid', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'fake', + }, + }, + }; + + expect(() => + renderHook(() => useGitTags(entity), { + wrapper: Wrapper, + }), + ).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "fake"', + ); + }); +}); diff --git a/plugins/azure-devops/src/hooks/useGitTags.ts b/plugins/azure-devops/src/hooks/useGitTags.ts index b940a7105d..13c7118934 100644 --- a/plugins/azure-devops/src/hooks/useGitTags.ts +++ b/plugins/azure-devops/src/hooks/useGitTags.ts @@ -20,7 +20,7 @@ import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; -import { useProjectRepoFromEntity } from './useProjectRepoFromEntity'; +import { getAnnotationValuesFromEntity } from '../utils'; export function useGitTags(entity: Entity): { items?: GitTag[]; @@ -28,10 +28,10 @@ export function useGitTags(entity: Entity): { error?: Error; } { const api = useApi(azureDevOpsApiRef); - const { project, repo } = useProjectRepoFromEntity(entity); + const { project, repo } = getAnnotationValuesFromEntity(entity); - const { value, loading, error } = useAsync(() => { - return api.getGitTags(project, repo); + const { value, loading, error } = useAsync(async () => { + return await api.getGitTags(project, repo as string); }, [api, project, repo]); return { diff --git a/plugins/azure-devops/src/hooks/useProjectRepoFromEntity.ts b/plugins/azure-devops/src/hooks/useProjectRepoFromEntity.ts deleted file mode 100644 index b484bca009..0000000000 --- a/plugins/azure-devops/src/hooks/useProjectRepoFromEntity.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { Entity } from '@backstage/catalog-model'; -import { AZURE_DEVOPS_REPO_ANNOTATION } from '../constants'; - -export function useProjectRepoFromEntity(entity: Entity): { - project: string; - repo: string; -} { - const [project, repo] = ( - entity.metadata.annotations?.[AZURE_DEVOPS_REPO_ANNOTATION] ?? '' - ).split('/'); - - if (!project && !repo) { - throw new Error( - 'Value for annotation dev.azure.com/project-repo was not in the correct format: /', - ); - } - - if (!project) { - throw new Error( - 'Project Name for annotation dev.azure.com/project-repo was not found; expected format is: /', - ); - } - - if (!repo) { - throw new Error( - 'Repo Name for annotation dev.azure.com/project-repo was not found; expected format is: /', - ); - } - - return { project, repo }; -} diff --git a/plugins/azure-devops/src/hooks/usePullRequests.test.tsx b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx new file mode 100644 index 0000000000..3e51c003e6 --- /dev/null +++ b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx @@ -0,0 +1,129 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderHook, waitFor } from '@testing-library/react'; +import { Entity } from '@backstage/catalog-model'; +import { PullRequest } from '@backstage/plugin-azure-devops-common'; +import { TestApiProvider } from '@backstage/test-utils'; +import { AzureDevOpsApi, azureDevOpsApiRef } from '../api'; +import { usePullRequests } from './usePullRequests'; + +describe('usePullRequests', () => { + const azureDevOpsApiMock = { + getPullRequests: jest.fn(), + }; + const azureDevOpsApi = + azureDevOpsApiMock as Partial as AzureDevOpsApi; + + const Wrapper = (props: { children?: React.ReactNode }) => ( + + {props.children} + + ); + + it('should provide an array of PullRequest', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const pullRequests: PullRequest[] = [ + { + pullRequestId: 1, + repoName: 'repo', + title: 'title-1', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + { + pullRequestId: 2, + repoName: 'repo', + title: 'title-2', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + { + pullRequestId: 3, + repoName: 'repo', + title: 'title-3', + createdBy: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + ]; + azureDevOpsApiMock.getPullRequests.mockResolvedValue({ + items: pullRequests, + }); + const { result } = renderHook(() => usePullRequests(entity), { + wrapper: Wrapper, + }); + + expect(result.current.loading).toEqual(true); + + await waitFor(() => { + expect(result.current).toEqual({ + error: undefined, + items: pullRequests, + loading: false, + }); + }); + }); + + it('should return throw when annotation missing', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + }, + }; + + expect(() => + renderHook(() => usePullRequests(entity), { + wrapper: Wrapper, + }), + ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + }); + + it('should return throw when annotation invalid', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'fake', + }, + }, + }; + + expect(() => + renderHook(() => usePullRequests(entity), { + wrapper: Wrapper, + }), + ).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "fake"', + ); + }); +}); diff --git a/plugins/azure-devops/src/hooks/usePullRequests.ts b/plugins/azure-devops/src/hooks/usePullRequests.ts index 9ddca9650b..c4223871f2 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.ts +++ b/plugins/azure-devops/src/hooks/usePullRequests.ts @@ -25,7 +25,7 @@ import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; -import { useProjectRepoFromEntity } from './useProjectRepoFromEntity'; +import { getAnnotationValuesFromEntity } from '../utils'; export function usePullRequests( entity: Entity, @@ -44,10 +44,10 @@ export function usePullRequests( }; const api = useApi(azureDevOpsApiRef); - const { project, repo } = useProjectRepoFromEntity(entity); + const { project, repo } = getAnnotationValuesFromEntity(entity); - const { value, loading, error } = useAsync(() => { - return api.getPullRequests(project, repo, options); + const { value, loading, error } = useAsync(async () => { + return await api.getPullRequests(project, repo as string, options); }, [api, project, repo, top, status]); return { diff --git a/plugins/azure-devops/src/hooks/useReadme.test.tsx b/plugins/azure-devops/src/hooks/useReadme.test.tsx new file mode 100644 index 0000000000..ffc33c136b --- /dev/null +++ b/plugins/azure-devops/src/hooks/useReadme.test.tsx @@ -0,0 +1,108 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderHook, waitFor } from '@testing-library/react'; +import { Entity } from '@backstage/catalog-model'; +import { Readme } from '@backstage/plugin-azure-devops-common'; +import { TestApiProvider } from '@backstage/test-utils'; +import { AzureDevOpsApi, azureDevOpsApiRef } from '../api'; +import { useReadme } from './useReadme'; + +describe('useReadme', () => { + const azureDevOpsApiMock = { + getReadme: jest.fn(), + }; + const azureDevOpsApi = + azureDevOpsApiMock as Partial as AzureDevOpsApi; + + const Wrapper = (props: { children?: React.ReactNode }) => ( + + {props.children} + + ); + + it('should provide a Readme', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const readme: Readme = { + url: 'https://dev.azure.com/org/project/repo', + content: 'This is some fake README content', + }; + azureDevOpsApiMock.getReadme.mockResolvedValue({ + item: readme, + }); + const { result } = renderHook(() => useReadme(entity), { + wrapper: Wrapper, + }); + + expect(result.current.loading).toEqual(true); + + await waitFor(() => { + expect(result.current.item).toEqual({ + item: readme, + }); + }); + }); + + it('should return throw when annotation missing', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + }, + }; + + expect(() => + renderHook(() => useReadme(entity), { + wrapper: Wrapper, + }), + ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + }); + + it('should return throw when annotation invalid', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'fake', + }, + }, + }; + + expect(() => + renderHook(() => useReadme(entity), { + wrapper: Wrapper, + }), + ).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "fake"', + ); + }); +}); diff --git a/plugins/azure-devops/src/hooks/useReadme.ts b/plugins/azure-devops/src/hooks/useReadme.ts new file mode 100644 index 0000000000..60b2e21990 --- /dev/null +++ b/plugins/azure-devops/src/hooks/useReadme.ts @@ -0,0 +1,42 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Readme } from '@backstage/plugin-azure-devops-common'; + +import { Entity } from '@backstage/catalog-model'; +import { azureDevOpsApiRef } from '../api'; +import { useApi } from '@backstage/core-plugin-api'; +import useAsync from 'react-use/lib/useAsync'; +import { getAnnotationValuesFromEntity } from '../utils'; + +export function useReadme(entity: Entity): { + item?: Readme; + loading: boolean; + error?: Error; +} { + const api = useApi(azureDevOpsApiRef); + const { project, repo } = getAnnotationValuesFromEntity(entity); + + const { value, loading, error } = useAsync(async () => { + return await api.getReadme({ project, repo: repo as string }); + }, [api, project, repo]); + + return { + item: value, + loading, + error, + }; +} diff --git a/plugins/azure-devops/src/hooks/useRepoBuilds.test.tsx b/plugins/azure-devops/src/hooks/useRepoBuilds.test.tsx new file mode 100644 index 0000000000..99eaeb1ce3 --- /dev/null +++ b/plugins/azure-devops/src/hooks/useRepoBuilds.test.tsx @@ -0,0 +1,126 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { renderHook, waitFor } from '@testing-library/react'; +import { Entity } from '@backstage/catalog-model'; +import { RepoBuild } from '@backstage/plugin-azure-devops-common'; +import { TestApiProvider } from '@backstage/test-utils'; +import { AzureDevOpsApi, azureDevOpsApiRef } from '../api'; +import { useRepoBuilds } from './useRepoBuilds'; + +describe('useRepoBuilds', () => { + const azureDevOpsApiMock = { + getRepoBuilds: jest.fn(), + }; + const azureDevOpsApi = + azureDevOpsApiMock as Partial as AzureDevOpsApi; + + const Wrapper = (props: { children?: React.ReactNode }) => ( + + {props.children} + + ); + + it('should provide an array of RepoBuild', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const repoBuilds: RepoBuild[] = [ + { + id: 1, + title: 'title-1', + source: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + { + id: 2, + title: 'title-2', + source: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + { + id: 3, + title: 'title-3', + source: 'awanlin', + link: 'https://dev.azure.com/org/project/repo', + }, + ]; + azureDevOpsApiMock.getRepoBuilds.mockResolvedValue({ + items: repoBuilds, + }); + const { result } = renderHook(() => useRepoBuilds(entity), { + wrapper: Wrapper, + }); + + expect(result.current.loading).toEqual(true); + + await waitFor(() => { + expect(result.current).toEqual({ + error: undefined, + items: repoBuilds, + loading: false, + }); + }); + }); + + it('should return throw when annotation missing', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + }, + }; + + expect(() => + renderHook(() => useRepoBuilds(entity), { + wrapper: Wrapper, + }), + ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + }); + + it('should return throw when annotation invalid', async () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'fake', + }, + }, + }; + + expect(() => + renderHook(() => useRepoBuilds(entity), { + wrapper: Wrapper, + }), + ).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "fake"', + ); + }); +}); diff --git a/plugins/azure-devops/src/hooks/useRepoBuilds.ts b/plugins/azure-devops/src/hooks/useRepoBuilds.ts index e0fe8c2093..9292a6e035 100644 --- a/plugins/azure-devops/src/hooks/useRepoBuilds.ts +++ b/plugins/azure-devops/src/hooks/useRepoBuilds.ts @@ -24,7 +24,7 @@ import { Entity } from '@backstage/catalog-model'; import { azureDevOpsApiRef } from '../api'; import { useApi } from '@backstage/core-plugin-api'; import useAsync from 'react-use/lib/useAsync'; -import { useProjectRepoFromEntity } from './useProjectRepoFromEntity'; +import { getAnnotationValuesFromEntity } from '../utils'; export function useRepoBuilds( entity: Entity, @@ -40,10 +40,10 @@ export function useRepoBuilds( }; const api = useApi(azureDevOpsApiRef); - const { project, repo } = useProjectRepoFromEntity(entity); + const { project, repo } = getAnnotationValuesFromEntity(entity); - const { value, loading, error } = useAsync(() => { - return api.getRepoBuilds(project, repo, options); + const { value, loading, error } = useAsync(async () => { + return await api.getRepoBuilds(project, repo as string, options); }, [api, project, repo, entity]); return { diff --git a/plugins/azure-devops/src/utils/getAnnotationFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationFromEntity.ts deleted file mode 100644 index 41cc1e6628..0000000000 --- a/plugins/azure-devops/src/utils/getAnnotationFromEntity.ts +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { - AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, - AZURE_DEVOPS_PROJECT_ANNOTATION, - AZURE_DEVOPS_REPO_ANNOTATION, -} from '../constants'; - -import { Entity } from '@backstage/catalog-model'; - -export function getAnnotationFromEntity(entity: Entity): { - project: string; - repo?: string; - definition?: string; -} { - const annotation = - entity.metadata.annotations?.[AZURE_DEVOPS_REPO_ANNOTATION]; - if (annotation) { - const { project, repo } = getProjectRepo(annotation); - const definition = undefined; - return { project, repo, definition }; - } - - const project = - entity.metadata.annotations?.[AZURE_DEVOPS_PROJECT_ANNOTATION]; - if (!project) { - throw new Error('Value for annotation dev.azure.com/project was not found'); - } - - const definition = - entity.metadata.annotations?.[AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION]; - if (!definition) { - throw new Error( - 'Value for annotation dev.azure.com/build-definition was not found', - ); - } - - const repo = undefined; - return { project, repo, definition }; -} - -function getProjectRepo(annotation: string): { - project: string; - repo: string; -} { - const [project, repo] = annotation.split('/'); - - if (!project && !repo) { - throw new Error( - 'Value for annotation dev.azure.com/project-repo was not in the correct format: /', - ); - } - - return { project, repo }; -} diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts new file mode 100644 index 0000000000..67e46cee7b --- /dev/null +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts @@ -0,0 +1,310 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { getAnnotationValuesFromEntity } from './getAnnotationValuesFromEntity'; + +describe('getAnnotationValuesFromEntity', () => { + describe('with valid project-repo annotation', () => { + it('should return project and repo', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: 'repoName', + definition: undefined, + host: undefined, + org: undefined, + }); + }); + }); + + describe('with invalid project-repo annotation', () => { + it('should throw incorrect format error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'project', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "project"', + ); + }); + }); + + describe('with project-repo annotation missing project', () => { + it('should throw missing project error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': '/repo', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "/repo"', + ); + }); + }); + + describe('with project-repo annotation missing repo', () => { + it('should throw missing repo error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'project/', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/project-repo"; expected format is: /, found: "project/"', + ); + }); + }); + + describe('with valid project and build-definition annotations', () => { + it('should return project and definition', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-build-definition', + annotations: { + 'dev.azure.com/project': 'projectName', + 'dev.azure.com/build-definition': 'buildDefinitionName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: undefined, + definition: 'buildDefinitionName', + host: undefined, + org: undefined, + }); + }); + }); + + describe('with only project annotation', () => { + it('should should throw annotation not found error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project', + annotations: { + 'dev.azure.com/project': 'projectName', + }, + }, + }; + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Value for annotation "dev.azure.com/build-definition" was not found', + ); + }); + }); + + describe('with only build-definition annotation', () => { + it('should should throw annotation not found error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'build-definition', + annotations: { + 'dev.azure.com/build-definition': 'buildDefinitionName', + }, + }, + }; + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Value for annotation "dev.azure.com/project" was not found', + ); + }); + }); + + describe('with valid project-repo and host-org annotations', () => { + it('should return project, repo, host, and org', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + 'dev.azure.com/host-org': 'hostName/organizationName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: 'repoName', + definition: undefined, + host: 'hostName', + org: 'organizationName', + }); + }); + }); + + describe('with valid project, build-definition, and host-org annotations', () => { + it('should return project, definition, host and org', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-build-definition', + annotations: { + 'dev.azure.com/project': 'projectName', + 'dev.azure.com/build-definition': 'buildDefinitionName', + 'dev.azure.com/host-org': 'hostName/organizationName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: undefined, + definition: 'buildDefinitionName', + host: 'hostName', + org: 'organizationName', + }); + }); + }); + + describe('with invalid host-org annotation', () => { + it('should throw incorrect format error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'host-org', + annotations: { + 'dev.azure.com/host-org': 'host', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/host-org"; expected format is: /, found: "host"', + ); + }); + }); + + describe('with host-org annotation missing host', () => { + it('should throw missing project error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'host-org', + annotations: { + 'dev.azure.com/host-org': '/org', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/host-org"; expected format is: /, found: "/org"', + ); + }); + }); + + describe('with host-org annotation missing org', () => { + it('should throw missing repo error', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'host-org', + annotations: { + 'dev.azure.com/host-org': 'host/', + }, + }, + }; + + const test = () => { + return getAnnotationValuesFromEntity(entity); + }; + + expect(test).toThrow( + 'Invalid value for annotation "dev.azure.com/host-org"; expected format is: /, found: "host/"', + ); + }); + }); +}); diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts new file mode 100644 index 0000000000..9feec8bf95 --- /dev/null +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts @@ -0,0 +1,103 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION, + AZURE_DEVOPS_HOST_ORG_ANNOTATION, + AZURE_DEVOPS_PROJECT_ANNOTATION, + AZURE_DEVOPS_REPO_ANNOTATION, +} from '../constants'; + +import { Entity } from '@backstage/catalog-model'; + +export function getAnnotationValuesFromEntity(entity: Entity): { + project: string; + repo?: string; + definition?: string; + host?: string; + org?: string; +} { + const { host, org } = getHostOrg(entity.metadata.annotations); + + const projectRepoValues = getProjectRepo(entity.metadata.annotations); + if (projectRepoValues.project && projectRepoValues.repo) { + return { + project: projectRepoValues.project, + repo: projectRepoValues.repo, + host, + org, + }; + } + + const project = + entity.metadata.annotations?.[AZURE_DEVOPS_PROJECT_ANNOTATION]; + if (!project) { + throw new Error( + `Value for annotation "${AZURE_DEVOPS_PROJECT_ANNOTATION}" was not found`, + ); + } + + const definition = + entity.metadata.annotations?.[AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION]; + if (!definition) { + throw new Error( + `Value for annotation "${AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION}" was not found`, + ); + } + return { project, definition, host, org }; +} + +function getProjectRepo(annotations?: Record): { + project?: string; + repo?: string; +} { + const annotation = annotations?.[AZURE_DEVOPS_REPO_ANNOTATION]; + if (!annotation) { + return { project: undefined, repo: undefined }; + } + + if (annotation.includes('/')) { + const [project, repo] = annotation.split('/'); + if (project && repo) { + return { project, repo }; + } + } + + throw new Error( + `Invalid value for annotation "${AZURE_DEVOPS_REPO_ANNOTATION}"; expected format is: /, found: "${annotation}"`, + ); +} + +function getHostOrg(annotations?: Record): { + host?: string; + org?: string; +} { + const annotation = annotations?.[AZURE_DEVOPS_HOST_ORG_ANNOTATION]; + if (!annotation) { + return { host: undefined, org: undefined }; + } + + if (annotation.includes('/')) { + const [host, org] = annotation.split('/'); + if (host && org) { + return { host, org }; + } + } + + throw new Error( + `Invalid value for annotation "${AZURE_DEVOPS_HOST_ORG_ANNOTATION}"; expected format is: /, found: "${annotation}"`, + ); +} diff --git a/plugins/azure-devops/src/utils/index.ts b/plugins/azure-devops/src/utils/index.ts index 8655b261c3..998bb0e23d 100644 --- a/plugins/azure-devops/src/utils/index.ts +++ b/plugins/azure-devops/src/utils/index.ts @@ -17,4 +17,4 @@ export * from './arrayHas'; export * from './equalsIgnoreCase'; export * from './getDurationFromDates'; -export * from './getAnnotationFromEntity'; +export * from './getAnnotationValuesFromEntity'; diff --git a/plugins/azure-sites-backend/CHANGELOG.md b/plugins/azure-sites-backend/CHANGELOG.md index 203b9bab9c..d6275f8838 100644 --- a/plugins/azure-sites-backend/CHANGELOG.md +++ b/plugins/azure-sites-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-azure-sites-backend +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-azure-sites-common@0.1.1 + ## 0.1.16 ### Patch Changes diff --git a/plugins/azure-sites-backend/package.json b/plugins/azure-sites-backend/package.json index aa5d700be2..5be47519ad 100644 --- a/plugins/azure-sites-backend/package.json +++ b/plugins/azure-sites-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites-backend", - "version": "0.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/azure-sites/CHANGELOG.md b/plugins/azure-sites/CHANGELOG.md index 1d96a10e10..5cdba26fcb 100644 --- a/plugins/azure-sites/CHANGELOG.md +++ b/plugins/azure-sites/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-azure-sites +## 0.1.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-azure-sites-common@0.1.1 + ## 0.1.14 ### Patch Changes diff --git a/plugins/azure-sites/package.json b/plugins/azure-sites/package.json index c00f1f21af..eb5390d236 100644 --- a/plugins/azure-sites/package.json +++ b/plugins/azure-sites/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-azure-sites", - "version": "0.1.14", + "version": "0.1.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 3bf44d0960..58081b140a 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-badges-backend +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index fb2ab72985..08118445e2 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges-backend", "description": "A Backstage backend plugin that generates README badges for your entities", - "version": "0.3.3", + "version": "0.3.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 7ba4750487..4ee172f365 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-badges +## 0.2.50-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.49 ### Patch Changes diff --git a/plugins/badges/package.json b/plugins/badges/package.json index 6cb8fa7642..6b476cac8a 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-badges", "description": "A Backstage plugin that generates README badges for your entities", - "version": "0.2.49", + "version": "0.2.50-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/bazaar-backend/CHANGELOG.md b/plugins/bazaar-backend/CHANGELOG.md index f5df21413d..9b1e3662e1 100644 --- a/plugins/bazaar-backend/CHANGELOG.md +++ b/plugins/bazaar-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-bazaar-backend +## 0.3.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/bazaar-backend/alpha-api-report.md b/plugins/bazaar-backend/alpha-api-report.md new file mode 100644 index 0000000000..7ef46f20ab --- /dev/null +++ b/plugins/bazaar-backend/alpha-api-report.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-bazaar-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'; + +// @alpha +const _default: () => BackendFeature; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/bazaar-backend/api-report.md b/plugins/bazaar-backend/api-report.md index d9243fd3af..1ee889c04a 100644 --- a/plugins/bazaar-backend/api-report.md +++ b/plugins/bazaar-backend/api-report.md @@ -3,17 +3,12 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { Logger } from 'winston'; import { PluginDatabaseManager } from '@backstage/backend-common'; -// @alpha -const bazaarPlugin: () => BackendFeature; -export default bazaarPlugin; - // @public (undocumented) export function createRouter(options: RouterOptions): Promise; diff --git a/plugins/bazaar-backend/package.json b/plugins/bazaar-backend/package.json index 3fdfa32a7f..838f01577b 100644 --- a/plugins/bazaar-backend/package.json +++ b/plugins/bazaar-backend/package.json @@ -1,14 +1,26 @@ { "name": "@backstage/plugin-bazaar-backend", - "version": "0.3.3", + "version": "0.3.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin" @@ -21,7 +33,7 @@ }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/bazaar-backend/src/plugin.ts b/plugins/bazaar-backend/src/alpha.ts similarity index 96% rename from plugins/bazaar-backend/src/plugin.ts rename to plugins/bazaar-backend/src/alpha.ts index 1c06bda705..466181e073 100644 --- a/plugins/bazaar-backend/src/plugin.ts +++ b/plugins/bazaar-backend/src/alpha.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * * @alpha */ -export const bazaarPlugin = createBackendPlugin({ +export default createBackendPlugin({ pluginId: 'bazaar', register(env) { env.registerInit({ diff --git a/plugins/bazaar-backend/src/index.ts b/plugins/bazaar-backend/src/index.ts index d6f3df2d04..ca73cb27ba 100644 --- a/plugins/bazaar-backend/src/index.ts +++ b/plugins/bazaar-backend/src/index.ts @@ -15,4 +15,3 @@ */ export * from './service/router'; -export { bazaarPlugin as default } from './plugin'; diff --git a/plugins/bazaar/CHANGELOG.md b/plugins/bazaar/CHANGELOG.md index e39456be88..83691388dc 100644 --- a/plugins/bazaar/CHANGELOG.md +++ b/plugins/bazaar/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-bazaar +## 0.2.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/cli@0.24.0-next.0 + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.17 ### Patch Changes diff --git a/plugins/bazaar/package.json b/plugins/bazaar/package.json index ed3428d7f9..53985979d5 100644 --- a/plugins/bazaar/package.json +++ b/plugins/bazaar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bazaar", - "version": "0.2.17", + "version": "0.2.18-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,6 @@ "dependencies": { "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", - "@backstage/cli": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", diff --git a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx index c07ae0cfd1..e2029f7d9f 100644 --- a/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx +++ b/plugins/bazaar/src/components/AddProjectDialog/AddProjectDialog.tsx @@ -17,7 +17,7 @@ import React, { useState } from 'react'; import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; import { UseFormReset, UseFormGetValues } from 'react-hook-form'; -import { useApi } from '@backstage/core-plugin-api'; +import { useApi, alertApiRef } from '@backstage/core-plugin-api'; import { ProjectDialog } from '../ProjectDialog'; import { ProjectSelector } from '../ProjectSelector'; import { BazaarProject, FormValues, Size, Status } from '../../types'; @@ -39,6 +39,7 @@ export const AddProjectDialog = ({ fetchCatalogEntities, }: Props) => { const bazaarApi = useApi(bazaarApiRef); + const alertApi = useApi(alertApiRef); const [selectedEntity, setSelectedEntity] = useState(null); const defaultValues = { @@ -76,6 +77,11 @@ export const AddProjectDialog = ({ if (response.status === 'ok') { fetchBazaarProjects(); fetchCatalogEntities(); + alertApi.post({ + message: `Added project '${formValues.title}' to the Bazaar list`, + severity: 'success', + display: 'transient', + }); } handleClose(); diff --git a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx index 0c6fb97096..2c5539b988 100644 --- a/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx +++ b/plugins/bazaar/src/components/EditProjectDialog/EditProjectDialog.tsx @@ -15,7 +15,7 @@ */ import React, { useState, useEffect } from 'react'; -import { useApi } from '@backstage/core-plugin-api'; +import { useApi, alertApiRef } from '@backstage/core-plugin-api'; import { ProjectDialog } from '../ProjectDialog'; import { BazaarProject, FormValues } from '../../types'; import { bazaarApiRef } from '../../api'; @@ -52,6 +52,7 @@ export const EditProjectDialog = ({ }: Props) => { const classes = useStyles(); const bazaarApi = useApi(bazaarApiRef); + const alertApi = useApi(alertApiRef); const [openDelete, setOpenDelete] = useState(false); const [defaultValues, setDefaultValues] = useState({ ...bazaarProject, @@ -71,6 +72,11 @@ export const EditProjectDialog = ({ handleDeleteClose(); fetchBazaarProject(); + alertApi.post({ + message: `Deleted project '${bazaarProject.title}' from the Bazaar list`, + severity: 'success', + display: 'transient', + }); }; useEffect(() => { @@ -97,6 +103,11 @@ export const EditProjectDialog = ({ if (updateResponse.status === 'ok') fetchBazaarProject(); handleEditClose(); + alertApi.post({ + message: `Updated project '${formValues.title}' in the Bazaar list`, + severity: 'success', + display: 'transient', + }); }; return ( diff --git a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx index 61d1dfebb9..eb125d2111 100644 --- a/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx +++ b/plugins/bazaar/src/components/ProjectCard/ProjectCard.tsx @@ -29,7 +29,6 @@ import { BazaarProject } from '../../types'; import { DateTime } from 'luxon'; import { HomePageBazaarInfoCard } from '../HomePageBazaarInfoCard'; import { Entity } from '@backstage/catalog-model'; -import { BackstageTheme } from '@backstage/theme'; type Props = { project: BazaarProject; @@ -42,7 +41,7 @@ type StyleProps = { height: 'large' | 'small'; }; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ description: (props: StyleProps) => ({ height: props.height === 'large' ? '10rem' : '4rem', WebkitBackgroundClip: 'text', diff --git a/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx b/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx index d29211dc44..2ce7f623d1 100644 --- a/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx +++ b/plugins/bazaar/src/components/SortMethodSelector/SortMethodSelector.tsx @@ -50,7 +50,8 @@ export const SortMethodSelector = ({ > Latest updated A-Z - Most members + Z-A + Most members ); diff --git a/plugins/bazaar/src/components/SortView/SortView.tsx b/plugins/bazaar/src/components/SortView/SortView.tsx index 46f4c64c42..aa310ba55d 100644 --- a/plugins/bazaar/src/components/SortView/SortView.tsx +++ b/plugins/bazaar/src/components/SortView/SortView.tsx @@ -27,7 +27,12 @@ import { BazaarProject } from '../../types'; import { bazaarApiRef } from '../../api'; import { Alert } from '@material-ui/lab'; import SearchBar from 'material-ui-search-bar'; -import { sortByDate, sortByMembers, sortByTitle } from '../../util/sortMethods'; +import { + sortByDate, + sortByMembers, + sortByTitle, + sortByTitleDescending, +} from '../../util/sortMethods'; import { SortMethodSelector } from '../SortMethodSelector'; import { fetchCatalogItems } from '../../util/fetchMethods'; import { parseBazaarProject } from '../../util/parseMethods'; @@ -78,7 +83,12 @@ export const SortView = (props: SortViewProps) => { const bazaarApi = useApi(bazaarApiRef); const catalogApi = useApi(catalogApiRef); const classes = useStyles(); - const sortMethods = [sortByDate, sortByTitle, sortByMembers]; + const sortMethods = [ + sortByDate, + sortByTitle, + sortByTitleDescending, + sortByMembers, + ]; const [sortMethodNbr, setSortMethodNbr] = useState(0); const [openAdd, setOpenAdd] = useState(false); const [searchValue, setSearchValue] = useState(''); diff --git a/plugins/bazaar/src/util/sortMethods.ts b/plugins/bazaar/src/util/sortMethods.ts index d7ef71c8de..2a9b7969b3 100644 --- a/plugins/bazaar/src/util/sortMethods.ts +++ b/plugins/bazaar/src/util/sortMethods.ts @@ -27,12 +27,11 @@ export const sortByDate = (a: BazaarProject, b: BazaarProject): number => { }; export const sortByTitle = (a: BazaarProject, b: BazaarProject) => { - if (a.title < b.title) { - return -1; - } else if (a.title > b.title) { - return 1; - } - return 0; + return a.title.localeCompare(b.title); +}; + +export const sortByTitleDescending = (a: BazaarProject, b: BazaarProject) => { + return b.title.localeCompare(a.title); }; export const sortByMembers = (a: BazaarProject, b: BazaarProject) => { diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index 175d1c20b0..871a6a718c 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-bitrise +## 0.1.53-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.52 ### Patch Changes diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 023472669b..2c2e577ec9 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-bitrise", "description": "A Backstage plugin that integrates towards Bitrise", - "version": "0.1.52", + "version": "0.1.53-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-aws/CHANGELOG.md b/plugins/catalog-backend-module-aws/CHANGELOG.md index 26971186d6..4cbd9e3c94 100644 --- a/plugins/catalog-backend-module-aws/CHANGELOG.md +++ b/plugins/catalog-backend-module-aws/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-catalog-backend-module-aws +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-aws/package.json b/plugins/catalog-backend-module-aws/package.json index a8999430fa..f51fd3f8c2 100644 --- a/plugins/catalog-backend-module-aws/package.json +++ b/plugins/catalog-backend-module-aws/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-aws", "description": "A Backstage catalog backend module that helps integrate towards AWS", - "version": "0.3.0", + "version": "0.3.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-azure/CHANGELOG.md b/plugins/catalog-backend-module-azure/CHANGELOG.md index 315d39baab..a1b7d52763 100644 --- a/plugins/catalog-backend-module-azure/CHANGELOG.md +++ b/plugins/catalog-backend-module-azure/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-azure +## 0.1.26-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.25 ### Patch Changes diff --git a/plugins/catalog-backend-module-azure/package.json b/plugins/catalog-backend-module-azure/package.json index ef21110f7b..9c49fb839a 100644 --- a/plugins/catalog-backend-module-azure/package.json +++ b/plugins/catalog-backend-module-azure/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-azure", "description": "A Backstage catalog backend module that helps integrate towards Azure", - "version": "0.1.25", + "version": "0.1.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-backstage-openapi/.eslintrc.js b/plugins/catalog-backend-module-backstage-openapi/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md new file mode 100644 index 0000000000..b2ee0a4d14 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/CHANGELOG.md @@ -0,0 +1,19 @@ +# @backstage/plugin-catalog-backend-module-backstage-openapi + +## 0.1.0-next.0 + +### Minor Changes + +- 785fb1ea75: Adds a new catalog module for ingesting Backstage plugin OpenAPI specs into the catalog for display as an API entity. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 diff --git a/plugins/catalog-backend-module-backstage-openapi/README.md b/plugins/catalog-backend-module-backstage-openapi/README.md new file mode 100644 index 0000000000..a7f4727669 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/README.md @@ -0,0 +1,33 @@ +# catalog-backend-module-backstage-openapi + +## Summary + +This module installs an entity provider that exports a single entity, your Backstage instance documentation, which merges as many backend plugins as you have defined in the config value `catalog.providers.openapi.plugins`. + +## Notes + +- **This only works with the new backend system.** + +## Installation + +To your new backend file, add + +```ts title="packages/backend/src/index.ts" +backend.add( + import('@backstage/plugin-catalog-backend-module-backstage-openapi'), +); +``` + +Add a list of plugins to your config like, + +```yaml title="app-config.yaml" +catalog: + providers: + openapi: + plugins: + - catalog + - todo + - search +``` + +We will attempt to load each plugin's OpenAPI spec hosted at `${pluginRoute}/openapi.json`. These are automatically added if you are using `@backstage/backend-openapi-utils`'s `createValidatedOpenApiRouter`. diff --git a/plugins/catalog-backend-module-backstage-openapi/api-report.md b/plugins/catalog-backend-module-backstage-openapi/api-report.md new file mode 100644 index 0000000000..d417be049c --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/api-report.md @@ -0,0 +1,22 @@ +## API Report File for "@backstage/plugin-catalog-backend-module-backstage-openapi" + +> 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 (undocumented) +const catalogModuleInternalOpenApiSpec: () => BackendFeature; +export { catalogModuleInternalOpenApiSpec }; +export default catalogModuleInternalOpenApiSpec; + +// @public (undocumented) +export type MetaApiDocsPluginOptions = { + exampleOption: boolean; +}; + +// @public (undocumented) +export const metaOpenApiDocsPluginId = 'meta-api-docs'; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/catalog-backend-module-backstage-openapi/catalog-info.yaml b/plugins/catalog-backend-module-backstage-openapi/catalog-info.yaml new file mode 100644 index 0000000000..5e98f11506 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/catalog-info.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-catalog-backend-module-backstage-openapi + title: '@backstage/plugin-catalog-backend-module-backstage-openapi' +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: openapi-tooling-maintainers diff --git a/plugins/catalog-backend-module-backstage-openapi/config.d.ts b/plugins/catalog-backend-module-backstage-openapi/config.d.ts new file mode 100644 index 0000000000..ce51640495 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/config.d.ts @@ -0,0 +1,31 @@ +/* + * 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. + */ + +export interface Config { + catalog?: { + providers?: { + /** + * BackstageOpenApiEntityProvider configuration + */ + backstageOpenapi?: { + /** + * A list of plugins, whose OpenAPI specs you want to collate in `InternalOpenApiDocumentationProvider`. + */ + plugins: string[]; + }; + }; + }; +} diff --git a/plugins/catalog-backend-module-backstage-openapi/package.json b/plugins/catalog-backend-module-backstage-openapi/package.json new file mode 100644 index 0000000000..eaed841623 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/package.json @@ -0,0 +1,55 @@ +{ + "name": "@backstage/plugin-catalog-backend-module-backstage-openapi", + "version": "0.1.0-next.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-openapi-utils": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/backend-tasks": "workspace:^", + "@backstage/catalog-model": "workspace:^", + "@backstage/config": "workspace:^", + "@backstage/errors": "workspace:^", + "@backstage/plugin-catalog-node": "workspace:^", + "cross-fetch": "^3.1.5", + "express": "^4.17.1", + "express-promise-router": "^4.1.0", + "lodash": "^4.17.21", + "openapi-merge": "^1.3.2", + "uuid": "^9.0.0", + "yn": "^4.0.0" + }, + "devDependencies": { + "@backstage/cli": "workspace:^", + "@backstage/test-utils": "workspace:^", + "@types/express": "*", + "@types/supertest": "^2.0.8", + "msw": "^1.0.0", + "openapi3-ts": "^3.1.2", + "supertest": "^6.2.4" + }, + "configSchema": "config.d.ts", + "files": [ + "dist", + "config.d.ts" + ] +} diff --git a/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts b/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts new file mode 100644 index 0000000000..33c461d95a --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/src/InternalOpenApiDocumentationProvider.ts @@ -0,0 +1,250 @@ +/* + * 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 { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, + type ApiEntity, +} from '@backstage/catalog-model'; +import { Config } from '@backstage/config'; +import { ForwardedError } from '@backstage/errors'; +import { + EntityProvider, + EntityProviderConnection, +} from '@backstage/plugin-catalog-node'; +import { merge, isErrorResult } from 'openapi-merge'; +import { getOpenApiSpecRoute } from '@backstage/backend-openapi-utils'; +import type { + OpenAPIObject, + OperationObject, + PathItemObject, +} from 'openapi3-ts'; +import fetch from 'cross-fetch'; +import { DiscoveryService, LoggerService } from '@backstage/backend-plugin-api'; +import * as uuid from 'uuid'; +import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; + +const HTTP_VERBS: (keyof PathItemObject)[] = [ + 'get', + 'post', + 'put', + 'delete', + 'patch', + 'trace', + 'options', + 'head', +]; + +const addTagsToSpec = (spec: OpenAPIObject, tag: string) => { + Object.values(spec?.paths).forEach((path: PathItemObject) => { + HTTP_VERBS.forEach(verb => { + if (verb in path) { + if (!('tags' in path[verb])) { + (path[verb] as OperationObject).tags = []; + } + if (!(path[verb] as OperationObject).tags?.includes(tag)) { + (path[verb] as OperationObject).tags?.push(tag); + } + } + }); + }); +}; + +const mergeSpecs = async ({ + baseUrl, + specs, +}: { + baseUrl: string; + specs: OpenAPIObject[]; +}) => { + const mergeResult = merge([ + // Add the full API information as the first item for other items to merge against it with. + { + oas: { + openapi: '3.0.3', + info: { + title: 'Backstage API', + version: '1', + }, + servers: [{ url: baseUrl }], + paths: {}, + }, + }, + // For each plugin, load its spec and the known endpoint that it sits under. + ...specs.map( + spec => + ({ + oas: spec, + // Weird typing differences between this package and the client package's openapi 3. + } as any), + ), + ]); + + if (isErrorResult(mergeResult)) { + throw new ForwardedError( + `${mergeResult.message} (${mergeResult.type})`, + mergeResult, + ); + } else { + return mergeResult.output; + } +}; + +const loadSpecs = async ({ + baseUrl, + discovery, + plugins, + logger, +}: { + baseUrl: string; + plugins: string[]; + discovery: DiscoveryService; + logger: LoggerService; +}) => { + const specs: OpenAPIObject[] = []; + for (const pluginId of plugins) { + const url = await discovery.getExternalBaseUrl(pluginId); + const openApiUrl = getOpenApiSpecRoute(url); + const response = await fetch(openApiUrl); + if (response.ok) { + const spec = await response.json(); + addTagsToSpec(spec, pluginId); + specs.push(spec); + } else if (response.status === 404) { + logger.error( + `Plugin=${pluginId} does not have an OpenAPI spec at '${openApiUrl}'.`, + ); + } else { + logger.error( + `Failed to load spec for plugin=${pluginId} at ${openApiUrl}. Error (${ + response.status + }): ${response.body ? await response.text() : response.statusText}`, + ); + } + } + return mergeSpecs({ baseUrl, specs }); +}; + +export class InternalOpenApiDocumentationProvider implements EntityProvider { + private connection?: EntityProviderConnection; + private readonly scheduleFn: () => Promise; + constructor( + public readonly config: Config, + public readonly discovery: DiscoveryService, + public readonly logger: LoggerService, + taskRunner: TaskRunner, + ) { + this.scheduleFn = this.createScheduleFn(taskRunner); + } + + static fromConfig( + config: Config, + options: { + discovery: DiscoveryService; + logger: LoggerService; + schedule: PluginTaskScheduler; + }, + ) { + const taskRunner = options.schedule.createScheduledTaskRunner({ + frequency: { + minutes: 1, + }, + timeout: { + minutes: 1, + }, + }); + return new InternalOpenApiDocumentationProvider( + config, + options.discovery, + options.logger, + taskRunner, + ); + } + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.getProviderName} */ + getProviderName() { + return `InternalOpenApiDocumentationProvider`; + } + + /** {@inheritdoc @backstage/plugin-catalog-backend#EntityProvider.connect} */ + async connect(connection: EntityProviderConnection) { + this.connection = connection; + return await this.scheduleFn(); + } + + private createScheduleFn(taskRunner: TaskRunner): () => Promise { + return async () => { + const taskId = `${this.getProviderName()}:refresh`; + return taskRunner.run({ + id: taskId, + fn: async () => { + const logger = this.logger.child({ + class: + InternalOpenApiDocumentationProvider.prototype.constructor.name, + taskId, + taskInstanceId: uuid.v4(), + }); + try { + await this.refresh(logger); + } catch (error) { + logger.error(`${this.getProviderName()} refresh failed`, error); + } + }, + }); + }; + } + + async refresh(logger: LoggerService) { + const pluginsToMerge = this.config.getStringArray( + 'catalog.providers.backstageOpenapi.plugins', + ); + logger.info(`Loading specs from from ${pluginsToMerge}.`); + const documentationEntity: ApiEntity = { + apiVersion: 'backstage.io/v1beta1', + kind: 'API', + metadata: { + name: 'INTERNAL_instance_openapi_doc', + title: 'Your Backstage Instance documentation', + annotations: { + [ANNOTATION_LOCATION]: + 'internal-package:@backstage/plugin-catalog-backend-module-backstage-openapi', + [ANNOTATION_ORIGIN_LOCATION]: + 'internal-package:@backstage/plugin-catalog-backend-module-backstage-openapi', + }, + }, + spec: { + type: 'openapi', + lifecycle: 'production', + owner: 'backstage', + definition: JSON.stringify( + await loadSpecs({ + baseUrl: this.config.getString('backend.baseUrl'), + discovery: this.discovery, + plugins: pluginsToMerge, + logger, + }), + ), + }, + }; + await this.connection?.applyMutation({ + type: 'full', + entities: [ + { + entity: documentationEntity, + locationKey: 'internal-api-doc', + }, + ], + }); + } +} diff --git a/plugins/catalog-backend-module-backstage-openapi/src/index.ts b/plugins/catalog-backend-module-backstage-openapi/src/index.ts new file mode 100644 index 0000000000..226442666c --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/src/index.ts @@ -0,0 +1,61 @@ +/* + * 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 { + coreServices, + createBackendModule, +} from '@backstage/backend-plugin-api'; +import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; +import { InternalOpenApiDocumentationProvider } from './InternalOpenApiDocumentationProvider'; + +/** + * @public + */ +export type MetaApiDocsPluginOptions = { exampleOption: boolean }; + +/** + * @public + */ +export const metaOpenApiDocsPluginId = 'meta-api-docs'; + +/** + * @public + */ +export const catalogModuleInternalOpenApiSpec = createBackendModule({ + moduleId: metaOpenApiDocsPluginId, + pluginId: 'catalog', + register(env) { + env.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + config: coreServices.rootConfig, + discovery: coreServices.discovery, + scheduler: coreServices.scheduler, + logger: coreServices.logger, + }, + async init({ catalog, config, discovery, scheduler, logger }) { + catalog.addEntityProvider( + InternalOpenApiDocumentationProvider.fromConfig(config, { + discovery, + schedule: scheduler, + logger, + }), + ); + }, + }); + }, +}); + +export default catalogModuleInternalOpenApiSpec; diff --git a/plugins/catalog-backend-module-backstage-openapi/src/setupTests.ts b/plugins/catalog-backend-module-backstage-openapi/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/catalog-backend-module-backstage-openapi/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export {}; diff --git a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md index 7682992e01..67930259fc 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-bitbucket-cloud +## 0.1.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.13 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.21 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-cloud/package.json b/plugins/catalog-backend-module-bitbucket-cloud/package.json index ed10c8433e..deda20c641 100644 --- a/plugins/catalog-backend-module-bitbucket-cloud/package.json +++ b/plugins/catalog-backend-module-bitbucket-cloud/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket-cloud", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket Cloud", - "version": "0.1.21", + "version": "0.1.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md index 67ea880c96..3b6e9a9a26 100644 --- a/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket-server/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket-server +## 0.1.20-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.19 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket-server/package.json b/plugins/catalog-backend-module-bitbucket-server/package.json index e0f395a1a2..249ee24660 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.1.19", + "version": "0.1.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md index cf7e871bc5..cdf8e53426 100644 --- a/plugins/catalog-backend-module-bitbucket/CHANGELOG.md +++ b/plugins/catalog-backend-module-bitbucket/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-bitbucket +## 0.2.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-bitbucket-cloud-common@0.2.13 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.2.21 ### Patch Changes diff --git a/plugins/catalog-backend-module-bitbucket/package.json b/plugins/catalog-backend-module-bitbucket/package.json index a68cb1e674..8efed95068 100644 --- a/plugins/catalog-backend-module-bitbucket/package.json +++ b/plugins/catalog-backend-module-bitbucket/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-bitbucket", "description": "A Backstage catalog backend module that helps integrate towards Bitbucket", - "version": "0.2.21", + "version": "0.2.22-next.0", "deprecated": true, "main": "src/index.ts", "types": "src/index.ts", diff --git a/plugins/catalog-backend-module-gcp/CHANGELOG.md b/plugins/catalog-backend-module-gcp/CHANGELOG.md index c755f7a72b..9240006161 100644 --- a/plugins/catalog-backend-module-gcp/CHANGELOG.md +++ b/plugins/catalog-backend-module-gcp/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-gcp +## 0.1.7-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.1.6 ### Patch Changes diff --git a/plugins/catalog-backend-module-gcp/package.json b/plugins/catalog-backend-module-gcp/package.json index 70bf6f7b88..678c8e1075 100644 --- a/plugins/catalog-backend-module-gcp/package.json +++ b/plugins/catalog-backend-module-gcp/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gcp", "description": "A Backstage catalog backend module that helps integrate towards GCP", - "version": "0.1.6", + "version": "0.1.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gerrit/CHANGELOG.md b/plugins/catalog-backend-module-gerrit/CHANGELOG.md index e55d30ed12..280215731b 100644 --- a/plugins/catalog-backend-module-gerrit/CHANGELOG.md +++ b/plugins/catalog-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gerrit +## 0.1.23-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.22 ### Patch Changes diff --git a/plugins/catalog-backend-module-gerrit/package.json b/plugins/catalog-backend-module-gerrit/package.json index 576f772968..7e8b248573 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.1.22", + "version": "0.1.23-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github-org/CHANGELOG.md b/plugins/catalog-backend-module-github-org/CHANGELOG.md index e0a24889c4..939233c8db 100644 --- a/plugins/catalog-backend-module-github-org/CHANGELOG.md +++ b/plugins/catalog-backend-module-github-org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-github-org +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-backend-module-github@0.4.5-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-github-org/package.json b/plugins/catalog-backend-module-github-org/package.json index 7ad96c65e2..6b91be826a 100644 --- a/plugins/catalog-backend-module-github-org/package.json +++ b/plugins/catalog-backend-module-github-org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github-org", "description": "The github-org backend module for the catalog plugin.", - "version": "0.1.0", + "version": "0.1.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/CHANGELOG.md b/plugins/catalog-backend-module-github/CHANGELOG.md index 21e8ad0e1b..e5fb524cb3 100644 --- a/plugins/catalog-backend-module-github/CHANGELOG.md +++ b/plugins/catalog-backend-module-github/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-catalog-backend-module-github +## 0.4.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.4.4 ### Patch Changes diff --git a/plugins/catalog-backend-module-github/api-report.md b/plugins/catalog-backend-module-github/api-report.md index 4c44f95b61..eb43748aa1 100644 --- a/plugins/catalog-backend-module-github/api-report.md +++ b/plugins/catalog-backend-module-github/api-report.md @@ -3,7 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { AnalyzeOptions } from '@backstage/plugin-catalog-backend'; +import { AnalyzeOptions } from '@backstage/plugin-catalog-node'; import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-node'; import { Config } from '@backstage/config'; @@ -21,7 +21,7 @@ import { Logger } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { ScmIntegrationRegistry } from '@backstage/integration'; -import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-backend'; +import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node'; import { TaskRunner } from '@backstage/backend-tasks'; import { TokenManager } from '@backstage/backend-common'; import { UserEntity } from '@backstage/catalog-model'; diff --git a/plugins/catalog-backend-module-github/package.json b/plugins/catalog-backend-module-github/package.json index f2c321f84a..ba51c49eff 100644 --- a/plugins/catalog-backend-module-github/package.json +++ b/plugins/catalog-backend-module-github/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-github", "description": "A Backstage catalog backend module that helps integrate towards GitHub", - "version": "0.4.4", + "version": "0.4.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts index 9341743413..3353254400 100644 --- a/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts +++ b/plugins/catalog-backend-module-github/src/analyzers/GithubLocationAnalyzer.ts @@ -27,7 +27,7 @@ import parseGitUrl from 'git-url-parse'; import { AnalyzeOptions, ScmLocationAnalyzer, -} from '@backstage/plugin-catalog-backend'; +} from '@backstage/plugin-catalog-node'; import { PluginEndpointDiscovery, TokenManager, diff --git a/plugins/catalog-backend-module-gitlab/CHANGELOG.md b/plugins/catalog-backend-module-gitlab/CHANGELOG.md index 5378ba5448..a48379e18d 100644 --- a/plugins/catalog-backend-module-gitlab/CHANGELOG.md +++ b/plugins/catalog-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-gitlab +## 0.3.4-next.0 + +### Patch Changes + +- d732f17610: Added try catch around fetching gitlab group users to prevent refresh from failing completely while only a select number of groups might not be able to load correctly. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-gitlab/package.json b/plugins/catalog-backend-module-gitlab/package.json index 2c5966bb68..abf9797f7f 100644 --- a/plugins/catalog-backend-module-gitlab/package.json +++ b/plugins/catalog-backend-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-gitlab", "description": "A Backstage catalog backend module that helps integrate towards GitLab", - "version": "0.3.3", + "version": "0.3.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-gitlab/src/lib/client.ts b/plugins/catalog-backend-module-gitlab/src/lib/client.ts index 722f079a21..c4cdd444cb 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/client.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/client.ts @@ -13,18 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import fetch from 'node-fetch'; import { getGitLabRequestOptions, GitLabIntegrationConfig, } from '@backstage/integration'; +import fetch from 'node-fetch'; import { Logger } from 'winston'; + import { - GitLabGroup, GitLabDescendantGroupsResponse, + GitLabGroup, GitLabGroupMembersResponse, GitLabUser, + PagedResponse, } from './types'; export type CommonListOptions = { @@ -44,11 +45,6 @@ interface UserListOptions extends CommonListOptions { exclude_internal?: boolean | undefined; } -export type PagedResponse = { - items: T[]; - nextPage?: number; -}; - export class GitLabClient { private readonly config: GitLabIntegrationConfig; private readonly logger: Logger; diff --git a/plugins/catalog-backend-module-gitlab/src/lib/types.ts b/plugins/catalog-backend-module-gitlab/src/lib/types.ts index 946317a11a..fa2eb73da6 100644 --- a/plugins/catalog-backend-module-gitlab/src/lib/types.ts +++ b/plugins/catalog-backend-module-gitlab/src/lib/types.ts @@ -13,9 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { TaskScheduleDefinition } from '@backstage/backend-tasks'; +export type PagedResponse = { + items: T[]; + nextPage?: number; +}; + export type GitlabGroupDescription = { id: number; web_url: string; diff --git a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts index 732f9f9fac..affa8a41cf 100644 --- a/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts +++ b/plugins/catalog-backend-module-gitlab/src/providers/GitlabOrgDiscoveryEntityProvider.ts @@ -13,31 +13,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import { PluginTaskScheduler, TaskRunner } from '@backstage/backend-tasks'; +import { + ANNOTATION_LOCATION, + ANNOTATION_ORIGIN_LOCATION, + Entity, + GroupEntity, + UserEntity, +} from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { GitLabIntegration, ScmIntegrations } from '@backstage/integration'; import { EntityProvider, EntityProviderConnection, } from '@backstage/plugin-catalog-node'; +import { merge } from 'lodash'; import * as uuid from 'uuid'; import { Logger } from 'winston'; + import { GitLabClient, GitlabProviderConfig, paginated, readGitlabConfigs, } from '../lib'; -import { GitLabGroup, GitLabUser } from '../lib/types'; -import { - ANNOTATION_LOCATION, - ANNOTATION_ORIGIN_LOCATION, - Entity, - UserEntity, - GroupEntity, -} from '@backstage/catalog-model'; -import { merge } from 'lodash'; +import { GitLabGroup, GitLabUser, PagedResponse } from '../lib/types'; type Result = { scanned: number; @@ -245,9 +245,14 @@ export class GitlabOrgDiscoveryEntityProvider implements EntityProvider { groupRes.scanned++; groupRes.matches.push(group); - const groupUsers = await client.getGroupMembers(group.full_path, [ - 'DIRECT', - ]); + let groupUsers: PagedResponse = { items: [] }; + try { + groupUsers = await client.getGroupMembers(group.full_path, ['DIRECT']); + } catch (e) { + logger.error( + `Failed fetching users for group '${group.full_path}': ${e}`, + ); + } for (const groupUser of groupUsers.items) { const user = idMappedUser[groupUser.id]; diff --git a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md index 58e19beae8..20f2abd655 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md +++ b/plugins/catalog-backend-module-incremental-ingestion/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-catalog-backend-module-incremental-ingestion +## 0.4.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + ## 0.4.10 ### Patch Changes diff --git a/plugins/catalog-backend-module-incremental-ingestion/package.json b/plugins/catalog-backend-module-incremental-ingestion/package.json index a0df7f5449..ef2a876f5f 100644 --- a/plugins/catalog-backend-module-incremental-ingestion/package.json +++ b/plugins/catalog-backend-module-incremental-ingestion/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-incremental-ingestion", "description": "An entity provider for streaming large asset sources into the catalog", - "version": "0.4.10", + "version": "0.4.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index f9d38ee033..061536cc9d 100644 --- a/plugins/catalog-backend-module-ldap/CHANGELOG.md +++ b/plugins/catalog-backend-module-ldap/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-ldap +## 0.5.22-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.5.21 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 36c1e6c46f..819ad2a81c 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", "description": "A Backstage catalog backend module that helps integrate towards LDAP", - "version": "0.5.21", + "version": "0.5.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 3cb7b30b3d..acd364a94c 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.5.14-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.5.13 ### Patch Changes diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 91459e847e..7802f5a50e 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", "description": "A Backstage catalog backend module that helps integrate towards Microsoft Graph", - "version": "0.5.13", + "version": "0.5.14-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-openapi/CHANGELOG.md b/plugins/catalog-backend-module-openapi/CHANGELOG.md index 65b55833ca..7e2651b957 100644 --- a/plugins/catalog-backend-module-openapi/CHANGELOG.md +++ b/plugins/catalog-backend-module-openapi/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-openapi +## 0.1.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.23 ### Patch Changes diff --git a/plugins/catalog-backend-module-openapi/package.json b/plugins/catalog-backend-module-openapi/package.json index 1f85d0d844..e96649ef95 100644 --- a/plugins/catalog-backend-module-openapi/package.json +++ b/plugins/catalog-backend-module-openapi/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-openapi", "description": "A Backstage catalog backend module that helps with OpenAPI specifications", - "version": "0.1.23", + "version": "0.1.24-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md index 7ce2b70e20..0e1f1fdccd 100644 --- a/plugins/catalog-backend-module-puppetdb/CHANGELOG.md +++ b/plugins/catalog-backend-module-puppetdb/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-backend-module-puppetdb +## 0.1.12-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.11 ### Patch Changes diff --git a/plugins/catalog-backend-module-puppetdb/package.json b/plugins/catalog-backend-module-puppetdb/package.json index db267bc22e..ffe9195fa4 100644 --- a/plugins/catalog-backend-module-puppetdb/package.json +++ b/plugins/catalog-backend-module-puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-puppetdb", "description": "A Backstage catalog backend module that helps integrate towards PuppetDB", - "version": "0.1.11", + "version": "0.1.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md index b077261f25..63b52026af 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md +++ b/plugins/catalog-backend-module-scaffolder-entity-model/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-backend-module-scaffolder-entity-model +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.1.3 ### 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 f20a772a30..ca87a07d1d 100644 --- a/plugins/catalog-backend-module-scaffolder-entity-model/package.json +++ b/plugins/catalog-backend-module-scaffolder-entity-model/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-scaffolder-entity-model", "description": "Adds support for the scaffolder specific entity model (e.g. the Template kind) to the catalog backend plugin.", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md index 310f3b9b5f..a911b911eb 100644 --- a/plugins/catalog-backend-module-unprocessed/CHANGELOG.md +++ b/plugins/catalog-backend-module-unprocessed/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-catalog-backend-module-unprocessed +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.3.3 ### Patch Changes diff --git a/plugins/catalog-backend-module-unprocessed/package.json b/plugins/catalog-backend-module-unprocessed/package.json index 5adc6e1f5f..ba1080d02b 100644 --- a/plugins/catalog-backend-module-unprocessed/package.json +++ b/plugins/catalog-backend-module-unprocessed/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend-module-unprocessed", "description": "Backstage Catalog module to view unprocessed entities", - "version": "0.3.3", + "version": "0.3.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index f6dee7b12f..0775bb6a1f 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,44 @@ # @backstage/plugin-catalog-backend +## 1.15.0-next.0 + +### Minor Changes + +- 8d756968f9: Introduce a new optional config parameter `catalog.stitchingStrategy.mode`, + which can have the values `'immediate'` (default) and `'deferred'`. The default + is for stitching to work as it did before this change, which means that it + happens "in-band" (blocking) immediately when each processing task finishes. + When set to `'deferred'`, stitching is instead deferred to happen on a separate + asynchronous worker queue just like processing. + + Deferred stitching should make performance smoother when ingesting large amounts + of entities, and reduce p99 processing times and repeated over-stitching of + hot spot entities when fan-out/fan-in in terms of relations is very large. It + does however also come with some performance cost due to the queuing with how + much wall-clock time some types of task take. + +### Patch Changes + +- 6694b369a3: Update the OpenAPI spec with more complete error responses and request bodies using Optic. Also, updates the test cases to use the new `supertest` pass through from `@backstage/backend-openapi-utils`. +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-backend-module-catalog@0.1.11-next.0 + ## 1.14.0 ### Minor Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index e426ca1d75..1aed0ea8b0 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -10,6 +10,7 @@ import { AnalyzeLocationExistingEntity as AnalyzeLocationExistingEntity_2 } from import { AnalyzeLocationGenerateEntity as AnalyzeLocationGenerateEntity_2 } from '@backstage/plugin-catalog-common'; import { AnalyzeLocationRequest as AnalyzeLocationRequest_2 } from '@backstage/plugin-catalog-common'; import { AnalyzeLocationResponse as AnalyzeLocationResponse_2 } from '@backstage/plugin-catalog-common'; +import { AnalyzeOptions as AnalyzeOptions_2 } from '@backstage/plugin-catalog-node'; import { CatalogApi } from '@backstage/catalog-client'; import type { CatalogCollatorEntityTransformer as CatalogCollatorEntityTransformer_2 } from '@backstage/plugin-search-backend-module-catalog'; import { CatalogEntityDocument } from '@backstage/plugin-catalog-common'; @@ -50,6 +51,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Router } from 'express'; import { ScmIntegrationRegistry } from '@backstage/integration'; +import { ScmLocationAnalyzer as ScmLocationAnalyzer_2 } from '@backstage/plugin-catalog-node'; import { TokenManager } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; import { Validators } from '@backstage/catalog-model'; @@ -69,11 +71,8 @@ export type AnalyzeLocationRequest = AnalyzeLocationRequest_2; // @public @deprecated (undocumented) export type AnalyzeLocationResponse = AnalyzeLocationResponse_2; -// @public (undocumented) -export type AnalyzeOptions = { - url: string; - catalogFilename?: string; -}; +// @public @deprecated (undocumented) +export type AnalyzeOptions = AnalyzeOptions_2; // @public (undocumented) export class AnnotateLocationEntityProcessor implements CatalogProcessor_2 { @@ -134,7 +133,7 @@ export class CatalogBuilder { ...providers: Array> ): CatalogBuilder; addLocationAnalyzers( - ...analyzers: Array> + ...analyzers: Array> ): CatalogBuilder; addPermissionRules( ...permissionRules: Array< @@ -468,13 +467,8 @@ export const processingResult: Readonly<{ readonly refresh: (key: string) => CatalogProcessorResult_2; }>; -// @public (undocumented) -export type ScmLocationAnalyzer = { - supports(url: string): boolean; - analyze(options: AnalyzeOptions): Promise<{ - existing: AnalyzeLocationExistingEntity[]; - }>; -}; +// @public @deprecated (undocumented) +export type ScmLocationAnalyzer = ScmLocationAnalyzer_2; // @public (undocumented) export class UrlReaderProcessor implements CatalogProcessor_2 { diff --git a/plugins/catalog-backend/config.d.ts b/plugins/catalog-backend/config.d.ts index 4b9f606043..265d78b496 100644 --- a/plugins/catalog-backend/config.d.ts +++ b/plugins/catalog-backend/config.d.ts @@ -145,6 +145,19 @@ export interface Config { */ orphanStrategy?: 'keep' | 'delete'; + /** + * The strategy to use when stitching together the final entities. + */ + stitchingStrategy?: + | { + /** Perform stitching in-band immediately when needed */ + mode: 'immediate'; + } + | { + /** Defer stitching to be performed asynchronously */ + mode: 'deferred'; + }; + /** * The interval at which the catalog should process its entities. * diff --git a/plugins/catalog-backend/migrations/20230525141717_stitch_queue.js b/plugins/catalog-backend/migrations/20230525141717_stitch_queue.js new file mode 100644 index 0000000000..f4344a290d --- /dev/null +++ b/plugins/catalog-backend/migrations/20230525141717_stitch_queue.js @@ -0,0 +1,85 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// @ts-check + +/** + * @param { import("knex").Knex } knex + */ +exports.up = async function up(knex) { + await knex.schema.alterTable('refresh_state', table => { + table + .dateTime('next_stitch_at') + .nullable() + .comment('Timestamp of when entity should be stitched'); + table + .string('next_stitch_ticket') + .nullable() + .comment('Random value distinguishing stitch requests'); + table.index('next_stitch_at', 'refresh_state_next_stitch_at_idx', { + predicate: knex.whereNotNull('next_stitch_at'), + }); + }); + + // Look for things that are due for stitching, and move them over to the new + // system. An explanation on the length based ones: Before adding the new + // system, stitching was triggered by setting hashes to various hard coded + // strings - and we leverage the fact that those strings were always shorter + // than a real hash would be. There's also the case where we have not yet + // created the final_entities row corresponding to the refresh_state one. This + // is split into two statements because MySQL doesn't allow you to write to + // tables you're reading from. + const candidates = await knex + .select({ entityId: 'refresh_state.entity_id' }) + .from('refresh_state') + .leftOuterJoin( + 'final_entities', + 'final_entities.entity_id', + 'refresh_state.entity_id', + ) + // no final entities at all + .orWhereNull('final_entities.entity_id') + // the final entity hash was forcibly set to something short + .orWhere(knex.raw('LENGTH(??) < 15', ['final_entities.hash'])) + // there used to be an ongoing stitch (possibly unfinished and aborted) + .orWhere(knex.raw('LENGTH(??) > 0', ['final_entities.stitch_ticket'])) + // the processing output entity hash was forcibly set to something short + .orWhere(knex.raw('LENGTH(??) < 15', ['refresh_state.result_hash'])); + if (candidates.length) { + for (let i = 0; i < candidates.length; i += 1000) { + await knex('refresh_state') + .update({ + next_stitch_at: knex.fn.now(), + next_stitch_ticket: 'initial', + }) + .whereIn( + 'entity_id', + candidates.slice(i, i + 1000).map(c => c.entityId), + ); + } + } +}; + +/** + * @param { import("knex").Knex } knex + */ +exports.down = async function down(knex) { + await knex.schema.alterTable('refresh_state', table => { + table.dropIndex([], 'refresh_state_next_stitch_at_idx'); + table.dropColumn('next_stitch_at'); + table.dropColumn('next_stitch_ticket'); + }); +}; diff --git a/plugins/catalog-backend/optic.yml b/plugins/catalog-backend/optic.yml new file mode 100644 index 0000000000..8b0c050516 --- /dev/null +++ b/plugins/catalog-backend/optic.yml @@ -0,0 +1,15 @@ +ruleset: + - breaking-changes +capture: + src/schema/openapi.yaml: + # 🔧 Runnable example with simple get requests. + # Run with "PORT=3000 optic capture src/schema/openapi.yaml --update interactive" in 'plugins/catalog-backend' + # You can change the server and the 'requests' section to experiment + server: + # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates. + url: http://localhost:3000 + requests: + # â„šī¸ Requests should be sent to the Optic proxy, the address of which is injected into 'run.command's env as OPTIC_PROXY (or the value of 'run.proxy_variable'). + run: + # 🔧 Specify a command that will generate traffic + command: yarn backstage-cli package test --no-watch src/service/router.test.ts src/service/createRouter.test.ts diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 388f6b5ffc..8dc96c3cb7 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-backend", "description": "The Backstage backend plugin that provides the Backstage catalog", - "version": "1.14.0", + "version": "1.15.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.test.ts new file mode 100644 index 0000000000..a909515f14 --- /dev/null +++ b/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.test.ts @@ -0,0 +1,114 @@ +/* + * 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 { TestDatabases } from '@backstage/backend-test-utils'; +import { applyDatabaseMigrations } from '../../migrations'; +import { getDeferredStitchableEntities } from './getDeferredStitchableEntities'; + +jest.setTimeout(60_000); + +describe('getDeferredStitchableEntities', () => { + const databases = TestDatabases.create({ + ids: ['MYSQL_8', 'POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + + it.each(databases.eachSupportedId())( + 'selects the right rows %p', + async databaseId => { + const knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + + await knex + .insert([ + { + entity_id: '1', + entity_ref: 'k:ns/no_stitch_time', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: null, + next_stitch_ticket: null, + }, + { + entity_id: '2', + entity_ref: 'k:ns/future_stitch_time', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: '2037-01-01T00:00:00.000', + next_stitch_ticket: 't1', + }, + { + entity_id: '3', + entity_ref: 'k:ns/past_stitch_time', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: '1971-01-01T00:00:00.000', + next_stitch_ticket: 't3', + }, + { + entity_id: '4', + entity_ref: 'k:ns/past_stitch_time_again', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: '1972-01-01T00:00:00.000', + next_stitch_ticket: 't4', + }, + ]) + .into('refresh_state'); + + const rowsBefore = await knex('refresh_state'); + + const items = await getDeferredStitchableEntities({ + knex, + batchSize: 1, + stitchTimeout: { seconds: 2 }, + }); + + const rowsAfter = await knex('refresh_state'); + + expect(items).toEqual([ + { + entityRef: 'k:ns/past_stitch_time', + stitchTicket: 't3', + stitchRequestedAt: expect.anything(), + }, + ]); + + const hitRowBefore = rowsBefore.filter(r => r.entity_id === '3')[0] + .next_stitch_at; + const hitRowAfter = rowsAfter.filter(r => r.entity_id === '3')[0] + .next_stitch_at; + const missRowBefore = rowsBefore.filter(r => r.entity_id === '4')[0] + .next_stitch_at; + const missRowAfter = rowsAfter.filter(r => r.entity_id === '4')[0] + .next_stitch_at; + + expect(+new Date(hitRowAfter)).toBeGreaterThan(+new Date(hitRowBefore)); + expect(+new Date(missRowAfter)).toEqual(+new Date(missRowBefore)); + }, + ); +}); diff --git a/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.ts b/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.ts new file mode 100644 index 0000000000..05c3f4d2eb --- /dev/null +++ b/plugins/catalog-backend/src/database/operations/stitcher/getDeferredStitchableEntities.ts @@ -0,0 +1,105 @@ +/* + * 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 { durationToMilliseconds, HumanDuration } from '@backstage/types'; +import { Knex } from 'knex'; +import { DateTime } from 'luxon'; +import { timestampToDateTime } from '../../conversion'; +import { DbRefreshStateRow } from '../../tables'; + +// TODO(freben): There is no retry counter or similar. If items start +// perpetually crashing during stitching, they'll just get silently retried over +// and over again, for better or worse. This will be visible in metrics though. + +/** + * Finds entities that are marked for deferred stitching. + * + * @remarks + * + * This assumes that the stitching strategy is set to deferred. + * + * They are expected to already have the next_stitch_ticket set (by + * markForStitching) so that their tickets can be returned with each item. + * + * All returned items have their next_stitch_at updated to be moved forward by + * the given timeout duration. This has the effect that they will be picked up + * for stitching again in the future, if it hasn't completed by that point for + * some reason (restarts, crashes, etc). + */ +export async function getDeferredStitchableEntities(options: { + knex: Knex | Knex.Transaction; + batchSize: number; + stitchTimeout: HumanDuration; +}): Promise< + Array<{ + entityRef: string; + stitchTicket: string; + stitchRequestedAt: DateTime; // the time BEFORE moving it forward by the timeout + }> +> { + const { knex, batchSize, stitchTimeout } = options; + + let itemsQuery = knex('refresh_state').select( + 'entity_ref', + 'next_stitch_at', + 'next_stitch_ticket', + ); + + // This avoids duplication of work because of race conditions and is + // also fast because locked rows are ignored rather than blocking. + // It's only available in MySQL and PostgreSQL + if (['mysql', 'mysql2', 'pg'].includes(knex.client.config.client)) { + itemsQuery = itemsQuery.forUpdate().skipLocked(); + } + + const items = await itemsQuery + .whereNotNull('next_stitch_at') + .whereNotNull('next_stitch_ticket') + .where('next_stitch_at', '<=', knex.fn.now()) + .orderBy('next_stitch_at', 'asc') + .limit(batchSize); + + if (!items.length) { + return []; + } + + await knex('refresh_state') + .whereIn( + 'entity_ref', + items.map(i => i.entity_ref), + ) + // avoid race condition where someone completes a stitch right between these statements + .whereNotNull('next_stitch_ticket') + .update({ + next_stitch_at: nowPlus(knex, stitchTimeout), + }); + + return items.map(i => ({ + entityRef: i.entity_ref, + stitchTicket: i.next_stitch_ticket!, + stitchRequestedAt: timestampToDateTime(i.next_stitch_at!), + })); +} + +function nowPlus(knex: Knex, duration: HumanDuration): Knex.Raw { + const seconds = durationToMilliseconds(duration) / 1000; + if (knex.client.config.client.includes('sqlite3')) { + return knex.raw(`datetime('now', ?)`, [`${seconds} seconds`]); + } else if (knex.client.config.client.includes('mysql')) { + return knex.raw(`now() + interval ${seconds} second`); + } + return knex.raw(`now() + interval '${seconds} seconds'`); +} diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.test.ts new file mode 100644 index 0000000000..76a6169cf4 --- /dev/null +++ b/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.test.ts @@ -0,0 +1,75 @@ +/* + * 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 { TestDatabases } from '@backstage/backend-test-utils'; +import { applyDatabaseMigrations } from '../../migrations'; +import { markDeferredStitchCompleted } from './markDeferredStitchCompleted'; +import { DbRefreshStateRow } from '../../tables'; + +jest.setTimeout(60_000); + +describe('markDeferredStitchCompleted', () => { + const databases = TestDatabases.create({ + ids: ['MYSQL_8', 'POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], + }); + + it.each(databases.eachSupportedId())( + 'completes only if unchanged %p', + async databaseId => { + const knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + + await knex('refresh_state').insert([ + { + entity_id: '1', + entity_ref: 'k:ns/n', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: '1971-01-01T00:00:00.000', + next_stitch_ticket: 'the-ticket', + }, + ]); + + async function result() { + return knex('refresh_state').select( + 'next_stitch_at', + 'next_stitch_ticket', + ); + } + + await markDeferredStitchCompleted({ + knex, + entityRef: 'k:ns/n', + stitchTicket: 'the-wrong-ticket', + }); + await expect(result()).resolves.toEqual([ + { next_stitch_at: expect.anything(), next_stitch_ticket: 'the-ticket' }, + ]); + + await markDeferredStitchCompleted({ + knex, + entityRef: 'k:ns/n', + stitchTicket: 'the-ticket', + }); + await expect(result()).resolves.toEqual([ + { next_stitch_at: null, next_stitch_ticket: null }, + ]); + }, + ); +}); diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.ts b/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.ts new file mode 100644 index 0000000000..d1c7c3a6b8 --- /dev/null +++ b/plugins/catalog-backend/src/database/operations/stitcher/markDeferredStitchCompleted.ts @@ -0,0 +1,46 @@ +/* + * 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 { Knex } from 'knex'; +import { DbRefreshStateRow } from '../../tables'; + +/** + * Marks a single entity as having been stitched. + * + * @remarks + * + * This assumes that the stitching strategy is set to deferred. + * + * The timestamp and ticket are only reset if the ticket hasn't changed. If it + * has, it means that a new stitch request has been made, and the entity should + * be stitched once more some time in the future - or is indeed already being + * stitched concurrently with ourselves. + */ +export async function markDeferredStitchCompleted(option: { + knex: Knex | Knex.Transaction; + entityRef: string; + stitchTicket: string; +}): Promise { + const { knex, entityRef, stitchTicket } = option; + + await knex('refresh_state') + .update({ + next_stitch_at: null, + next_stitch_ticket: null, + }) + .where('entity_ref', '=', entityRef) + .andWhere('next_stitch_ticket', '=', stitchTicket); +} diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts index ce44fe15d8..2cae76040b 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.test.ts @@ -26,6 +26,182 @@ describe('markForStitching', () => { ids: ['MYSQL_8', 'POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], }); + it.each(databases.eachSupportedId())( + 'marks the right rows in deferred mode %p', + async databaseId => { + const knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + + await knex('refresh_state').insert([ + { + entity_id: '1', + entity_ref: 'k:ns/one', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: null, + next_stitch_ticket: null, + }, + { + entity_id: '2', + entity_ref: 'k:ns/two', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: null, + next_stitch_ticket: null, + }, + { + entity_id: '3', + entity_ref: 'k:ns/three', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: null, + next_stitch_ticket: null, + }, + { + entity_id: '4', + entity_ref: 'k:ns/four', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + next_stitch_at: '1971-01-01T00:00:00.000', + next_stitch_ticket: 'old', + }, + ]); + + async function result() { + return knex('refresh_state') + .select('entity_id', 'next_stitch_at', 'next_stitch_ticket') + .orderBy('entity_id', 'asc'); + } + + const original = await result(); + + await markForStitching({ + knex, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: new Set(), + }); + await expect(result()).resolves.toEqual([ + { entity_id: '1', next_stitch_at: null, next_stitch_ticket: null }, + { entity_id: '2', next_stitch_at: null, next_stitch_ticket: null }, + { entity_id: '3', next_stitch_at: null, next_stitch_ticket: null }, + { + entity_id: '4', + next_stitch_at: expect.anything(), + next_stitch_ticket: 'old', + }, + ]); + + await markForStitching({ + knex, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: new Set(['k:ns/one']), + }); + await expect(result()).resolves.toEqual([ + { + entity_id: '1', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { entity_id: '2', next_stitch_at: null, next_stitch_ticket: null }, + { entity_id: '3', next_stitch_at: null, next_stitch_ticket: null }, + { + entity_id: '4', + next_stitch_at: expect.anything(), + next_stitch_ticket: 'old', + }, + ]); + + await markForStitching({ + knex, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityRefs: ['k:ns/two'], + }); + await expect(result()).resolves.toEqual([ + { + entity_id: '1', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { + entity_id: '2', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { entity_id: '3', next_stitch_at: null, next_stitch_ticket: null }, + { + entity_id: '4', + next_stitch_at: expect.anything(), + next_stitch_ticket: 'old', + }, + ]); + + await markForStitching({ + knex, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, + entityIds: ['3', '4'], + }); + await expect(result()).resolves.toEqual([ + { + entity_id: '1', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { + entity_id: '2', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { + entity_id: '3', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + { + entity_id: '4', + next_stitch_at: expect.anything(), + next_stitch_ticket: expect.anything(), + }, + ]); + + // It overwrites timers and tickets if they existed before + const final = await result(); + for (let i = 0; i < final.length; ++i) { + expect(original[i].next_stitch_at).not.toEqual(final[i].next_stitch_at); + expect(original[i].next_stitch_ticket).not.toEqual( + final[i].next_stitch_ticket, + ); + } + }, + ); + it.each(databases.eachSupportedId())( 'marks the right rows in immediate mode %p', async databaseId => { diff --git a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts index 7ab9bda1c0..ecc364a9cc 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/markForStitching.ts @@ -16,11 +16,12 @@ import { Knex } from 'knex'; import splitToChunks from 'lodash/chunk'; +import { v4 as uuid } from 'uuid'; import { StitchingStrategy } from '../../../stitching/types'; import { DbFinalEntitiesRow, DbRefreshStateRow } from '../../tables'; /** - * Marks a number of entities for deferred stitching some time in the near + * Marks a number of entities for stitching some time in the near * future. * * @remarks @@ -35,42 +36,69 @@ export async function markForStitching(options: { const entityRefs = split(options.entityRefs); const entityIds = split(options.entityIds); const knex = options.knex; + const mode = options.strategy.mode; - for (const chunk of entityRefs) { - await knex - .table('final_entities') - .update({ - hash: 'force-stitching', - }) - .whereIn( - 'entity_id', - knex('refresh_state') - .select('entity_id') - .whereIn('entity_ref', chunk), - ); - await knex - .table('refresh_state') - .update({ - result_hash: 'force-stitching', - next_update_at: knex.fn.now(), - }) - .whereIn('entity_ref', chunk); - } + if (mode === 'immediate') { + for (const chunk of entityRefs) { + await knex + .table('final_entities') + .update({ + hash: 'force-stitching', + }) + .whereIn( + 'entity_id', + knex('refresh_state') + .select('entity_id') + .whereIn('entity_ref', chunk), + ); + await knex + .table('refresh_state') + .update({ + result_hash: 'force-stitching', + next_update_at: knex.fn.now(), + }) + .whereIn('entity_ref', chunk); + } - for (const chunk of entityIds) { - await knex - .table('final_entities') - .update({ - hash: 'force-stitching', - }) - .whereIn('entity_id', chunk); - await knex - .table('refresh_state') - .update({ - result_hash: 'force-stitching', - next_update_at: knex.fn.now(), - }) - .whereIn('entity_id', chunk); + for (const chunk of entityIds) { + await knex + .table('final_entities') + .update({ + hash: 'force-stitching', + }) + .whereIn('entity_id', chunk); + await knex + .table('refresh_state') + .update({ + result_hash: 'force-stitching', + next_update_at: knex.fn.now(), + }) + .whereIn('entity_id', chunk); + } + } else if (mode === 'deferred') { + // It's OK that this is shared across refresh state rows; it just needs to + // be uniquely generated for every new stitch request. + const ticket = uuid(); + + for (const chunk of entityRefs) { + await knex('refresh_state') + .update({ + next_stitch_at: knex.fn.now(), + next_stitch_ticket: ticket, + }) + .whereIn('entity_ref', chunk); + } + + for (const chunk of entityIds) { + await knex('refresh_state') + .update({ + next_stitch_at: knex.fn.now(), + next_stitch_ticket: ticket, + }) + .whereIn('entity_id', chunk); + } + } else { + throw new Error(`Unknown stitching strategy mode ${mode}`); } } diff --git a/plugins/catalog-backend/src/database/operations/stitcher/performStitching.test.ts b/plugins/catalog-backend/src/database/operations/stitcher/performStitching.test.ts index a864cbdef4..a471f1dce2 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/performStitching.test.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/performStitching.test.ts @@ -35,8 +35,9 @@ describe('performStitching', () => { }); const logger = getVoidLogger(); + // NOTE(freben): Testing the deferred path since it's a superset of the immediate one it.each(databases.eachSupportedId())( - 'runs the happy path in immediate mode for %p', + 'runs the happy path in deferred mode for %p', async databaseId => { const knex = await databases.init(databaseId); await applyDatabaseMigrations(knex); @@ -87,7 +88,11 @@ describe('performStitching', () => { await performStitching({ knex, logger, - strategy: { mode: 'immediate' }, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, entityRef: 'k:ns/n', }); @@ -172,7 +177,11 @@ describe('performStitching', () => { await performStitching({ knex, logger, - strategy: { mode: 'immediate' }, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, entityRef: 'k:ns/n', }); @@ -195,7 +204,11 @@ describe('performStitching', () => { await performStitching({ knex, logger, - strategy: { mode: 'immediate' }, + strategy: { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }, entityRef: 'k:ns/n', }); diff --git a/plugins/catalog-backend/src/database/operations/stitcher/performStitching.ts b/plugins/catalog-backend/src/database/operations/stitcher/performStitching.ts index bcb112690c..e4388813e8 100644 --- a/plugins/catalog-backend/src/database/operations/stitcher/performStitching.ts +++ b/plugins/catalog-backend/src/database/operations/stitcher/performStitching.ts @@ -32,6 +32,7 @@ import { DbSearchRow, } from '../../tables'; import { buildEntitySearch } from './buildEntitySearch'; +import { markDeferredStitchCompleted } from './markDeferredStitchCompleted'; import { BATCH_SIZE, generateStableHash } from './util'; // See https://github.com/facebook/react/blob/f0cf832e1d0c8544c36aa8b310960885a11a847c/packages/react-dom-bindings/src/shared/sanitizeURL.js @@ -217,6 +218,14 @@ export async function performStitching(options: { .onConflict('entity_id') .merge(['final_entity', 'hash', 'last_updated_at']); + if (options.strategy.mode === 'deferred') { + await markDeferredStitchCompleted({ + knex: knex, + entityRef, + stitchTicket, + }); + } + if (amountOfRowsChanged === 0) { logger.debug(`Entity ${entityRef} is already stitched, skipping write.`); return 'abandoned'; diff --git a/plugins/catalog-backend/src/database/operations/util/deleteOrphanedEntities.test.ts b/plugins/catalog-backend/src/database/operations/util/deleteOrphanedEntities.test.ts index 07ade1c4af..3dacc8f09b 100644 --- a/plugins/catalog-backend/src/database/operations/util/deleteOrphanedEntities.test.ts +++ b/plugins/catalog-backend/src/database/operations/util/deleteOrphanedEntities.test.ts @@ -101,7 +101,7 @@ describe('deleteOrphanedEntities', () => { async function refreshState(knex: Knex) { return await knex('refresh_state') .orderBy('entity_ref') - .select('entity_ref', 'result_hash'); + .select('entity_ref', 'result_hash', 'next_stitch_at'); } async function finalEntities(knex: Knex) { @@ -115,6 +115,7 @@ describe('deleteOrphanedEntities', () => { .select({ entity_ref: 'refresh_state.entity_ref', hash: 'final_entities.hash', + next_stitch_at: 'refresh_state.next_stitch_at', }); } @@ -178,30 +179,132 @@ describe('deleteOrphanedEntities', () => { await insertRelation(knex, 'E7', 'E6'); await expect(run(knex, { mode: 'immediate' })).resolves.toEqual(5); await expect(refreshState(knex)).resolves.toEqual([ - { entity_ref: 'E1', result_hash: 'original' }, + { entity_ref: 'E1', result_hash: 'original', next_stitch_at: null }, { entity_ref: 'E2', result_hash: 'force-stitching', + next_stitch_at: null, }, { entity_ref: 'E7', result_hash: 'force-stitching', + next_stitch_at: null, }, - { entity_ref: 'E8', result_hash: 'original' }, - { entity_ref: 'E9', result_hash: 'original' }, + { entity_ref: 'E8', result_hash: 'original', next_stitch_at: null }, + { entity_ref: 'E9', result_hash: 'original', next_stitch_at: null }, ]); await expect(finalEntities(knex)).resolves.toEqual([ - { entity_ref: 'E1', hash: 'original' }, + { entity_ref: 'E1', hash: 'original', next_stitch_at: null }, { entity_ref: 'E2', hash: 'force-stitching', + next_stitch_at: null, }, { entity_ref: 'E7', hash: 'force-stitching', + next_stitch_at: null, }, - { entity_ref: 'E8', hash: 'original' }, - { entity_ref: 'E9', hash: 'original' }, + { entity_ref: 'E8', hash: 'original', next_stitch_at: null }, + { entity_ref: 'E9', hash: 'original', next_stitch_at: null }, + ]); + }, + ); + + it.each(databases.eachSupportedId())( + 'works for some mixed paths in deferred mode, %p', + async databaseId => { + /* + In this graph, edges represent refresh state references, not entity relations: + + P1 - E1 -- E2 + / + E3 + / + E4 + \ + E5 + / + E6 + \ + E7 + / + P2 - E8 + + P3 - E9 + + E10 + + Result: E3, E4, E5, E6, and E10 deleted; others remain + Entities that had relations pointing at orphans are marked for reprocessing + */ + const knex = await createDatabase(databaseId); + await insertEntity( + knex, + 'E1', + 'E2', + 'E3', + 'E4', + 'E5', + 'E6', + 'E7', + 'E8', + 'E9', + 'E10', + ); + await insertReference( + knex, + { source_key: 'P1', target_entity_ref: 'E1' }, + { source_entity_ref: 'E1', target_entity_ref: 'E2' }, + { source_entity_ref: 'E3', target_entity_ref: 'E2' }, + { source_entity_ref: 'E4', target_entity_ref: 'E3' }, + { source_entity_ref: 'E4', target_entity_ref: 'E5' }, + { source_entity_ref: 'E6', target_entity_ref: 'E5' }, + { source_entity_ref: 'E6', target_entity_ref: 'E7' }, + { source_key: 'P2', target_entity_ref: 'E8' }, + { source_entity_ref: 'E8', target_entity_ref: 'E7' }, + { source_key: 'P3', target_entity_ref: 'E9' }, + ); + await insertRelation(knex, 'E1', 'E2'); + await insertRelation(knex, 'E2', 'E3'); + await insertRelation(knex, 'E10', 'E6'); + await insertRelation(knex, 'E7', 'E6'); + await expect( + run(knex, { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 1 }, + }), + ).resolves.toEqual(5); + await expect(refreshState(knex)).resolves.toEqual([ + { entity_ref: 'E1', result_hash: 'original', next_stitch_at: null }, + { + entity_ref: 'E2', + result_hash: 'original', + next_stitch_at: expect.anything(), + }, + { + entity_ref: 'E7', + result_hash: 'original', + next_stitch_at: expect.anything(), + }, + { entity_ref: 'E8', result_hash: 'original', next_stitch_at: null }, + { entity_ref: 'E9', result_hash: 'original', next_stitch_at: null }, + ]); + await expect(finalEntities(knex)).resolves.toEqual([ + { entity_ref: 'E1', hash: 'original', next_stitch_at: null }, + { + entity_ref: 'E2', + hash: 'original', + next_stitch_at: expect.anything(), + }, + { + entity_ref: 'E7', + hash: 'original', + next_stitch_at: expect.anything(), + }, + { entity_ref: 'E8', hash: 'original', next_stitch_at: null }, + { entity_ref: 'E9', hash: 'original', next_stitch_at: null }, ]); }, ); diff --git a/plugins/catalog-backend/src/database/tables.ts b/plugins/catalog-backend/src/database/tables.ts index a429dcde8d..57731e3d27 100644 --- a/plugins/catalog-backend/src/database/tables.ts +++ b/plugins/catalog-backend/src/database/tables.ts @@ -81,6 +81,47 @@ export type DbRefreshStateRow = { * continuously gets moved forward as items are picked up for processing. */ next_update_at: string | Date; + /** + * If a stitch has been requested, this is the point in time that that last + * happened. + * + * @remarks + * + * Each time that a request is made, this timestamp is updated to the current + * time, overwriting the previous value if applicable. + * + * When the stitch loop runs and picks up an entity, this timestamp is not + * immediately reset. It's instead moved forward in time by a certain amount, + * which means that if the stitcher for some reason fails (eg if the process + * crashes or gets shut down), the entity will be picked up again in the + * future. + * + * Only when a stitch run is completed successfully, AND it's found that the + * stitch ticket has not changed since the start (which means that no new + * request has been made behind our backs), does the timestamp (and the + * ticket) get reset. + */ + next_stitch_at?: string | Date | null; + /** + * If a stitch has been requested, this is the unique ticket that was chosen + * to mark the last request. + * + * @remarks + * + * Each time that a request is made, a new random ticket is chosen, + * overwriting the previous value if applicable. + * + * When the stitch loop runs and picks up an entity, this column is left + * unchanged. This means that if the stitcher for some reason fails (eg if the + * process crashes or gets shut down), the entity will be picked up again in + * the future. + * + * Only when a stitch run is completed successfully, AND it's found that the + * stitch ticket has not changed since the start (which means that no new + * request has been made behind our backs), does the ticket (and the + * timestamp) get reset. + */ + next_stitch_ticket?: string | null; /** * The last time that this entity was emitted by somebody (the entity provider * or a parent entity). diff --git a/plugins/catalog-backend/src/deprecated.ts b/plugins/catalog-backend/src/deprecated.ts index c92ec719b7..f3c199cbd3 100644 --- a/plugins/catalog-backend/src/deprecated.ts +++ b/plugins/catalog-backend/src/deprecated.ts @@ -33,6 +33,8 @@ import { type EntityProvider as _EntityProvider, type EntityProviderConnection as _EntityProviderConnection, type EntityProviderMutation as _EntityProviderMutation, + type AnalyzeOptions as _AnalyzeOptions, + type ScmLocationAnalyzer as _ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { type LocationSpec as _LocationSpec } from '@backstage/plugin-catalog-common'; @@ -141,3 +143,13 @@ export type EntityProviderMutation = _EntityProviderMutation; * @deprecated use the same type from `@backstage/plugin-catalog-common` instead */ export type LocationSpec = _LocationSpec; +/** + * @public + * @deprecated import from `@backstage/plugin-catalog-node` instead + */ +export type AnalyzeOptions = _AnalyzeOptions; +/** + * @public + * @deprecated import from `@backstage/plugin-catalog-node` instead + */ +export type ScmLocationAnalyzer = _ScmLocationAnalyzer; diff --git a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts index 761a0f8c0a..ad752b770d 100644 --- a/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts +++ b/plugins/catalog-backend/src/ingestion/LocationAnalyzer.ts @@ -18,11 +18,12 @@ import { Logger } from 'winston'; import parseGitUrl from 'git-url-parse'; import { Entity } from '@backstage/catalog-model'; import { ScmIntegrationRegistry } from '@backstage/integration'; -import { LocationAnalyzer, ScmLocationAnalyzer } from './types'; +import { LocationAnalyzer } from './types'; import { AnalyzeLocationRequest, AnalyzeLocationResponse, } from '@backstage/plugin-catalog-common'; +import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node'; export class RepoLocationAnalyzer implements LocationAnalyzer { private readonly logger: Logger; diff --git a/plugins/catalog-backend/src/ingestion/index.ts b/plugins/catalog-backend/src/ingestion/index.ts index 0b00809043..c97d029b5c 100644 --- a/plugins/catalog-backend/src/ingestion/index.ts +++ b/plugins/catalog-backend/src/ingestion/index.ts @@ -21,6 +21,4 @@ export type { AnalyzeLocationRequest, AnalyzeLocationResponse, LocationAnalyzer, - ScmLocationAnalyzer, - AnalyzeOptions, } from './types'; diff --git a/plugins/catalog-backend/src/ingestion/types.ts b/plugins/catalog-backend/src/ingestion/types.ts index 78339b3b23..3875d3af03 100644 --- a/plugins/catalog-backend/src/ingestion/types.ts +++ b/plugins/catalog-backend/src/ingestion/types.ts @@ -78,19 +78,3 @@ export type LocationAnalyzer = { location: AnalyzeLocationRequest, ): Promise; }; -/** @public */ -export type AnalyzeOptions = { - url: string; - catalogFilename?: string; -}; - -/** @public */ -export type ScmLocationAnalyzer = { - /** The method that decides if this analyzer can work with the provided url */ - supports(url: string): boolean; - /** This function can return an array of already existing entities */ - analyze(options: AnalyzeOptions): Promise<{ - /** Existing entities in the analyzed location */ - existing: AnalyzeLocationExistingEntity[]; - }>; -}; diff --git a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts index 72f92b9b40..3acf811559 100644 --- a/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts +++ b/plugins/catalog-backend/src/processing/DefaultCatalogProcessingEngine.ts @@ -28,7 +28,7 @@ import { metrics, trace } from '@opentelemetry/api'; import { ProcessingDatabase, RefreshStateItem } from '../database/types'; import { createCounterMetric, createSummaryMetric } from '../util/metrics'; import { CatalogProcessingOrchestrator, EntityProcessingResult } from './types'; -import { Stitcher } from '../stitching/types'; +import { Stitcher, stitchingStrategyFromConfig } from '../stitching/types'; import { startTaskPipeline } from './TaskPipeline'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { Config } from '@backstage/config'; @@ -335,11 +335,13 @@ export class DefaultCatalogProcessingEngine { return () => {}; } + const stitchingStrategy = stitchingStrategyFromConfig(this.config); + const runOnce = async () => { try { const n = await deleteOrphanedEntities({ knex: this.knex, - strategy: { mode: 'immediate' }, + strategy: stitchingStrategy, }); if (n > 0) { this.logger.info(`Deleted ${n} orphaned entities`); diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 2278a30e62..a31615dfc6 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -191,12 +191,69 @@ export const spec = { }, }, requestBodies: {}, - responses: {}, + responses: { + ErrorResponse: { + description: 'An error response from the backend.', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/Error', + }, + }, + }, + }, + }, schemas: { + Error: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + name: { + type: 'string', + }, + message: { + type: 'string', + }, + stack: { + type: 'string', + }, + code: { + type: 'string', + }, + }, + required: ['name', 'message'], + }, + request: { + type: 'object', + properties: { + method: { + type: 'string', + }, + url: { + type: 'string', + }, + }, + required: ['method', 'url'], + }, + response: { + type: 'object', + properties: { + statusCode: { + type: 'number', + }, + }, + required: ['statusCode'], + }, + }, + required: ['error', 'response'], + }, JsonObject: { type: 'object', properties: {}, description: 'A type representing all allowed JSON object values.', + additionalProperties: true, }, MapStringString: { type: 'object', @@ -698,38 +755,6 @@ export const spec = { required: ['type', 'target'], additionalProperties: false, }, - SerializedError: { - allOf: [ - { - $ref: '#/components/schemas/JsonObject', - }, - { - type: 'object', - properties: { - code: { - type: 'string', - description: - 'A custom code (not necessarily the same as an HTTP response code); may not be present', - }, - stack: { - type: 'string', - description: 'A stringified stack trace; may not be present', - }, - message: { - type: 'string', - description: 'The message of the exception that was thrown', - }, - name: { - type: 'string', - description: 'The name of the exception that was thrown', - }, - }, - required: ['message', 'name'], - }, - ], - description: 'The serialized form of an Error.', - additionalProperties: false, - }, EntitiesQueryResponse: { type: 'object', properties: { @@ -777,6 +802,12 @@ export const spec = { '200': { description: 'Refreshed', }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -829,6 +860,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -882,6 +919,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -902,6 +945,12 @@ export const spec = { '204': { description: 'Deleted successfully.', }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -931,6 +980,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -966,6 +1021,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1002,6 +1063,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1015,7 +1082,7 @@ export const spec = { }, ], requestBody: { - required: true, + required: false, content: { 'application/json': { schema: { @@ -1072,6 +1139,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1139,6 +1212,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1203,6 +1282,44 @@ export const spec = { }, }, }, + '201': { + description: '201 response', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + location: { + type: 'object', + properties: { + id: { + type: 'string', + }, + type: { + type: 'string', + }, + target: { + type: 'string', + }, + }, + required: ['id', 'type', 'target'], + }, + entities: { + type: 'array', + items: {}, + }, + }, + required: ['location', 'entities'], + }, + }, + }, + }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1264,6 +1381,9 @@ export const spec = { }, }, }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1289,6 +1409,9 @@ export const spec = { }, }, }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1315,6 +1438,12 @@ export const spec = { '204': { description: 'No content', }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1350,6 +1479,12 @@ export const spec = { }, }, }, + '400': { + $ref: '#/components/responses/ErrorResponse', + }, + default: { + $ref: '#/components/responses/ErrorResponse', + }, }, security: [ {}, @@ -1387,32 +1522,31 @@ export const spec = { responses: { '200': { description: 'Ok', + }, + '400': { + description: '400 response', content: { - 'application/json': { + 'application/json; charset=utf-8': { schema: { - anyOf: [ - { - type: 'object', - properties: { - errors: { - $ref: '#/components/schemas/SerializedError', - }, - }, - required: ['errors'], - }, - { - type: 'object', - properties: { - errors: { - type: 'array', - items: { - $ref: '#/components/schemas/SerializedError', + type: 'object', + properties: { + errors: { + type: 'array', + items: { + type: 'object', + properties: { + name: { + type: 'string', + }, + message: { + type: 'string', }, }, + required: ['name', 'message'], }, - required: ['errors'], }, - ], + }, + required: ['errors'], }, }, }, diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index 4db3147a97..3375f7fe15 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1,5 +1,4 @@ openapi: 3.0.3 - info: title: '@backstage/plugin-catalog-backend' version: '1' @@ -8,10 +7,8 @@ info: name: Apache-2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html contact: {} - servers: - url: / - components: examples: {} headers: {} @@ -73,10 +70,15 @@ components: items: type: string examples: - 'Get name and the entire relations collection': - value: ['metadata.name', 'relations'] - 'Get kind, name and namespace': - value: ['kind', 'metadata.name', 'metadata.namespace'] + Get name and the entire relations collection: + value: + - metadata.name + - relations + Get kind, name and namespace: + value: + - kind + - metadata.name + - metadata.namespace filter: name: filter in: query @@ -88,11 +90,12 @@ components: items: type: string examples: - 'Get groups': - value: ['kind=group'] - 'Get orphaned components': + Get groups: value: - ['kind=component,metadata.annotations.backstage.io/orphan=true'] + - kind=group + Get orphaned components: + value: + - kind=component,metadata.annotations.backstage.io/orphan=true offset: name: offset in: query @@ -125,17 +128,63 @@ components: explode: true style: form examples: - 'Order ascending by name': - value: ['metadata.name,asc'] - 'Order descending by owner': - value: ['spec.owner,desc'] + Order ascending by name: + value: + - metadata.name,asc + Order descending by owner: + value: + - spec.owner,desc requestBodies: {} - responses: {} + responses: + ErrorResponse: + description: An error response from the backend. + content: + application/json: + schema: + $ref: '#/components/schemas/Error' schemas: + Error: + type: object + properties: + error: + type: object + properties: + name: + type: string + message: + type: string + stack: + type: string + code: + type: string + required: + - name + - message + request: + type: object + properties: + method: + type: string + url: + type: string + required: + - method + - url + response: + type: object + properties: + statusCode: + type: number + required: + - statusCode + required: + - error + - response JsonObject: type: object properties: {} description: A type representing all allowed JSON object values. + additionalProperties: true MapStringString: type: object properties: {} @@ -350,7 +399,6 @@ components: count: type: number additionalProperties: false - EntityFacetsResponse: type: object properties: @@ -593,28 +641,6 @@ components: - type - target additionalProperties: false - SerializedError: - allOf: - - $ref: '#/components/schemas/JsonObject' - - type: object - properties: - code: - type: string - description: A custom code (not necessarily the same as an HTTP response code); may not be present - stack: - type: string - description: A stringified stack trace; may not be present - message: - type: string - description: The message of the exception that was thrown - name: - type: string - description: The name of the exception that was thrown - required: - - message - - name - description: The serialized form of an Error. - additionalProperties: false EntitiesQueryResponse: type: object properties: @@ -622,8 +648,7 @@ components: type: array items: $ref: '#/components/schemas/Entity' - description: |- - The list of entities paginated by a specific filter. + description: The list of entities paginated by a specific filter. totalItems: type: number pageInfo: @@ -631,12 +656,10 @@ components: properties: nextCursor: type: string - description: |- - The cursor for the next batch of entities. + description: The cursor for the next batch of entities. prevCursor: type: string - description: |- - The cursor for the previous batch of entities. + description: The cursor for the previous batch of entities. additionalProperties: false securitySchemes: JWT: @@ -651,6 +674,10 @@ paths: responses: '200': description: Refreshed + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -684,6 +711,10 @@ paths: type: array items: $ref: '#/components/schemas/Entity' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -712,6 +743,10 @@ paths: application/json: schema: $ref: '#/components/schemas/Entity' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -723,6 +758,10 @@ paths: responses: '204': description: Deleted successfully. + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -739,6 +778,10 @@ paths: application/json: schema: $ref: '#/components/schemas/Entity' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -757,6 +800,10 @@ paths: application/json: schema: $ref: '#/components/schemas/EntityAncestryResponse' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -775,13 +822,17 @@ paths: application/json: schema: $ref: '#/components/schemas/EntitiesBatchResponse' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] parameters: - $ref: '#/components/parameters/fields' requestBody: - required: true + required: false content: application/json: schema: @@ -798,14 +849,17 @@ paths: items: type: string examples: - 'Fetch Backstage entities': + Fetch Backstage entities: value: entityRefs: - ['component:default/backstage', 'api:default/backstage'] - 'Fetch annotations for backstage entity': + - component:default/backstage + - api:default/backstage + Fetch annotations for backstage entity: value: - entityRefs: ['component:default/backstage'] - fields: ['metadata.annotations'] + entityRefs: + - component:default/backstage + fields: + - metadata.annotations /entities/by-query: get: operationId: GetEntitiesByQuery @@ -817,6 +871,10 @@ paths: application/json: schema: $ref: '#/components/schemas/EntitiesQueryResponse' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -855,6 +913,10 @@ paths: application/json: schema: $ref: '#/components/schemas/EntityFacetsResponse' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -868,10 +930,12 @@ paths: items: type: string examples: - 'Entities by kind': - value: ['kind'] - 'Entities by spec type': - value: ['spec.type'] + Entities by kind: + value: + - kind + Entities by spec type: + value: + - spec.type - $ref: '#/components/parameters/filter' /locations: post: @@ -896,6 +960,36 @@ paths: required: - entities - location + '201': + description: 201 response + content: + application/json: + schema: + type: object + properties: + location: + type: object + properties: + id: + type: string + type: + type: string + target: + type: string + required: + - id + - type + - target + entities: + type: array + items: {} + required: + - location + - entities + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -937,6 +1031,8 @@ paths: $ref: '#/components/schemas/Location' required: - data + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -952,6 +1048,8 @@ paths: application/json: schema: $ref: '#/components/schemas/Location' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -968,6 +1066,10 @@ paths: responses: '204': description: No content + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -989,6 +1091,10 @@ paths: application/json: schema: $ref: '#/components/schemas/AnalyzeLocationResponse' + '400': + $ref: '#/components/responses/ErrorResponse' + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] @@ -1013,24 +1119,27 @@ paths: responses: '200': description: Ok + '400': + description: 400 response content: - application/json: + application/json; charset=utf-8: schema: - anyOf: - - type: object - properties: - errors: - $ref: '#/components/schemas/SerializedError' - required: - - errors - - type: object - properties: - errors: - type: array - items: - $ref: '#/components/schemas/SerializedError' - required: - - errors + type: object + properties: + errors: + type: array + items: + type: object + properties: + name: + type: string + message: + type: string + required: + - name + - message + required: + - errors security: - {} - JWT: [] diff --git a/plugins/catalog-backend/src/service/CatalogBuilder.ts b/plugins/catalog-backend/src/service/CatalogBuilder.ts index c12178e888..5cf76ee1ac 100644 --- a/plugins/catalog-backend/src/service/CatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/CatalogBuilder.ts @@ -38,6 +38,7 @@ import { CatalogProcessor, CatalogProcessorParser, EntityProvider, + ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { AnnotateLocationEntityProcessor, @@ -57,7 +58,7 @@ import { yamlPlaceholderResolver, } from '../modules/core/PlaceholderProcessor'; import { defaultEntityDataParser } from '../modules/util/parse'; -import { LocationAnalyzer, ScmLocationAnalyzer } from '../ingestion/types'; +import { LocationAnalyzer } from '../ingestion/types'; import { CatalogProcessingEngine } from '../processing'; import { DefaultProcessingDatabase } from '../database/DefaultProcessingDatabase'; import { applyDatabaseMigrations } from '../database/migrations'; diff --git a/plugins/catalog-backend/src/service/CatalogPlugin.ts b/plugins/catalog-backend/src/service/CatalogPlugin.ts index b8d5bdbeda..fa53ef510b 100644 --- a/plugins/catalog-backend/src/service/CatalogPlugin.ts +++ b/plugins/catalog-backend/src/service/CatalogPlugin.ts @@ -19,17 +19,22 @@ import { } from '@backstage/backend-plugin-api'; import { CatalogBuilder } from './CatalogBuilder'; import { + CatalogAnalysisExtensionPoint, + catalogAnalysisExtensionPoint, CatalogProcessingExtensionPoint, catalogProcessingExtensionPoint, } from '@backstage/plugin-catalog-node/alpha'; import { CatalogProcessor, EntityProvider, + ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; import { loggerToWinstonLogger } from '@backstage/backend-common'; import { PlaceholderResolver } from '../modules'; -class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { +class CatalogProcessingExtensionPointImpl + implements CatalogProcessingExtensionPoint +{ #processors = new Array(); #entityProviders = new Array(); #placeholderResolvers: Record = {}; @@ -67,6 +72,20 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { } } +class CatalogAnalysisExtensionPointImpl + implements CatalogAnalysisExtensionPoint +{ + #locationAnalyzers = new Array(); + + addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void { + this.#locationAnalyzers.push(analyzer); + } + + get locationAnalyzers() { + return this.#locationAnalyzers; + } +} + /** * Catalog plugin * @alpha @@ -74,13 +93,19 @@ class CatalogExtensionPointImpl implements CatalogProcessingExtensionPoint { export const catalogPlugin = createBackendPlugin({ pluginId: 'catalog', register(env) { - const processingExtensions = new CatalogExtensionPointImpl(); + const processingExtensions = new CatalogProcessingExtensionPointImpl(); // plugins depending on this API will be initialized before this plugins init method is executed. env.registerExtensionPoint( catalogProcessingExtensionPoint, processingExtensions, ); + const analysisExtensions = new CatalogAnalysisExtensionPointImpl(); + env.registerExtensionPoint( + catalogAnalysisExtensionPoint, + analysisExtensions, + ); + env.registerInit({ deps: { logger: coreServices.logger, @@ -116,6 +141,7 @@ export const catalogPlugin = createBackendPlugin({ Object.entries(processingExtensions.placeholderResolvers).forEach( ([key, resolver]) => builder.setPlaceholderResolver(key, resolver), ); + builder.addLocationAnalyzers(...analysisExtensions.locationAnalyzers); const { processingEngine, router } = await builder.build(); diff --git a/plugins/catalog-backend/src/service/createRouter.test.ts b/plugins/catalog-backend/src/service/createRouter.test.ts index c14bc05476..700e35edc1 100644 --- a/plugins/catalog-backend/src/service/createRouter.test.ts +++ b/plugins/catalog-backend/src/service/createRouter.test.ts @@ -22,6 +22,7 @@ import { ANNOTATION_LOCATION, ANNOTATION_ORIGIN_LOCATION, Entity, + stringifyEntityRef, } from '@backstage/catalog-model'; import express from 'express'; import request from 'supertest'; @@ -38,12 +39,14 @@ import { RESOURCE_TYPE_CATALOG_ENTITY } from '@backstage/plugin-catalog-common/a import { CatalogProcessingOrchestrator } from '../processing/types'; import { z } from 'zod'; import { encodeCursor } from './util'; +import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; +import { Server } from 'http'; describe('createRouter readonly disabled', () => { let entitiesCatalog: jest.Mocked; let locationService: jest.Mocked; let orchestrator: jest.Mocked; - let app: express.Express; + let app: express.Express | Server; let refreshService: RefreshService; beforeAll(async () => { @@ -72,7 +75,7 @@ describe('createRouter readonly disabled', () => { config: new ConfigReader(undefined), permissionIntegrationRouter: express.Router(), }); - app = express().use(router); + app = wrapInOpenApiTestServer(express().use(router)); }); beforeEach(() => { @@ -412,15 +415,27 @@ describe('createRouter readonly disabled', () => { }); it('can fetch entities by refs', async () => { - const entity: Entity = {} as any; + const entity: Entity = { + apiVersion: 'a', + kind: 'component', + metadata: { + name: 'a', + }, + }; + const entityRef = stringifyEntityRef(entity); entitiesCatalog.entitiesBatch.mockResolvedValue({ items: [entity] }); const response = await request(app) .post('/entities/by-refs') .set('Content-Type', 'application/json') - .send('{"entityRefs":["a"],"fields":["b"]}'); + .send( + JSON.stringify({ + entityRefs: [entityRef], + fields: ['metadata.name'], + }), + ); expect(entitiesCatalog.entitiesBatch).toHaveBeenCalledTimes(1); expect(entitiesCatalog.entitiesBatch).toHaveBeenCalledWith({ - entityRefs: ['a'], + entityRefs: [entityRef], fields: expect.any(Function), }); expect(response.status).toEqual(200); diff --git a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts index 33033c66f3..b1d5e8dd1f 100644 --- a/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts +++ b/plugins/catalog-backend/src/service/request/parseQueryEntitiesParams.ts @@ -24,10 +24,10 @@ import { parseEntityFilterParams } from './parseEntityFilterParams'; import { parseEntityOrderFieldParams } from './parseEntityOrderFieldParams'; import { parseEntityTransformParams } from './parseEntityTransformParams'; import { spec } from '../../schema/openapi.generated'; -import { QueryParameters } from '@backstage/backend-openapi-utils'; +import { internal } from '@backstage/backend-openapi-utils'; export function parseQueryEntitiesParams( - params: QueryParameters, + params: internal.QuerySchema, ): Omit { const fields = parseEntityTransformParams(params); diff --git a/plugins/catalog-backend/src/stitching/DefaultStitcher.ts b/plugins/catalog-backend/src/stitching/DefaultStitcher.ts index 095d80ad83..990c5efd3b 100644 --- a/plugins/catalog-backend/src/stitching/DefaultStitcher.ts +++ b/plugins/catalog-backend/src/stitching/DefaultStitcher.ts @@ -15,14 +15,26 @@ */ import { Config } from '@backstage/config'; +import { durationToMilliseconds, HumanDuration } from '@backstage/types'; import { Knex } from 'knex'; import splitToChunks from 'lodash/chunk'; import { DateTime } from 'luxon'; import { Logger } from 'winston'; +import { getDeferredStitchableEntities } from '../database/operations/stitcher/getDeferredStitchableEntities'; +import { markForStitching } from '../database/operations/stitcher/markForStitching'; import { performStitching } from '../database/operations/stitcher/performStitching'; import { DbRefreshStateRow } from '../database/tables'; +import { startTaskPipeline } from '../processing/TaskPipeline'; import { progressTracker } from './progressTracker'; -import { Stitcher, StitchingStrategy } from './types'; +import { + Stitcher, + StitchingStrategy, + stitchingStrategyFromConfig, +} from './types'; + +type DeferredStitchItem = Awaited< + ReturnType +>[0]; type StitchProgressTracker = ReturnType; @@ -36,9 +48,10 @@ export class DefaultStitcher implements Stitcher { private readonly logger: Logger; private readonly strategy: StitchingStrategy; private readonly tracker: StitchProgressTracker; + private stopFunc?: () => void; static fromConfig( - _config: Config, + config: Config, options: { knex: Knex; logger: Logger; @@ -47,7 +60,7 @@ export class DefaultStitcher implements Stitcher { return new DefaultStitcher({ knex: options.knex, logger: options.logger, - strategy: { mode: 'immediate' }, + strategy: stitchingStrategyFromConfig(config), }); } @@ -68,6 +81,16 @@ export class DefaultStitcher implements Stitcher { }) { const { entityRefs, entityIds } = options; + if (this.strategy.mode === 'deferred') { + await markForStitching({ + knex: this.knex, + strategy: this.strategy, + entityRefs, + entityIds, + }); + return; + } + if (entityRefs) { for (const entityRef of entityRefs) { await this.#stitchOne({ entityRef }); @@ -91,11 +114,55 @@ export class DefaultStitcher implements Stitcher { } async start() { - // Only called immediately for now + if (this.strategy.mode === 'deferred') { + if (this.stopFunc) { + throw new Error('Processing engine is already started'); + } + + const { pollingInterval, stitchTimeout } = this.strategy; + + const stopPipeline = startTaskPipeline({ + lowWatermark: 2, + highWatermark: 5, + pollingIntervalMs: durationToMilliseconds(pollingInterval), + loadTasks: async count => { + return await this.#getStitchableEntities(count, stitchTimeout); + }, + processTask: async item => { + return await this.#stitchOne({ + entityRef: item.entityRef, + stitchTicket: item.stitchTicket, + stitchRequestedAt: item.stitchRequestedAt, + }); + }, + }); + + this.stopFunc = () => { + stopPipeline(); + }; + } } async stop() { - // Only called immediately for now + if (this.strategy.mode === 'deferred') { + if (this.stopFunc) { + this.stopFunc(); + this.stopFunc = undefined; + } + } + } + + async #getStitchableEntities(count: number, stitchTimeout: HumanDuration) { + try { + return await getDeferredStitchableEntities({ + knex: this.knex, + batchSize: count, + stitchTimeout: stitchTimeout, + }); + } catch (error) { + this.logger.warn('Failed to load stitchable entities', error); + return []; + } } async #stitchOne(options: { diff --git a/plugins/catalog-backend/src/stitching/progressTracker.ts b/plugins/catalog-backend/src/stitching/progressTracker.ts index e8e08dbc54..f82dbe378e 100644 --- a/plugins/catalog-backend/src/stitching/progressTracker.ts +++ b/plugins/catalog-backend/src/stitching/progressTracker.ts @@ -19,10 +19,11 @@ import { metrics } from '@opentelemetry/api'; import { Knex } from 'knex'; import { DateTime } from 'luxon'; import { Logger } from 'winston'; +import { DbRefreshStateRow } from '../database/tables'; import { createCounterMetric } from '../util/metrics'; // Helps wrap the timing and logging behaviors -export function progressTracker(_knex: Knex, logger: Logger) { +export function progressTracker(knex: Knex, logger: Logger) { // prom-client metrics are deprecated in favour of OpenTelemetry metrics. const promStitchedEntities = createCounterMetric({ name: 'catalog_stitched_entities_count', @@ -46,6 +47,26 @@ export function progressTracker(_knex: Knex, logger: Logger) { }, ); + const stitchingQueueCount = meter.createObservableGauge( + 'catalog.stitching.queue.length', + { description: 'Number of entities currently in the stitching queue' }, + ); + stitchingQueueCount.addCallback(async result => { + const total = await knex('refresh_state') + .count({ count: '*' }) + .whereNotNull('next_stitch_at'); + result.observe(Number(total[0].count)); + }); + + const stitchingQueueDelay = meter.createHistogram( + 'catalog.stitching.queue.delay', + { + description: + 'The amount of delay between being scheduled for stitching, and the start of actually being stitched', + unit: 'seconds', + }, + ); + function stitchStart(item: { entityRef: string; stitchRequestedAt?: DateTime; @@ -53,6 +74,11 @@ export function progressTracker(_knex: Knex, logger: Logger) { logger.debug(`Stitching ${item.entityRef}`); const startTime = process.hrtime(); + if (item.stitchRequestedAt) { + stitchingQueueDelay.record( + -item.stitchRequestedAt.diffNow().as('seconds'), + ); + } function endTime() { const delta = process.hrtime(startTime); diff --git a/plugins/catalog-backend/src/stitching/types.ts b/plugins/catalog-backend/src/stitching/types.ts index 5f32a85b1f..9f7a5652dd 100644 --- a/plugins/catalog-backend/src/stitching/types.ts +++ b/plugins/catalog-backend/src/stitching/types.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +import { Config } from '@backstage/config'; +import { HumanDuration } from '@backstage/types'; + /** * Performs the act of stitching - to take all of the various outputs from the * ingestion process, and stitching them together into the final entity JSON @@ -33,8 +36,45 @@ export interface Stitcher { * @remarks * * In immediate mode, stitching happens "in-band" (blocking) immediately when - * each processing task finishes. + * each processing task finishes. When set to `'deferred'`, stitching is instead + * deferred to happen on a separate asynchronous worker queue just like + * processing. + * + * Deferred stitching should make performance smoother when ingesting large + * amounts of entities, and reduce p99 processing times and repeated + * over-stitching of hot spot entities when fan-out/fan-in in terms of relations + * is very large. It does however also come with some performance cost due to + * the queuing with how much wall-clock time some types of task take. */ -export type StitchingStrategy = { - mode: 'immediate'; -}; +export type StitchingStrategy = + | { + mode: 'immediate'; + } + | { + mode: 'deferred'; + pollingInterval: HumanDuration; + stitchTimeout: HumanDuration; + }; + +export function stitchingStrategyFromConfig(config: Config): StitchingStrategy { + const strategyMode = config.getOptionalString( + 'catalog.stitchingStrategy.mode', + ); + + if (strategyMode === undefined || strategyMode === 'immediate') { + return { + mode: 'immediate', + }; + } else if (strategyMode === 'deferred') { + // TODO(freben): Make parameters configurable + return { + mode: 'deferred', + pollingInterval: { seconds: 1 }, + stitchTimeout: { seconds: 60 }, + }; + } + + throw new Error( + `Invalid stitching strategy mode '${strategyMode}', expected one of 'immediate' or 'deferred'`, + ); +} diff --git a/plugins/catalog-backend/src/tests/integration.test.ts b/plugins/catalog-backend/src/tests/integration.test.ts index 8aeb95f49b..5511677851 100644 --- a/plugins/catalog-backend/src/tests/integration.test.ts +++ b/plugins/catalog-backend/src/tests/integration.test.ts @@ -215,6 +215,11 @@ class TestHarness { connection: ':memory:', }, }, + catalog: { + stitchingStrategy: { + mode: 'immediate', + }, + }, }, ); const logger = options?.logger ?? getVoidLogger(); diff --git a/plugins/catalog-backend/src/tests/migrations.test.ts b/plugins/catalog-backend/src/tests/migrations.test.ts index d2c00e31c0..aa305598aa 100644 --- a/plugins/catalog-backend/src/tests/migrations.test.ts +++ b/plugins/catalog-backend/src/tests/migrations.test.ts @@ -239,4 +239,75 @@ describe('migrations', () => { await knex.destroy(); }, ); + + it.each(databases.eachSupportedId())( + '20230525141717_stitch_queue.js, %p', + async databaseId => { + const knex = await databases.init(databaseId); + + await migrateUntilBefore(knex, '20230525141717_stitch_queue.js'); + + await knex + .insert([ + { + entity_id: 'my-id', + entity_ref: 'k:ns/n', + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + next_update_at: knex.fn.now(), + last_discovery_at: knex.fn.now(), + }, + ]) + .into('refresh_state'); + await knex + .insert({ + entity_id: 'my-id', + hash: 'd1c0c56d5fea4238e4c091d9dde4cb42d05a6b7e', + stitch_ticket: '', + final_entity: '{}', + }) + .into('final_entities'); + + await migrateUpOnce(knex); + + await expect(knex('refresh_state')).resolves.toEqual([ + { + entity_id: 'my-id', + entity_ref: 'k:ns/n', + location_key: null, + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + cache: null, + unprocessed_hash: null, + result_hash: null, + next_update_at: expect.anything(), + next_stitch_at: null, + next_stitch_ticket: null, + last_discovery_at: expect.anything(), + }, + ]); + + await migrateDownOnce(knex); + + await expect(knex('refresh_state')).resolves.toEqual([ + { + entity_id: 'my-id', + entity_ref: 'k:ns/n', + location_key: null, + unprocessed_entity: '{}', + processed_entity: '{}', + errors: '[]', + cache: null, + unprocessed_hash: null, + result_hash: null, + next_update_at: expect.anything(), + last_discovery_at: expect.anything(), + }, + ]); + + await knex.destroy(); + }, + ); }); diff --git a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts index c1a869f9ad..ad659ce845 100644 --- a/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts +++ b/plugins/catalog-backend/src/tests/performance/stitchingPerformance.test.ts @@ -19,18 +19,24 @@ import { createBackendModule, createServiceFactory, } from '@backstage/backend-plugin-api'; -import { TestDatabases, startTestBackend } from '@backstage/backend-test-utils'; +import { + mockServices, + startTestBackend, + TestDatabases, +} from '@backstage/backend-test-utils'; import { catalogProcessingExtensionPoint } from '@backstage/plugin-catalog-node/alpha'; import { Knex } from 'knex'; import { applyDatabaseMigrations } from '../../database/migrations'; import { + SyntheticLoadEntitiesProcessor, + SyntheticLoadEntitiesProvider, SyntheticLoadEvents, SyntheticLoadOptions, - SyntheticLoadEntitiesProvider, - SyntheticLoadEntitiesProcessor, } from './lib/catalogModuleSyntheticLoadEntities'; import { describePerformanceTest, performanceTraceEnabled } from './lib/env'; +jest.setTimeout(600_000); + function defer() { let resolve: (value: T | PromiseLike) => void; let reject: (error?: unknown) => void; @@ -147,17 +153,6 @@ class Tracker { } } -function staticDatabase(knex: Knex) { - return createServiceFactory({ - service: coreServices.database, - deps: {}, - createRootContext: () => undefined, - factory: () => ({ getClient: async () => knex }), - }); -} - -jest.setTimeout(600_000); - describePerformanceTest('stitchingPerformance', () => { const databases = TestDatabases.create({ ids: [/* 'MYSQL_8', */ 'POSTGRES_13', 'POSTGRES_9', 'SQLITE_3'], @@ -176,12 +171,79 @@ describePerformanceTest('stitchingPerformance', () => { childrenCount: 3, }; + const config = { + backend: { baseUrl: 'http://localhost:7007' }, + catalog: { stitchingStrategy: { mode: 'immediate' } }, + }; + const tracker = new Tracker(knex, load); const backend = await startTestBackend({ features: [ import('@backstage/plugin-catalog-backend/alpha'), - staticDatabase(knex), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory({ + service: coreServices.database, + deps: {}, + factory: () => ({ getClient: async () => knex }), + }), + createBackendModule({ + moduleId: 'syntheticLoadEntities', + pluginId: 'catalog', + register(reg) { + reg.registerInit({ + deps: { + catalog: catalogProcessingExtensionPoint, + }, + async init({ catalog }) { + catalog.addEntityProvider( + new SyntheticLoadEntitiesProvider(load, tracker.events()), + ); + catalog.addProcessor( + new SyntheticLoadEntitiesProcessor(load), + ); + }, + }); + }, + }), + ], + }); + + await expect(tracker.completion()).resolves.toBeUndefined(); + await backend.stop(); + await knex.destroy(); + }, + ); + + it.each(databases.eachSupportedId())( + 'runs stitching in deferred mode, %p', + async databaseId => { + const knex = await databases.init(databaseId); + await applyDatabaseMigrations(knex); + + const load: SyntheticLoadOptions = { + baseEntitiesCount: 1000, + baseRelationsCount: 3, + baseRelationsSkew: 0.3, + childrenCount: 3, + }; + + const config = { + backend: { baseUrl: 'http://localhost:7007' }, + catalog: { stitchingStrategy: { mode: 'deferred' } }, + }; + + const tracker = new Tracker(knex, load); + + const backend = await startTestBackend({ + features: [ + import('@backstage/plugin-catalog-backend/alpha'), + mockServices.rootConfig.factory({ data: config }), + createServiceFactory({ + service: coreServices.database, + deps: {}, + factory: () => ({ getClient: async () => knex }), + }), createBackendModule({ moduleId: 'syntheticLoadEntities', pluginId: 'catalog', diff --git a/plugins/catalog-graph/CHANGELOG.md b/plugins/catalog-graph/CHANGELOG.md index 5d10ff94f3..31f342f335 100644 --- a/plugins/catalog-graph/CHANGELOG.md +++ b/plugins/catalog-graph/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-catalog-graph +## 0.2.38-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/types@1.1.1 + ## 0.2.37 ### Patch Changes diff --git a/plugins/catalog-graph/package.json b/plugins/catalog-graph/package.json index 0afb6d92f7..e9a69f1545 100644 --- a/plugins/catalog-graph/package.json +++ b/plugins/catalog-graph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graph", - "version": "0.2.37", + "version": "0.2.38-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx index 19082cfe81..2717d813cc 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomLabel.tsx @@ -14,14 +14,13 @@ * limitations under the License. */ import { DependencyGraphTypes } from '@backstage/core-components'; -import { BackstageTheme } from '@backstage/theme'; import makeStyles from '@material-ui/core/styles/makeStyles'; import React from 'react'; import { EntityEdgeData } from './types'; import classNames from 'classnames'; const useStyles = makeStyles( - (theme: BackstageTheme) => ({ + theme => ({ text: { fill: theme.palette.textContrast, }, diff --git a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx index edcdb97fcb..1229ad28f9 100644 --- a/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx +++ b/plugins/catalog-graph/src/components/EntityRelationsGraph/CustomNode.tsx @@ -15,7 +15,6 @@ */ import { DependencyGraphTypes } from '@backstage/core-components'; import { humanizeEntityRef } from '@backstage/plugin-catalog-react'; -import { BackstageTheme } from '@backstage/theme'; import { makeStyles } from '@material-ui/core/styles'; import classNames from 'classnames'; import React, { useLayoutEffect, useRef, useState } from 'react'; @@ -23,7 +22,7 @@ import { EntityKindIcon } from './EntityKindIcon'; import { EntityNodeData } from './types'; const useStyles = makeStyles( - (theme: BackstageTheme) => ({ + theme => ({ node: { fill: theme.palette.grey[300], stroke: theme.palette.grey[300], diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 0a08803e7d..0a8a24b828 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-graphql +## 0.4.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 0.4.0 ### Minor Changes diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index d8869e2578..de493b16f4 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-graphql", "description": "Deprecated, consider using @frontside/backstage-plugin-graphql-backend instead", - "version": "0.4.0", + "version": "0.4.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 8a84c211e5..3c4b4e9472 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-catalog-import +## 0.10.2-next.0 + +### Patch Changes + +- 6db75b900a: Create an experimental plugin that is compatible with the declarative integration system, it is exported from the `/alpha` subpath. +- 6c2b872153: Add official support for React 18. +- 71c97e7d73: The `app.title` configuration is now properly required to be a string. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + ## 0.10.1 ### Patch Changes diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index e892338dea..8749b4924f 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-import", "description": "A Backstage plugin the helps you import entities into your catalog", - "version": "0.10.1", + "version": "0.10.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-node/CHANGELOG.md b/plugins/catalog-node/CHANGELOG.md index 22a2319142..13978277ff 100644 --- a/plugins/catalog-node/CHANGELOG.md +++ b/plugins/catalog-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-node +## 1.4.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + ## 1.4.7 ### Patch Changes diff --git a/plugins/catalog-node/alpha-api-report.md b/plugins/catalog-node/alpha-api-report.md index 082228a58e..c4dc6b12e1 100644 --- a/plugins/catalog-node/alpha-api-report.md +++ b/plugins/catalog-node/alpha-api-report.md @@ -8,8 +8,18 @@ import { CatalogProcessor } from '@backstage/plugin-catalog-node'; import { EntityProvider } from '@backstage/plugin-catalog-node'; import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { PlaceholderResolver } from '@backstage/plugin-catalog-node'; +import { ScmLocationAnalyzer } from '@backstage/plugin-catalog-node'; import { ServiceRef } from '@backstage/backend-plugin-api'; +// @alpha (undocumented) +export interface CatalogAnalysisExtensionPoint { + // (undocumented) + addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; +} + +// @alpha (undocumented) +export const catalogAnalysisExtensionPoint: ExtensionPoint; + // @alpha (undocumented) export interface CatalogProcessingExtensionPoint { // (undocumented) diff --git a/plugins/catalog-node/api-report.md b/plugins/catalog-node/api-report.md index 34d7a4bc9b..ef2c09e61f 100644 --- a/plugins/catalog-node/api-report.md +++ b/plugins/catalog-node/api-report.md @@ -5,12 +5,19 @@ ```ts /// +import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import { JsonValue } from '@backstage/types'; import { LocationEntityV1alpha1 } from '@backstage/catalog-model'; import { LocationSpec as LocationSpec_2 } from '@backstage/plugin-catalog-common'; +// @public (undocumented) +export type AnalyzeOptions = { + url: string; + catalogFilename?: string; +}; + // @public (undocumented) export type CatalogProcessor = { getProcessorName(): string; @@ -198,4 +205,12 @@ export const processingResult: Readonly<{ readonly relation: (spec: EntityRelationSpec) => CatalogProcessorResult; readonly refresh: (key: string) => CatalogProcessorResult; }>; + +// @public (undocumented) +export type ScmLocationAnalyzer = { + supports(url: string): boolean; + analyze(options: AnalyzeOptions): Promise<{ + existing: AnalyzeLocationExistingEntity[]; + }>; +}; ``` diff --git a/plugins/catalog-node/package.json b/plugins/catalog-node/package.json index 2817703044..aa5db93f6d 100644 --- a/plugins/catalog-node/package.json +++ b/plugins/catalog-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-node", "description": "The plugin-catalog-node module for @backstage/plugin-catalog-backend", - "version": "1.4.7", + "version": "1.4.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog-node/src/alpha.ts b/plugins/catalog-node/src/alpha.ts index 4fb07bbc25..cec1a0eedb 100644 --- a/plugins/catalog-node/src/alpha.ts +++ b/plugins/catalog-node/src/alpha.ts @@ -17,3 +17,5 @@ export { catalogServiceRef } from './catalogService'; export type { CatalogProcessingExtensionPoint } from './extensions'; export { catalogProcessingExtensionPoint } from './extensions'; +export type { CatalogAnalysisExtensionPoint } from './extensions'; +export { catalogAnalysisExtensionPoint } from './extensions'; diff --git a/plugins/catalog-node/src/extensions.ts b/plugins/catalog-node/src/extensions.ts index 5225ad7545..247ef142ad 100644 --- a/plugins/catalog-node/src/extensions.ts +++ b/plugins/catalog-node/src/extensions.ts @@ -18,6 +18,7 @@ import { EntityProvider, CatalogProcessor, PlaceholderResolver, + ScmLocationAnalyzer, } from '@backstage/plugin-catalog-node'; /** @@ -40,3 +41,18 @@ export const catalogProcessingExtensionPoint = createExtensionPoint({ id: 'catalog.processing', }); + +/** + * @alpha + */ +export interface CatalogAnalysisExtensionPoint { + addLocationAnalyzer(analyzer: ScmLocationAnalyzer): void; +} + +/** + * @alpha + */ +export const catalogAnalysisExtensionPoint = + createExtensionPoint({ + id: 'catalog.analysis', + }); diff --git a/plugins/catalog-node/src/processing/index.ts b/plugins/catalog-node/src/processing/index.ts index 40cee63e25..8cffac6961 100644 --- a/plugins/catalog-node/src/processing/index.ts +++ b/plugins/catalog-node/src/processing/index.ts @@ -15,9 +15,11 @@ */ export type { + AnalyzeOptions, DeferredEntity, PlaceholderResolver, PlaceholderResolverParams, PlaceholderResolverRead, PlaceholderResolverResolveUrl, + ScmLocationAnalyzer, } from './types'; diff --git a/plugins/catalog-node/src/processing/types.ts b/plugins/catalog-node/src/processing/types.ts index b98e2da1b7..b6785c1b91 100644 --- a/plugins/catalog-node/src/processing/types.ts +++ b/plugins/catalog-node/src/processing/types.ts @@ -15,6 +15,7 @@ */ import { Entity } from '@backstage/catalog-model'; +import { AnalyzeLocationExistingEntity } from '@backstage/plugin-catalog-common'; import { JsonValue } from '@backstage/types'; import { CatalogProcessorEmit } from '../api'; @@ -50,3 +51,20 @@ export type PlaceholderResolverParams = { export type PlaceholderResolver = ( params: PlaceholderResolverParams, ) => Promise; + +/** @public */ +export type AnalyzeOptions = { + url: string; + catalogFilename?: string; +}; + +/** @public */ +export type ScmLocationAnalyzer = { + /** The method that decides if this analyzer can work with the provided url */ + supports(url: string): boolean; + /** This function can return an array of already existing entities */ + analyze(options: AnalyzeOptions): Promise<{ + /** Existing entities in the analyzed location */ + existing: AnalyzeLocationExistingEntity[]; + }>; +}; diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index 9d6dbd2840..c8630a80ee 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-catalog-react +## 1.9.0-next.0 + +### Minor Changes + +- 1e5b7d993a: Added an `EntityPresentationApi` and associated `entityPresentationApiRef`. This + API lets you control how references to entities (e.g. in links, headings, + iconography etc) are represented in the user interface. + + Usage of this API is initially added to the `EntityRefLink` and `EntityRefLinks` + components, so that they can render richer, more correct representation of + entity refs. There's also a new `EntityDisplayName` component, which works just like + the `EntityRefLink` but without the link. + + Along with that change, the `fetchEntities` and `getTitle` props of + `EntityRefLinksProps` are deprecated and no longer used, since the same need + instead is fulfilled (and by default always enabled) by the + `entityPresentationApiRef`. + +- 1fd53fa0c6: The `UserListPicker` component has undergone improvements to enhance its performance. + + The previous implementation inferred the number of owned and starred entities based on the entities available in the `EntityListContext`. The updated version no longer relies on the `EntityListContext` for inference, allowing for better decoupling. + + The component now loads the entities' count asynchronously, resulting in improved performance and responsiveness. For this purpose, some of the exported filters such as `EntityTagFilter`, `EntityOwnerFilter`, `EntityLifecycleFilter` and `EntityNamespaceFilter` have now the `getCatalogFilters` method implemented. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added new APIs at the `/alpha` subpath for creating entity page cards and content for the new frontend system. +- 71c97e7d73: The `spec.type` field in entities will now always be rendered as a string. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + ## 1.8.5 ### Patch Changes diff --git a/plugins/catalog-react/alpha-api-report.md b/plugins/catalog-react/alpha-api-report.md index f9b740c07c..ed851f810a 100644 --- a/plugins/catalog-react/alpha-api-report.md +++ b/plugins/catalog-react/alpha-api-report.md @@ -3,8 +3,80 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// + +import { AnyExtensionInputMap } from '@backstage/frontend-plugin-api'; +import { ConfigurableExtensionDataRef } from '@backstage/frontend-plugin-api'; import { Entity } from '@backstage/catalog-model'; +import { Extension } from '@backstage/frontend-plugin-api'; +import { ExtensionInputValues } from '@backstage/frontend-plugin-api'; import { ResourcePermission } from '@backstage/plugin-permission-common'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +export function createEntityCardExtension< + TInputs extends AnyExtensionInputMap, +>(options: { + id: string; + attachTo?: { + id: string; + input: string; + }; + disabled?: boolean; + inputs?: TInputs; + filter?: (ctx: { entity: Entity }) => boolean; + loader: (options: { + inputs: Expand>; + }) => Promise; +}): Extension<{ + filter?: + | { + isKind?: string | undefined; + isType?: string | undefined; + }[] + | undefined; +}>; + +// @alpha (undocumented) +export function createEntityContentExtension< + TInputs extends AnyExtensionInputMap, +>(options: { + id: string; + attachTo?: { + id: string; + input: string; + }; + disabled?: boolean; + inputs?: TInputs; + routeRef?: RouteRef; + defaultPath: string; + defaultTitle: string; + filter?: (ctx: { entity: Entity }) => boolean; + loader: (options: { + inputs: Expand>; + }) => Promise; +}): Extension<{ + title: string; + path: string; + filter?: + | { + isKind?: string | undefined; + isType?: string | undefined; + }[] + | undefined; +}>; + +// @alpha (undocumented) +export const entityContentTitleExtensionDataRef: ConfigurableExtensionDataRef< + string, + {} +>; + +// @alpha (undocumented) +export const entityFilterExtensionDataRef: ConfigurableExtensionDataRef< + (ctx: { entity: Entity }) => boolean, + {} +>; // @alpha export function isOwnerOf(owner: Entity, entity: Entity): boolean; diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index a72df6bb3d..e2000b5658 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -3,6 +3,8 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// + import { ApiRef } from '@backstage/core-plugin-api'; import { CATALOG_FILTER_EXISTS } from '@backstage/catalog-client'; import { CatalogApi } from '@backstage/catalog-client'; @@ -11,6 +13,7 @@ import { ComponentProps } from 'react'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import { IconButton } from '@material-ui/core'; +import { IconComponent } from '@backstage/core-plugin-api'; import { InfoCardVariants } from '@backstage/core-components'; import { LinkProps } from '@backstage/core-components'; import { Observable } from '@backstage/types'; @@ -84,6 +87,7 @@ export const CatalogFilterLayout: { // @public (undocumented) export type CatalogReactComponentsNameToClassKey = { CatalogReactUserListPicker: CatalogReactUserListPickerClassKey; + CatalogReactEntityDisplayName: CatalogReactEntityDisplayNameClassKey; CatalogReactEntityLifecyclePicker: CatalogReactEntityLifecyclePickerClassKey; CatalogReactEntitySearchBar: CatalogReactEntitySearchBarClassKey; CatalogReactEntityTagPicker: CatalogReactEntityTagPickerClassKey; @@ -91,6 +95,9 @@ export type CatalogReactComponentsNameToClassKey = { CatalogReactEntityProcessingStatusPicker: CatalogReactEntityProcessingStatusPickerClassKey; }; +// @public +export type CatalogReactEntityDisplayNameClassKey = 'root' | 'icon'; + // @public (undocumented) export type CatalogReactEntityLifecyclePickerClassKey = 'input'; @@ -142,7 +149,7 @@ export const columnFactories: Readonly<{ export type DefaultEntityFilters = { kind?: EntityKindFilter; type?: EntityTypeFilter; - user?: UserListFilter; + user?: UserListFilter | EntityUserFilter; owners?: EntityOwnerFilter; lifecycles?: EntityLifecycleFilter; tags?: EntityTagFilter; @@ -152,6 +159,15 @@ export type DefaultEntityFilters = { namespace?: EntityNamespaceFilter; }; +// @public +export function defaultEntityPresentation( + entityOrRef: Entity | CompoundEntityRef | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, +): EntityRefPresentationSnapshot; + // @public (undocumented) export function EntityAutocompletePicker< T extends DefaultEntityFilters = DefaultEntityFilters, @@ -174,6 +190,18 @@ export type EntityAutocompletePickerProps< initialSelectedOptions?: string[]; }; +// @public +export const EntityDisplayName: (props: EntityDisplayNameProps) => JSX.Element; + +// @public +export type EntityDisplayNameProps = { + entityRef: Entity | CompoundEntityRef | string; + noIcon?: boolean; + noTooltip?: boolean; + defaultKind?: string; + defaultNamespace?: string; +}; + // @public export class EntityErrorFilter implements EntityFilter { constructor(value: boolean); @@ -224,6 +252,8 @@ export class EntityLifecycleFilter implements EntityFilter { // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) + getCatalogFilters(): Record; + // (undocumented) toQueryValue(): string[]; // (undocumented) readonly values: string[]; @@ -275,6 +305,8 @@ export class EntityNamespaceFilter implements EntityFilter { // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) + getCatalogFilters(): Record; + // (undocumented) toQueryValue(): string[]; // (undocumented) readonly values: string[]; @@ -289,6 +321,8 @@ export class EntityOrphanFilter implements EntityFilter { // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) + getCatalogFilters(): Record; + // (undocumented) readonly value: boolean; } @@ -297,6 +331,8 @@ export class EntityOwnerFilter implements EntityFilter { constructor(values: string[]); // (undocumented) filterEntity(entity: Entity): boolean; + // (undocumented) + getCatalogFilters(): Record; toQueryValue(): string[]; // (undocumented) readonly values: string[]; @@ -323,6 +359,20 @@ export type EntityPeekAheadPopoverProps = PropsWithChildren<{ delayTime?: number; }>; +// @public +export interface EntityPresentationApi { + forEntity( + entityOrRef: Entity | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, + ): EntityRefPresentation; +} + +// @public +export const entityPresentationApiRef: ApiRef; + // @public (undocumented) export const EntityProcessingStatusPicker: () => React_2.JSX.Element; @@ -346,6 +396,7 @@ export const EntityRefLink: (props: EntityRefLinkProps) => JSX.Element; export type EntityRefLinkProps = { entityRef: Entity | CompoundEntityRef | string; defaultKind?: string; + defaultNamespace?: string; title?: string; children?: React_2.ReactNode; } & Omit; @@ -358,21 +409,26 @@ export function EntityRefLinks< // @public export type EntityRefLinksProps< TRef extends string | CompoundEntityRef | Entity, -> = ( - | { - defaultKind?: string; - entityRefs: TRef[]; - fetchEntities?: false; - getTitle?(entity: TRef): string | undefined; - } - | { - defaultKind?: string; - entityRefs: TRef[]; - fetchEntities: true; - getTitle(entity: Entity): string | undefined; - } -) & - Omit; +> = { + defaultKind?: string; + entityRefs: TRef[]; + fetchEntities?: boolean; + getTitle?(entity: TRef): string | undefined; +} & Omit; + +// @public +export interface EntityRefPresentation { + snapshot: EntityRefPresentationSnapshot; + update$?: Observable; +} + +// @public +export interface EntityRefPresentationSnapshot { + entityRef: string; + Icon?: IconComponent | undefined | false; + primaryTitle: string; + secondaryTitle?: string; +} // @public export function entityRouteParams(entity: Entity): { @@ -447,6 +503,8 @@ export class EntityTagFilter implements EntityFilter { // (undocumented) filterEntity(entity: Entity): boolean; // (undocumented) + getCatalogFilters(): Record; + // (undocumented) toQueryValue(): string[]; // (undocumented) readonly values: string[]; @@ -497,6 +555,26 @@ export interface EntityTypePickerProps { initialFilter?: string; } +// @public (undocumented) +export class EntityUserFilter implements EntityFilter { + // (undocumented) + static all(): EntityUserFilter; + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + getCatalogFilters(): Record; + // (undocumented) + static owned(ownershipEntityRefs: string[]): EntityUserFilter; + // (undocumented) + readonly refs?: string[] | undefined; + // (undocumented) + static starred(starredEntityRefs: string[]): EntityUserFilter; + // (undocumented) + toQueryValue(): string; + // (undocumented) + readonly value: UserListFilterKind; +} + // @public export const FavoriteEntity: ( props: FavoriteEntityProps, @@ -598,6 +676,15 @@ export function useEntityOwnership(): { isOwnedEntity: (entity: Entity) => boolean; }; +// @public +export function useEntityPresentation( + entityOrRef: Entity | CompoundEntityRef | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, +): EntityRefPresentationSnapshot; + // @public export function useEntityTypeFilter(): { loading: boolean; @@ -620,7 +707,7 @@ export function useRelatedEntities( error: Error | undefined; }; -// @public +// @public @deprecated export class UserListFilter implements EntityFilter { constructor( value: UserListFilterKind, diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index bb18160595..6f156beea4 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog-react", "description": "A frontend library that helps other Backstage plugins interact with the catalog", - "version": "1.8.5", + "version": "1.9.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -10,13 +10,13 @@ }, "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.ts", + "./alpha": "./src/alpha.tsx", "./package.json": "./package.json" }, "typesVersions": { "*": { "alpha": [ - "src/alpha.ts" + "src/alpha.tsx" ], "package.json": [ "package.json" @@ -51,6 +51,7 @@ "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/integration": "workspace:^", "@backstage/plugin-catalog-common": "workspace:^", "@backstage/plugin-permission-common": "workspace:^", diff --git a/plugins/catalog-react/src/alpha.tsx b/plugins/catalog-react/src/alpha.tsx new file mode 100644 index 0000000000..8d0436e4be --- /dev/null +++ b/plugins/catalog-react/src/alpha.tsx @@ -0,0 +1,202 @@ +/* + * 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 React, { lazy } from 'react'; +import { + AnyExtensionInputMap, + ExtensionBoundary, + ExtensionInputValues, + RouteRef, + coreExtensionData, + createExtension, + createExtensionDataRef, + createSchemaFromZod, +} from '@backstage/frontend-plugin-api'; +// eslint-disable-next-line @backstage/no-relative-monorepo-imports +import { Expand } from '../../../packages/frontend-plugin-api/src/types'; +import { Entity } from '@backstage/catalog-model'; + +export { isOwnerOf } from './utils'; +export { useEntityPermission } from './hooks/useEntityPermission'; + +/** @alpha */ +export const entityContentTitleExtensionDataRef = + createExtensionDataRef('plugin.catalog.entity.content.title'); + +/** @alpha */ +export const entityFilterExtensionDataRef = createExtensionDataRef< + (ctx: { entity: Entity }) => boolean +>('plugin.catalog.entity.filter'); + +function applyFilter(a?: string, b?: string): boolean { + if (!a) { + return true; + } + return a.toLocaleLowerCase('en-US') === b?.toLocaleLowerCase('en-US'); +} + +// TODO: Only two hardcoded isKind and isType filters are available for now +// This is just an initial config filter implementation and needs to be revisited +function buildFilter( + config: { filter?: { isKind?: string; isType?: string }[] }, + filterFunc?: (ctx: { entity: Entity }) => boolean, +) { + return (ctx: { entity: Entity }) => { + const configuredFilterMatch = config.filter?.some(filter => { + const kindMatch = applyFilter(filter.isKind, ctx.entity.kind); + const typeMatch = applyFilter( + filter.isType, + ctx.entity.spec?.type?.toString(), + ); + return kindMatch && typeMatch; + }); + if (configuredFilterMatch) { + return true; + } + if (filterFunc) { + return filterFunc(ctx); + } + return true; + }; +} + +// TODO: Figure out how to merge with provided config schema +/** @alpha */ +export function createEntityCardExtension< + TInputs extends AnyExtensionInputMap, +>(options: { + id: string; + attachTo?: { id: string; input: string }; + disabled?: boolean; + inputs?: TInputs; + filter?: (ctx: { entity: Entity }) => boolean; + loader: (options: { + inputs: Expand>; + }) => Promise; +}) { + const id = `entity.cards.${options.id}`; + + return createExtension({ + id, + attachTo: options.attachTo ?? { + id: 'entity.content.overview', + input: 'cards', + }, + disabled: options.disabled ?? true, + output: { + element: coreExtensionData.reactElement, + filter: entityFilterExtensionDataRef, + }, + inputs: options.inputs, + configSchema: createSchemaFromZod(z => + z.object({ + filter: z + .array( + z.object({ + isKind: z.string().optional(), + isType: z.string().optional(), + }), + ) + .optional(), + }), + ), + factory({ config, inputs, source }) { + const ExtensionComponent = lazy(() => + options + .loader({ inputs }) + .then(element => ({ default: () => element })), + ); + + return { + element: ( + + + + ), + filter: buildFilter(config, options.filter), + }; + }, + }); +} + +/** @alpha */ +export function createEntityContentExtension< + TInputs extends AnyExtensionInputMap, +>(options: { + id: string; + attachTo?: { id: string; input: string }; + disabled?: boolean; + inputs?: TInputs; + routeRef?: RouteRef; + defaultPath: string; + defaultTitle: string; + filter?: (ctx: { entity: Entity }) => boolean; + loader: (options: { + inputs: Expand>; + }) => Promise; +}) { + const id = `entity.content.${options.id}`; + + return createExtension({ + id, + attachTo: options.attachTo ?? { + id: 'plugin.catalog.page.entity', + input: 'contents', + }, + disabled: options.disabled ?? true, + output: { + element: coreExtensionData.reactElement, + path: coreExtensionData.routePath, + routeRef: coreExtensionData.routeRef.optional(), + title: entityContentTitleExtensionDataRef, + filter: entityFilterExtensionDataRef, + }, + inputs: options.inputs, + configSchema: createSchemaFromZod(z => + z.object({ + path: z.string().default(options.defaultPath), + title: z.string().default(options.defaultTitle), + filter: z + .array( + z.object({ + isKind: z.string().optional(), + isType: z.string().optional(), + }), + ) + .optional(), + }), + ), + factory({ config, inputs, source }) { + const ExtensionComponent = lazy(() => + options + .loader({ inputs }) + .then(element => ({ default: () => element })), + ); + + return { + path: config.path, + title: config.title, + routeRef: options.routeRef, + element: ( + + + + ), + filter: buildFilter(config, options.filter), + }; + }, + }); +} diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts new file mode 100644 index 0000000000..945bce1d27 --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/EntityPresentationApi.ts @@ -0,0 +1,141 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { + ApiRef, + IconComponent, + createApiRef, +} from '@backstage/core-plugin-api'; +import { Observable } from '@backstage/types'; + +/** + * An API that handles how to represent entities in the interface. + * + * @public + */ +export const entityPresentationApiRef: ApiRef = + createApiRef({ + id: 'plugin.catalog.entity-presentation', + }); + +/** + * The visual presentation of an entity reference at some point in time. + * + * @public + */ +export interface EntityRefPresentationSnapshot { + /** + * The ref to the entity that this snapshot represents. + * + * @remarks + * + * Note that when the input data was broken or had missing vital pieces of + * information, this string may contain placeholders such as "unknown". You + * can therefore not necessarily assume that the ref is completely valid and + * usable for example for forming a clickable link to the entity. + */ + entityRef: string; + /** + * A string that can be used as a plain representation of the entity, for + * example in a header or a link. + * + * @remarks + * + * The title may be short and not contain all of the information that the + * entity holds. When rendering the primary title, you may also want to + * make sure to add more contextual information nearby such as the icon or + * secondary title, since the primary could for example just be the + * `metadata.name` of the entity which might be ambiguous to the reader. + */ + primaryTitle: string; + /** + * Optionally, some additional textual information about the entity, to be + * used as a clarification on top of the primary title. + * + * @remarks + * + * This text can for example be rendered in a tooltip or be used as a + * subtitle. It may not be sufficient to display on its own; it should + * typically be used in conjunction with the primary title. It can contain + * such information as the entity ref and/or a `spec.type` etc. + */ + secondaryTitle?: string; + /** + * Optionally, an icon that represents the kind/type of entity. + * + * @remarks + * + * This icon should ideally be easily recognizable as the kind of entity, and + * be used consistently throughout the Backstage interface. It can be rendered + * both in larger formats such as in a header, or in smaller formats such as + * inline with regular text, so bear in mind that the legibility should be + * high in both cases. + * + * A value of `false` here indicates the desire to not have an icon present + * for the given implementation. A value of `undefined` leaves it at the + * discretion of the display layer to choose what to do (such as for example + * showing a fallback icon). + */ + Icon?: IconComponent | undefined | false; +} + +/** + * The visual presentation of an entity reference. + * + * @public + */ +export interface EntityRefPresentation { + /** + * The representation that's suitable to use for this entity right now. + */ + snapshot: EntityRefPresentationSnapshot; + /** + * Some presentation implementations support emitting updated snapshots over + * time, for example after retrieving additional data from the catalog or + * elsewhere. + */ + update$?: Observable; +} + +/** + * An API that decides how to visually represent entities in the interface. + * + * @remarks + * + * Most consumers will want to use the {@link useEntityPresentation} hook + * instead of this interface directly. + * + * @public + */ +export interface EntityPresentationApi { + /** + * Fetches the presentation for an entity. + * + * @param entityOrRef - Either an entity, or a string ref to it. If you pass + * in an entity, it is assumed that it is not a partial one - i.e. only pass + * in an entity if you know that it was fetched in such a way that it + * contains all of the fields that the representation renderer needs. + * @param context - Contextual information that may affect the presentation. + */ + forEntity( + entityOrRef: Entity | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, + ): EntityRefPresentation; +} diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts new file mode 100644 index 0000000000..ea755dfebc --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.test.ts @@ -0,0 +1,260 @@ +/* + * 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 { CompoundEntityRef, Entity } from '@backstage/catalog-model'; +import { defaultEntityPresentation } from './defaultEntityPresentation'; + +describe('defaultEntityPresentation', () => { + describe('entity given', () => { + it('happy path', () => { + expect( + defaultEntityPresentation({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + description: 'desc', + }, + spec: { + type: 'type', + }, + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test | type | desc', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + title: 'title', + description: 'desc', + }, + spec: { + type: 'type', + }, + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'title', + secondaryTitle: 'component:default/test | type | desc', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation({ + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + title: 'title', + description: 'desc', + }, + spec: { + type: 'type', + profile: { + displayName: 'displayName', + }, + }, + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'displayName', + secondaryTitle: 'component:default/test | type | desc', + Icon: expect.anything(), + }); + }); + + it('handles the absolute minimum', () => { + expect( + defaultEntityPresentation({ + kind: 'Component', + metadata: { name: 'test' }, + } as Entity), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + }); + + it('fails without throwing on malformed entities', () => { + expect( + defaultEntityPresentation({ metadata: 7 } as unknown as Entity), + ).toEqual({ + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }); + }); + }); + + describe('string ref given', () => { + it('happy path', () => { + expect(defaultEntityPresentation('component:default/test')).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation('component:default/test', { + defaultKind: 'X', + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'component:test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation('component:default/test', { + defaultNamespace: 'X', + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'default/test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + }); + + it('works without throwing on malformed and shortened refs', () => { + expect(defaultEntityPresentation('')).toEqual({ + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }); + + expect(defaultEntityPresentation('name')).toEqual({ + entityRef: 'unknown:default/name', + primaryTitle: 'name', + secondaryTitle: 'unknown:default/name', + Icon: expect.anything(), + }); + }); + }); + + describe('compound ref given', () => { + it('happy path', () => { + expect( + defaultEntityPresentation({ + kind: 'Component', + namespace: 'default', + name: 'test', + }), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation( + { kind: 'component', namespace: 'default', name: 'test' }, + { + defaultKind: 'X', + }, + ), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'component:test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + + expect( + defaultEntityPresentation( + { kind: 'component', namespace: 'default', name: 'test' }, + { + defaultNamespace: 'X', + }, + ), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'default/test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + }); + + it('works without throwing on malformed refs', () => { + expect( + defaultEntityPresentation( + { kind: 'component', name: 'test' } as CompoundEntityRef, + { + defaultNamespace: 'X', + }, + ), + ).toEqual({ + entityRef: 'component:default/test', + primaryTitle: 'default/test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }); + + expect(defaultEntityPresentation('')).toEqual({ + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }); + }); + }); + + describe('entirely invalid input type given', () => { + it('sad path', () => { + expect(defaultEntityPresentation(null as unknown as Entity)).toEqual({ + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }); + + expect(defaultEntityPresentation(undefined as unknown as Entity)).toEqual( + { + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }, + ); + + expect( + defaultEntityPresentation(Symbol.for('Prince') as unknown as Entity), + ).toEqual({ + entityRef: 'unknown:default/unknown', + primaryTitle: 'unknown', + secondaryTitle: 'unknown:default/unknown', + Icon: expect.anything(), + }); + }); + }); +}); diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts new file mode 100644 index 0000000000..eacb659062 --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/defaultEntityPresentation.ts @@ -0,0 +1,197 @@ +/* + * 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 { + CompoundEntityRef, + DEFAULT_NAMESPACE, + Entity, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { IconComponent } from '@backstage/core-plugin-api'; +import ApartmentIcon from '@material-ui/icons/Apartment'; +import BusinessIcon from '@material-ui/icons/Business'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HelpIcon from '@material-ui/icons/Help'; +import LibraryAddIcon from '@material-ui/icons/LibraryAdd'; +import LocationOnIcon from '@material-ui/icons/LocationOn'; +import MemoryIcon from '@material-ui/icons/Memory'; +import PeopleIcon from '@material-ui/icons/People'; +import PersonIcon from '@material-ui/icons/Person'; +import get from 'lodash/get'; +import { EntityRefPresentationSnapshot } from './EntityPresentationApi'; + +const UNKNOWN_KIND_ICON: IconComponent = HelpIcon; + +const DEFAULT_ICONS: Record = { + api: ExtensionIcon, + component: MemoryIcon, + system: BusinessIcon, + domain: ApartmentIcon, + location: LocationOnIcon, + user: PersonIcon, + group: PeopleIcon, + template: LibraryAddIcon, +}; + +/** + * This returns the default representation of an entity. + * + * @public + * @param entityOrRef - Either an entity, or a ref to it. + * @param context - Contextual information that may affect the presentation. + */ +export function defaultEntityPresentation( + entityOrRef: Entity | CompoundEntityRef | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, +): EntityRefPresentationSnapshot { + // NOTE(freben): This code may look convoluted, but it tries its very best to + // be defensive and handling any type of malformed input and still producing + // some form of result without crashing. + const { kind, namespace, name, title, description, displayName, type } = + getParts(entityOrRef); + + const Icon = + (kind && DEFAULT_ICONS[kind.toLocaleLowerCase('en-US')]) || + UNKNOWN_KIND_ICON; + + const entityRef: string = stringifyEntityRef({ + kind: kind || 'unknown', + namespace: namespace || DEFAULT_NAMESPACE, + name: name || 'unknown', + }); + + const shortRef = getShortRef({ kind, namespace, name, context }); + + const primary = [displayName, title, shortRef].find( + candidate => candidate && typeof candidate === 'string', + )!; + + const secondary = [ + primary !== entityRef ? entityRef : undefined, + type, + description, + ] + .filter(candidate => candidate && typeof candidate === 'string') + .join(' | '); + + return { + entityRef, + primaryTitle: primary, + secondaryTitle: secondary || undefined, + Icon, + }; +} + +// Try to extract display-worthy parts of an entity or ref as best we can, without throwing +function getParts(entityOrRef: Entity | CompoundEntityRef | string): { + kind?: string; + namespace?: string; + name?: string; + title?: string; + description?: string; + displayName?: string; + type?: string; +} { + if (typeof entityOrRef === 'string') { + let colonI = entityOrRef.indexOf(':'); + const slashI = entityOrRef.indexOf('/'); + + // If the / is ahead of the :, treat the rest as the name + if (slashI !== -1 && slashI < colonI) { + colonI = -1; + } + + const kind = colonI === -1 ? undefined : entityOrRef.slice(0, colonI); + const namespace = + slashI === -1 ? undefined : entityOrRef.slice(colonI + 1, slashI); + const name = entityOrRef.slice(Math.max(colonI + 1, slashI + 1)); + + return { kind, namespace, name }; + } + + if (typeof entityOrRef === 'object' && entityOrRef !== null) { + const kind = [get(entityOrRef, 'kind')].find( + candidate => candidate && typeof candidate === 'string', + ); + + const namespace = [ + get(entityOrRef, 'metadata.namespace'), + get(entityOrRef, 'namespace'), + ].find(candidate => candidate && typeof candidate === 'string'); + + const name = [ + get(entityOrRef, 'metadata.name'), + get(entityOrRef, 'name'), + ].find(candidate => candidate && typeof candidate === 'string'); + + const title = [get(entityOrRef, 'metadata.title')].find( + candidate => candidate && typeof candidate === 'string', + ); + + const description = [get(entityOrRef, 'metadata.description')].find( + candidate => candidate && typeof candidate === 'string', + ); + + const displayName = [get(entityOrRef, 'spec.profile.displayName')].find( + candidate => candidate && typeof candidate === 'string', + ); + + const type = [get(entityOrRef, 'spec.type')].find( + candidate => candidate && typeof candidate === 'string', + ); + + return { kind, namespace, name, title, description, displayName, type }; + } + + return {}; +} + +function getShortRef(options: { + kind?: string; + namespace?: string; + name?: string; + context?: { defaultKind?: string; defaultNamespace?: string }; +}): string { + const kind = options.kind?.toLocaleLowerCase('en-US') || 'unknown'; + const namespace = options.namespace || DEFAULT_NAMESPACE; + const name = options.name || 'unknown'; + const defaultKindLower = + options.context?.defaultKind?.toLocaleLowerCase('en-US'); + const defaultNamespaceLower = + options.context?.defaultNamespace?.toLocaleLowerCase('en-US'); + + let result = name; + + if ( + (defaultNamespaceLower && + namespace.toLocaleLowerCase('en-US') !== defaultNamespaceLower) || + namespace !== DEFAULT_NAMESPACE + ) { + result = `${namespace}/${result}`; + } + + if ( + defaultKindLower && + kind.toLocaleLowerCase('en-US') !== defaultKindLower + ) { + result = `${kind}:${result}`; + } + + return result; +} diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/index.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/index.ts new file mode 100644 index 0000000000..405afe23f6 --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/index.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + entityPresentationApiRef, + type EntityPresentationApi, + type EntityRefPresentation, + type EntityRefPresentationSnapshot, +} from './EntityPresentationApi'; +export { defaultEntityPresentation } from './defaultEntityPresentation'; +export { useEntityPresentation } from './useEntityPresentation'; diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/useEntityPresentation.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/useEntityPresentation.ts new file mode 100644 index 0000000000..bae3936cc8 --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/useEntityPresentation.ts @@ -0,0 +1,86 @@ +/* + * 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 { + CompoundEntityRef, + Entity, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { useApiHolder } from '@backstage/core-plugin-api'; +import { useMemo } from 'react'; +import { + EntityRefPresentation, + EntityRefPresentationSnapshot, + entityPresentationApiRef, +} from './EntityPresentationApi'; +import { defaultEntityPresentation } from './defaultEntityPresentation'; +import { useUpdatingObservable } from './useUpdatingObservable'; + +/** + * Returns information about how to represent an entity in the interface. + * + * @public + * @param entityOrRef - The entity to represent, or an entity ref to it. If you + * pass in an entity, it is assumed that it is NOT a partial one - i.e. only + * pass in an entity if you know that it was fetched in such a way that it + * contains all of the fields that the representation renderer needs. + * @param context - Optional context that control details of the presentation. + * @returns A snapshot of the entity presentation, which may change over time + */ +export function useEntityPresentation( + entityOrRef: Entity | CompoundEntityRef | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, +): EntityRefPresentationSnapshot { + // Defensively allow for a missing presentation API, which makes this hook + // safe to use in tests. + const apis = useApiHolder(); + const entityPresentationApi = apis.get(entityPresentationApiRef); + + const deps = [ + entityPresentationApi, + JSON.stringify(entityOrRef), + JSON.stringify(context || null), + ]; + + const presentation = useMemo( + () => { + if (!entityPresentationApi) { + return { snapshot: defaultEntityPresentation(entityOrRef, context) }; + } + + return entityPresentationApi.forEntity( + typeof entityOrRef === 'string' || 'metadata' in entityOrRef + ? entityOrRef + : stringifyEntityRef(entityOrRef), + context, + ); + }, + // eslint-disable-next-line react-hooks/exhaustive-deps + deps, + ); + + // NOTE(freben): We intentionally do not use the plain useObservable from the + // react-use library here. That hook does not support a dependencies array, + // and also it only subscribes once to the initially passed in observable and + // won't properly react when either initial value or the actual observable + // changes. + return useUpdatingObservable(presentation.snapshot, presentation.update$, [ + presentation, + ]); +} diff --git a/plugins/catalog-react/src/apis/EntityPresentationApi/useUpdatingObservable.ts b/plugins/catalog-react/src/apis/EntityPresentationApi/useUpdatingObservable.ts new file mode 100644 index 0000000000..a15119d177 --- /dev/null +++ b/plugins/catalog-react/src/apis/EntityPresentationApi/useUpdatingObservable.ts @@ -0,0 +1,60 @@ +/* + * 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 { Observable } from '@backstage/types'; +import { DependencyList, useEffect, useState } from 'react'; + +/** + * Subscribe to an observable and return the latest value from it. + * + * @remarks + * + * This implementation differs in a few important ways from the plain + * useObservable from the react-use library. That hook does not support a + * dependencies array, and also it only subscribes once to the initially passed + * in observable and won't properly react when either initial value or the + * actual observable changes. + * + * This hook will ensure to resubscribe and reconsider the initial value, + * whenever the dependencies change. + */ +export function useUpdatingObservable( + value: T, + observable: Observable | undefined, + deps: DependencyList, +): T { + const [snapshot, setSnapshot] = useState(value); + + useEffect(() => { + setSnapshot(value); + + const subscription = observable?.subscribe({ + next: updatedValue => { + setSnapshot(updatedValue); + }, + complete: () => { + subscription?.unsubscribe(); + }, + }); + + return () => { + subscription?.unsubscribe(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, deps); + + return snapshot; +} diff --git a/plugins/catalog-react/src/apis/index.ts b/plugins/catalog-react/src/apis/index.ts index 5c7e980890..271d749afc 100644 --- a/plugins/catalog-react/src/apis/index.ts +++ b/plugins/catalog-react/src/apis/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ +export * from './EntityPresentationApi'; export * from './StarredEntitiesApi'; diff --git a/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.stories.tsx b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.stories.tsx new file mode 100644 index 0000000000..d68618b07c --- /dev/null +++ b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.stories.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React, { ComponentType } from 'react'; +import { EntityDisplayName, EntityDisplayNameProps } from './EntityDisplayName'; +import { wrapInTestApp } from '@backstage/test-utils'; + +const defaultArgs = { + entityRef: 'component:default/playback', +}; + +export default { + title: 'Catalog /EntityDisplayName', + decorators: [(Story: ComponentType<{}>) => wrapInTestApp()], +}; + +export const Default = (args: EntityDisplayNameProps) => ( + +); +Default.args = defaultArgs; diff --git a/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.test.tsx b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.test.tsx new file mode 100644 index 0000000000..573cfa9011 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.test.tsx @@ -0,0 +1,95 @@ +/* + * 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 { TestApiProvider, renderInTestApp } from '@backstage/test-utils'; +import { screen } from '@testing-library/react'; +import React from 'react'; +import ObservableImpl from 'zen-observable'; +import { + EntityRefPresentation, + EntityRefPresentationSnapshot, + entityPresentationApiRef, +} from '../../apis'; +import { EntityDisplayName } from './EntityDisplayName'; + +function defer() { + let resolve = (_value: T) => {}; + const promise = new Promise(_resolve => { + resolve = _resolve; + }); + return { promise, resolve }; +} + +describe('', () => { + const entityPresentationApi = { + forEntity: jest.fn(), + }; + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('works with the sync the happy path', async () => { + entityPresentationApi.forEntity.mockReturnValue({ + snapshot: { + entityRef: 'component:default/foo', + primaryTitle: 'foo', + }, + update$: undefined, + } as EntityRefPresentation); + + await renderInTestApp( + + + , + ); + + expect(screen.getByText('foo')).toBeInTheDocument(); + }); + + it('works with the async the happy path', async () => { + const { promise, resolve } = defer(); + + entityPresentationApi.forEntity.mockReturnValue({ + snapshot: { + entityRef: 'component:default/foo', + primaryTitle: 'foo', + }, + update$: new ObservableImpl(subscriber => { + promise.then(value => subscriber.next(value)); + }), + } as EntityRefPresentation); + + await renderInTestApp( + + + , + ); + + expect(screen.getByText('foo')).toBeInTheDocument(); + + resolve({ + entityRef: 'component:default/foo', + primaryTitle: 'bar', + }); + + await expect(screen.findByText('bar')).resolves.toBeInTheDocument(); + }); +}); diff --git a/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.tsx b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.tsx new file mode 100644 index 0000000000..6bdbe83a84 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityDisplayName/EntityDisplayName.tsx @@ -0,0 +1,99 @@ +/* + * 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 { CompoundEntityRef, Entity } from '@backstage/catalog-model'; +import { Box, Theme, Tooltip, makeStyles } from '@material-ui/core'; +import React from 'react'; +import { useEntityPresentation } from '../../apis'; + +/** + * The available style class keys for {@link EntityDisplayName}, under the name + * "CatalogReactEntityDisplayName". + * + * @public + */ +export type CatalogReactEntityDisplayNameClassKey = 'root' | 'icon'; + +const useStyles = makeStyles( + (theme: Theme) => ({ + root: { + display: 'inline-flex', + alignItems: 'center', + }, + icon: { + marginLeft: theme.spacing(0.5), + color: theme.palette.text.secondary, + lineHeight: 0, + }, + }), + { name: 'CatalogReactEntityDisplayName' }, +); + +/** + * Props for {@link EntityDisplayName}. + * + * @public + */ +export type EntityDisplayNameProps = { + entityRef: Entity | CompoundEntityRef | string; + noIcon?: boolean; + noTooltip?: boolean; + defaultKind?: string; + defaultNamespace?: string; +}; + +/** + * Shows a nice representation of a reference to an entity. + * + * @public + */ +export const EntityDisplayName = ( + props: EntityDisplayNameProps, +): JSX.Element => { + const { entityRef, noIcon, noTooltip, defaultKind, defaultNamespace } = props; + + const classes = useStyles(); + const { primaryTitle, secondaryTitle, Icon } = useEntityPresentation( + entityRef, + { defaultKind, defaultNamespace }, + ); + + // The innermost "body" content + let content = <>{primaryTitle}; + + // Optionally an icon, and wrapper around them both + content = ( + + {content} + {Icon && !noIcon ? ( + + + + ) : null} + + ); + + // Optionally, a tooltip as the outermost layer + if (secondaryTitle && !noTooltip) { + content = ( + + {content} + + ); + } + + return content; +}; diff --git a/plugins/catalog-react/src/components/EntityDisplayName/index.ts b/plugins/catalog-react/src/components/EntityDisplayName/index.ts new file mode 100644 index 0000000000..0a9faa6f41 --- /dev/null +++ b/plugins/catalog-react/src/components/EntityDisplayName/index.ts @@ -0,0 +1,21 @@ +/* + * 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. + */ + +export { + EntityDisplayName, + type CatalogReactEntityDisplayNameClassKey, + type EntityDisplayNameProps, +} from './EntityDisplayName'; diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx index cecf27be24..c1a37b9682 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.test.tsx @@ -27,6 +27,7 @@ describe('', () => { kind: 'Component', metadata: { name: 'software', + namespace: 'default', }, spec: { owner: 'guest', @@ -39,8 +40,7 @@ describe('', () => { '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, }); - - expect(screen.getByText('component:software')).toHaveAttribute( + expect(screen.getByText('software').closest('a')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -65,7 +65,7 @@ describe('', () => { '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, }); - expect(screen.getByText('component:test/software')).toHaveAttribute( + expect(screen.getByText('test/software').closest('a')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -93,7 +93,7 @@ describe('', () => { }, }, ); - expect(screen.getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software').closest('a')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -110,7 +110,7 @@ describe('', () => { '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, }); - expect(screen.getByText('component:software')).toHaveAttribute( + expect(screen.getByText('software').closest('a')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -127,7 +127,7 @@ describe('', () => { '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, }); - expect(screen.getByText('component:test/software')).toHaveAttribute( + expect(screen.getByText('test/software').closest('a')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -147,7 +147,7 @@ describe('', () => { }, }, ); - expect(screen.getByText('test/software')).toHaveAttribute( + expect(screen.getByText('test/software').closest('a')).toHaveAttribute( 'href', '/catalog/test/component/software', ); @@ -169,7 +169,7 @@ describe('', () => { }, }, ); - expect(screen.getByText('Custom Children')).toHaveAttribute( + expect(screen.getByText('Custom Children').closest('a')).toHaveAttribute( 'href', '/catalog/test/component/software', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx index d682abdc2b..32627fccb2 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLink.tsx @@ -15,17 +15,16 @@ */ import { - Entity, CompoundEntityRef, DEFAULT_NAMESPACE, + Entity, parseEntityRef, } from '@backstage/catalog-model'; -import React, { forwardRef } from 'react'; -import { entityRouteRef } from '../../routes'; -import { humanizeEntityRef } from './humanize'; import { Link, LinkProps } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; -import { Tooltip } from '@material-ui/core'; +import React, { forwardRef } from 'react'; +import { entityRouteRef } from '../../routes'; +import { EntityDisplayName } from '../EntityDisplayName'; /** * Props for {@link EntityRefLink}. @@ -35,6 +34,8 @@ import { Tooltip } from '@material-ui/core'; export type EntityRefLinkProps = { entityRef: Entity | CompoundEntityRef | string; defaultKind?: string; + defaultNamespace?: string; + /** @deprecated This option should no longer be used; presentation is requested through the {@link entityPresentationApiRef} instead */ title?: string; children?: React.ReactNode; } & Omit; @@ -46,52 +47,68 @@ export type EntityRefLinkProps = { */ export const EntityRefLink = forwardRef( (props, ref) => { - const { entityRef, defaultKind, title, children, ...linkProps } = props; - const entityRoute = useRouteRef(entityRouteRef); + const { + entityRef, + defaultKind, + defaultNamespace, + title, + children, + ...linkProps + } = props; + const entityRoute = useEntityRoute(props.entityRef); - let kind; - let namespace; - let name; - - if (typeof entityRef === 'string') { - const parsed = parseEntityRef(entityRef); - kind = parsed.kind; - namespace = parsed.namespace; - name = parsed.name; - } else if ('metadata' in entityRef) { - kind = entityRef.kind; - namespace = entityRef.metadata.namespace; - name = entityRef.metadata.name; - } else { - kind = entityRef.kind; - namespace = entityRef.namespace; - name = entityRef.name; - } - - kind = kind.toLocaleLowerCase('en-US'); - namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE; - - const routeParams = { - kind: encodeURIComponent(kind), - namespace: encodeURIComponent(namespace), - name: encodeURIComponent(name), - }; - const formattedEntityRefTitle = humanizeEntityRef( - { kind, namespace, name }, - { defaultKind }, + const content = children ?? title ?? ( + ); - const link = ( - - {children} - {!children && (title ?? formattedEntityRefTitle)} + return ( + + {content} ); - - return title ? ( - {link} - ) : ( - link - ); }, ) as (props: EntityRefLinkProps) => JSX.Element; + +// Hook that computes the route to a given entity / ref. This is a bit +// contrived, because it tries to retain the casing of the entity name if +// present, but not of other parts. This is in an attempt to make slightly more +// nice-looking URLs. +function useEntityRoute( + entityRef: Entity | CompoundEntityRef | string, +): string { + const entityRoute = useRouteRef(entityRouteRef); + + let kind; + let namespace; + let name; + + if (typeof entityRef === 'string') { + const parsed = parseEntityRef(entityRef); + kind = parsed.kind; + namespace = parsed.namespace; + name = parsed.name; + } else if ('metadata' in entityRef) { + kind = entityRef.kind; + namespace = entityRef.metadata.namespace; + name = entityRef.metadata.name; + } else { + kind = entityRef.kind; + namespace = entityRef.namespace; + name = entityRef.name; + } + + kind = kind.toLocaleLowerCase('en-US'); + namespace = namespace?.toLocaleLowerCase('en-US') ?? DEFAULT_NAMESPACE; + + const routeParams = { + kind: encodeURIComponent(kind), + namespace: encodeURIComponent(namespace), + name: encodeURIComponent(name), + }; + + return entityRoute(routeParams); +} diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx index 4e0d52ef4e..31a5be33d0 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.test.tsx @@ -34,7 +34,7 @@ describe('', () => { '/catalog/:namespace/:kind/:name/*': entityRouteRef, }, }); - expect(screen.getByText('component:software')).toHaveAttribute( + expect(screen.getByText('software').closest('a')).toHaveAttribute( 'href', '/catalog/default/component/software', ); @@ -59,11 +59,11 @@ describe('', () => { }, }); expect(screen.getByText(',')).toBeInTheDocument(); - expect(screen.getByText('component:software')).toHaveAttribute( + expect(screen.getByText('software').closest('a')).toHaveAttribute( 'href', '/catalog/default/component/software', ); - expect(screen.getByText('api:interface')).toHaveAttribute( + expect(screen.getByText('interface').closest('a')).toHaveAttribute( 'href', '/catalog/default/api/interface', ); diff --git a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.tsx b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.tsx index 2502fbbb6e..929b3f9376 100644 --- a/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.tsx +++ b/plugins/catalog-react/src/components/EntityRefLink/EntityRefLinks.tsx @@ -13,11 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; +import { + Entity, + CompoundEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; import React from 'react'; import { EntityRefLink } from './EntityRefLink'; import { LinkProps } from '@backstage/core-components'; -import { FetchedEntityRefLinks } from './FetchedEntityRefLinks'; /** * Props for {@link EntityRefLink}. @@ -26,21 +29,14 @@ import { FetchedEntityRefLinks } from './FetchedEntityRefLinks'; */ export type EntityRefLinksProps< TRef extends string | CompoundEntityRef | Entity, -> = ( - | { - defaultKind?: string; - entityRefs: TRef[]; - fetchEntities?: false; - getTitle?(entity: TRef): string | undefined; - } - | { - defaultKind?: string; - entityRefs: TRef[]; - fetchEntities: true; - getTitle(entity: Entity): string | undefined; - } -) & - Omit; +> = { + defaultKind?: string; + entityRefs: TRef[]; + /** @deprecated This option is no longer used; presentation is handled by entityPresentationApiRef instead */ + fetchEntities?: boolean; + /** @deprecated This option is no longer used; presentation is handled by entityPresentationApiRef instead */ + getTitle?(entity: TRef): string | undefined; +} & Omit; /** * Shows a list of clickable links to entities. @@ -50,32 +46,17 @@ export type EntityRefLinksProps< export function EntityRefLinks< TRef extends string | CompoundEntityRef | Entity, >(props: EntityRefLinksProps) { - const { entityRefs, defaultKind, fetchEntities, getTitle, ...linkProps } = - props; - - if (fetchEntities) { - return ( - - ); - } + const { entityRefs, ...linkProps } = props; return ( <> {entityRefs.map((r: TRef, i: number) => { + const entityRefString = + typeof r === 'string' ? r : stringifyEntityRef(r); return ( - + {i > 0 && ', '} - + ); })} diff --git a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx deleted file mode 100644 index 9db6511662..0000000000 --- a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.test.tsx +++ /dev/null @@ -1,219 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { FetchedEntityRefLinks } from './FetchedEntityRefLinks'; -import { entityRouteRef } from '../../routes'; -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { screen } from '@testing-library/react'; -import { Entity } from '@backstage/catalog-model'; -import React from 'react'; -import { JsonObject } from '@backstage/types'; -import { catalogApiRef } from '../../api'; -import { CatalogApi } from '@backstage/catalog-client'; - -describe('', () => { - const getTitle = (e: Entity): string => - (e.spec?.profile!! as JsonObject).displayName!!.toString()!!; - - it('should fetch entities and render the custom display text', async () => { - const entityRefs = [ - { - kind: 'Component', - namespace: 'default', - name: 'software', - }, - { - kind: 'API', - namespace: 'default', - name: 'interface', - }, - ]; - - const catalogApi: Partial = { - getEntities: () => - Promise.resolve({ - items: entityRefs.map(ref => ({ - apiVersion: 'backstage.io/v1alpha1', - kind: ref.kind, - metadata: { - name: ref.name, - namespace: ref.namespace, - }, - spec: { - profile: { - displayName: ref.name.toLocaleUpperCase('en-US'), - }, - type: 'organization', - }, - })), - }), - }; - - await renderInTestApp( - - - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, - }, - ); - - expect(screen.getByText('SOFTWARE')).toHaveAttribute( - 'href', - '/catalog/default/component/software', - ); - - expect(screen.getByText('INTERFACE')).toHaveAttribute( - 'href', - '/catalog/default/api/interface', - ); - }); - - it('should use entities as they are provided and render the custom display text', async () => { - const entityRefs = [ - { - kind: 'Component', - namespace: 'default', - name: 'tool', - }, - { - kind: 'API', - namespace: 'default', - name: 'implementation', - }, - ].map(ref => ({ - apiVersion: 'backstage.io/v1alpha1', - kind: ref.kind, - metadata: { - name: ref.name, - namespace: ref.namespace, - }, - spec: { - profile: { - displayName: ref.name.toLocaleUpperCase('en-US'), - }, - type: 'organization', - }, - })); - - const catalogApi: Partial = {}; - - await renderInTestApp( - - - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, - }, - ); - - expect(screen.getByText('TOOL')).toHaveAttribute( - 'href', - '/catalog/default/component/tool', - ); - - expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( - 'href', - '/catalog/default/api/implementation', - ); - }); - - it('should handle heterogeneous array of values to render the custom display text', async () => { - const entityRefs = [ - ...[ - { - kind: 'Component', - namespace: 'default', - name: 'tool', - }, - { - kind: 'API', - namespace: 'default', - name: 'implementation', - }, - ].map(ref => ({ - apiVersion: 'backstage.io/v1alpha1', - kind: ref.kind, - metadata: { - name: ref.name, - namespace: ref.namespace, - }, - spec: { - profile: { - displayName: ref.name.toLocaleUpperCase('en-US'), - }, - type: 'organization', - }, - })), - { - kind: 'Component', - namespace: 'default', - name: 'interface', - }, - ]; - - const catalogApi: Partial = { - getEntities: () => - Promise.resolve({ - items: [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'interface', - namespace: 'default', - }, - spec: { - profile: { - displayName: 'INTERFACE', - }, - type: 'organization', - }, - }, - ], - }), - }; - - await renderInTestApp( - - - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, - }, - ); - - expect(screen.getByText('TOOL')).toHaveAttribute( - 'href', - '/catalog/default/component/tool', - ); - - expect(screen.getByText('IMPLEMENTATION')).toHaveAttribute( - 'href', - '/catalog/default/api/implementation', - ); - - expect(screen.getByText('INTERFACE')).toHaveAttribute( - 'href', - '/catalog/default/component/interface', - ); - }); -}); diff --git a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.tsx b/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.tsx deleted file mode 100644 index d8f013d50c..0000000000 --- a/plugins/catalog-react/src/components/EntityRefLink/FetchedEntityRefLinks.tsx +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { - Entity, - CompoundEntityRef, - parseEntityRef, -} from '@backstage/catalog-model'; -import React from 'react'; -import { EntityRefLink } from './EntityRefLink'; -import { ErrorPanel, LinkProps, Progress } from '@backstage/core-components'; -import useAsync from 'react-use/lib/useAsync'; -import { catalogApiRef } from '../../api'; -import { useApi } from '@backstage/core-plugin-api'; - -/** - * Props for {@link FetchedEntityRefLinks}. - * - * @public - */ -export type FetchedEntityRefLinksProps< - TRef extends string | CompoundEntityRef | Entity, -> = { - defaultKind?: string; - entityRefs: TRef[]; - getTitle(entity: Entity): string | undefined; -} & Omit; - -/** - * Shows a list of clickable links to entities with auto-fetching of entities - * for customising a displayed text via title attribute. - * - * @public - */ -export function FetchedEntityRefLinks< - TRef extends string | CompoundEntityRef | Entity, ->(props: FetchedEntityRefLinksProps) { - const { entityRefs, defaultKind, getTitle, ...linkProps } = props; - - const catalogApi = useApi(catalogApiRef); - - const { - value: entities = new Array(), - loading, - error, - } = useAsync(async () => { - const refs = entityRefs.reduce((acc, current) => { - if (typeof current === 'object' && 'metadata' in current) { - return acc; - } - return [...acc, parseEntityRef(current)]; - }, new Array()); - - const pureEntities = entityRefs.filter( - ref => typeof ref === 'object' && 'metadata' in ref, - ) as Array; - - return refs.length > 0 - ? [ - ...( - await catalogApi.getEntities({ - filter: refs.map(ref => ({ - kind: ref.kind, - 'metadata.namespace': ref.namespace, - 'metadata.name': ref.name, - })), - }) - ).items, - ...pureEntities, - ] - : pureEntities; - }, [entityRefs]); - - if (loading) { - return ; - } - - if (error) { - return ; - } - - return ( - <> - {entities.map((r: Entity, i) => { - return ( - - {i > 0 && ', '} - - - ); - })} - - ); -} diff --git a/plugins/catalog-react/src/components/UnregisterEntityDialog/UnregisterEntityDialog.test.tsx b/plugins/catalog-react/src/components/UnregisterEntityDialog/UnregisterEntityDialog.test.tsx index 7f139854f6..7c8ddb3afb 100644 --- a/plugins/catalog-react/src/components/UnregisterEntityDialog/UnregisterEntityDialog.test.tsx +++ b/plugins/catalog-react/src/components/UnregisterEntityDialog/UnregisterEntityDialog.test.tsx @@ -285,8 +285,8 @@ describe('UnregisterEntityDialog', () => { expect( screen.getByText(/will unregister the following entities/), ).toBeInTheDocument(); - expect(screen.getByText(/k1:ns1\/n1/)).toBeInTheDocument(); - expect(screen.getByText(/k2:ns2\/n2/)).toBeInTheDocument(); + expect(screen.getByText(/ns1\/n1/)).toBeInTheDocument(); + expect(screen.getByText(/ns2\/n2/)).toBeInTheDocument(); }); await userEvent.click(screen.getByText('Unregister Location')); @@ -333,8 +333,8 @@ describe('UnregisterEntityDialog', () => { expect( screen.getByText(/will unregister the following entities/), ).toBeInTheDocument(); - expect(screen.getByText(/k1:ns1\/n1/)).toBeInTheDocument(); - expect(screen.getByText(/k2:ns2\/n2/)).toBeInTheDocument(); + expect(screen.getByText(/ns1\/n1/)).toBeInTheDocument(); + expect(screen.getByText(/ns2\/n2/)).toBeInTheDocument(); }); await userEvent.click(screen.getByText('Advanced Options')); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx index c31381df20..ad56681d6c 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.test.tsx @@ -16,15 +16,19 @@ import React from 'react'; import { fireEvent, render, waitFor, screen } from '@testing-library/react'; -import { - Entity, - RELATION_OWNED_BY, - UserEntity, -} from '@backstage/catalog-model'; -import { UserListPicker } from './UserListPicker'; +import { UserEntity } from '@backstage/catalog-model'; +import { UserListPicker, UserListPickerProps } from './UserListPicker'; import { MockEntityListContextProvider } from '../../testUtils/providers'; -import { EntityTagFilter, UserListFilter } from '../../filters'; -import { CatalogApi } from '@backstage/catalog-client'; +import { + EntityKindFilter, + EntityNamespaceFilter, + EntityTagFilter, + EntityUserFilter, +} from '../../filters'; +import { + CatalogApi, + QueryEntitiesInitialRequest, +} from '@backstage/catalog-client'; import { catalogApiRef } from '../../api'; import { MockStorageApi, TestApiRegistry } from '@backstage/test-utils'; import { ApiProvider } from '@backstage/core-app-api'; @@ -35,7 +39,7 @@ import { identityApiRef, storageApiRef, } from '@backstage/core-plugin-api'; -import { useEntityOwnership } from '../../hooks'; +import { MockStarredEntitiesApi, starredEntitiesApiRef } from '../../apis'; const mockUser: UserEntity = { apiVersion: 'backstage.io/v1alpha1', @@ -54,136 +58,151 @@ const mockConfigApi = { } as Partial; const mockCatalogApi = { - getEntityByRef: () => Promise.resolve(mockUser), -} as Partial; + getEntityByRef: jest.fn(), + queryEntities: jest.fn(), +} as Partial>; const mockIdentityApi = { - getUserId: () => 'testUser', - getIdToken: async () => undefined, -} as Partial; + getBackstageIdentity: jest.fn(), +} as Partial>; + +const mockStarredEntitiesApi = new MockStarredEntitiesApi(); const apis = TestApiRegistry.from( [configApiRef, mockConfigApi], [catalogApiRef, mockCatalogApi], [identityApiRef, mockIdentityApi], [storageApiRef, MockStorageApi.create()], + [starredEntitiesApiRef, mockStarredEntitiesApi], ); -const mockIsOwnedEntity = jest.fn( - (entity: Entity) => entity.metadata.name === 'component-1', -); - -const mockIsStarredEntity = jest.fn( - (entity: Entity) => entity.metadata.name === 'component-3', -); - -jest.mock('../../hooks', () => { - const actual = jest.requireActual('../../hooks'); - return { - ...actual, - useEntityOwnership: jest.fn(() => ({ - isOwnedEntity: mockIsOwnedEntity, - })), - useStarredEntities: () => ({ - isStarredEntity: mockIsStarredEntity, - }), - }; -}); - -const backendEntities: Entity[] = [ - { - apiVersion: '1', - kind: 'Component', - metadata: { - namespace: 'namespace-1', - name: 'component-1', - tags: ['tag1'], - }, - relations: [ - { - type: RELATION_OWNED_BY, - targetRef: 'user:default/testuser', - }, - ], - }, - { - apiVersion: '1', - kind: 'Component', - metadata: { - namespace: 'namespace-2', - name: 'component-2', - tags: ['tag1'], - }, - }, - { - apiVersion: '1', - kind: 'Component', - metadata: { - namespace: 'namespace-2', - name: 'component-3', - tags: [], - }, - }, - { - apiVersion: '1', - kind: 'Component', - metadata: { - namespace: 'namespace-2', - name: 'component-4', - tags: [], - }, - relations: [ - { - type: RELATION_OWNED_BY, - targetRef: 'user:default/testuser', - }, - ], - }, -]; - +const ownershipEntityRefs = ['user:default/testuser']; describe('', () => { - it('renders filter groups', () => { + const mockQueryEntitiesImplementation: CatalogApi['queryEntities'] = + async request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['relations.ownedBy'] + ) { + // owned entities + return { items: [], totalItems: 3, pageInfo: {} }; + } + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['metadata.name'] + ) { + // starred entities + return { + items: [ + { + apiVersion: '1', + kind: 'component', + metadata: { name: 'e-1', namespace: 'default' }, + }, + { + apiVersion: '1', + kind: 'component', + metadata: { name: 'e-2', namespace: 'default' }, + }, + ], + totalItems: 2, + pageInfo: {}, + }; + } + // all items + return { items: [], totalItems: 10, pageInfo: {} }; + }; + + beforeAll(() => { + mockStarredEntitiesApi.toggleStarred('component:default/e-1'); + mockStarredEntitiesApi.toggleStarred('component:default/e-2'); + }); + + beforeEach(() => { + mockCatalogApi.getEntityByRef?.mockResolvedValue(mockUser); + mockIdentityApi.getBackstageIdentity?.mockResolvedValue({ + ownershipEntityRefs, + type: 'user', + userEntityRef: 'user:default/testuser', + }); + + mockCatalogApi.queryEntities?.mockImplementation( + mockQueryEntitiesImplementation, + ); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + it('renders filter groups', async () => { render( - + , ); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalled(), + ); expect(screen.getByText('Personal')).toBeInTheDocument(); expect(screen.getByText('Test Company')).toBeInTheDocument(); }); - it('renders filters', () => { + it('renders filters', async () => { render( - + , ); - expect( - screen.getAllByRole('menuitem').map(({ textContent }) => textContent), - ).toEqual(['Owned 1', 'Starred 1', 'All 4']); - }); - - it('includes counts alongside each filter', async () => { - render( - - - - - , + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), ); - - // Material UI renders ListItemSecondaryActions outside the - // menuitem itself, so we pick off the next sibling. - await waitFor(() => { + await waitFor(() => expect( screen.getAllByRole('menuitem').map(({ textContent }) => textContent), - ).toEqual(['Owned 1', 'Starred 1', 'All 4']); + ).toEqual(['Owned 3', 'Starred 2', 'All 10']), + ); + + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.namespace': ['default'], + }, + limit: 0, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.namespace': ['default'], + 'relations.ownedBy': ['user:default/testuser'], + }, + limit: 0, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.namespace': ['default'], + 'metadata.name': ['e-1', 'e-2'], + }, + limit: 1000, }); }); @@ -192,7 +211,6 @@ describe('', () => { @@ -204,35 +222,74 @@ describe('', () => { await waitFor(() => { expect( screen.getAllByRole('menuitem').map(({ textContent }) => textContent), - ).toEqual(['Owned 1', 'Starred 0', 'All 2']); + ).toEqual(['Owned 3', 'Starred 2', 'All 10']); + }); + + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { 'metadata.tags': ['tag1'] }, + limit: 0, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { 'metadata.name': ['e-1', 'e-2'], 'metadata.tags': ['tag1'] }, + limit: 1000, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { + 'relations.ownedBy': ['user:default/testuser'], + 'metadata.tags': ['tag1'], + }, + limit: 0, }); }); - it('respects the query parameter filter value', () => { + it('respects the query parameter filter value', async () => { const updateFilters = jest.fn(); - const queryParameters = { user: 'owned' }; + const queryParameters = { user: 'owned', kind: 'component' }; render( , ); + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); - expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter('owned', mockIsOwnedEntity, mockIsStarredEntity), + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.owned(ownershipEntityRefs), + }), + ); + + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { kind: 'component' }, + limit: 0, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { kind: 'component', 'metadata.name': ['e-1', 'e-2'] }, + limit: 1000, + }); + expect(mockCatalogApi.queryEntities).toHaveBeenCalledWith({ + filter: { + kind: 'component', + 'relations.ownedBy': ['user:default/testuser'], + }, + limit: 0, }); }); - it('updates user filter when a menuitem is selected', () => { + it('updates user filter when a menuitem is selected', async () => { const updateFilters = jest.fn(); render( - + , @@ -240,40 +297,55 @@ describe('', () => { fireEvent.click(screen.getByText('Starred')); - expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter( - 'starred', - mockIsOwnedEntity, - mockIsStarredEntity, - ), - }); + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.starred([ + 'component:default/e-1', + 'component:default/e-2', + ]), + }), + ); }); - it('responds to external queryParameters changes', () => { + it('responds to external queryParameters changes', async () => { const updateFilters = jest.fn(); const rendered = render( , ); - expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter('all', mockIsOwnedEntity, mockIsStarredEntity), - }); + + await waitFor(() => + expect(mockIdentityApi.getBackstageIdentity).toHaveBeenCalled(), + ); + + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.all(), + }), + ); + rendered.rerender( @@ -281,101 +353,237 @@ describe('', () => { , ); expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter('owned', mockIsOwnedEntity, mockIsStarredEntity), + user: EntityUserFilter.owned(ownershipEntityRefs), }); }); - describe.each` - type | filterFn - ${'owned'} | ${mockIsOwnedEntity} - ${'starred'} | ${mockIsStarredEntity} - `('filter resetting for $type entities', ({ type, filterFn }) => { - let updateFilters: jest.Mock; + describe('filter resetting', () => { + const updateFilters = jest.fn(); - const picker = (props: { loading: boolean }) => ( + const Picker = ({ ...props }: UserListPickerProps) => ( - + ); - beforeEach(() => { - updateFilters = jest.fn(); - }); + describe(`when there are no owned entities matching the filter`, () => { + it('does not reset the filter while entities are loading', async () => { + mockCatalogApi.queryEntities?.mockReturnValue(new Promise(() => {})); - describe(`when there are no ${type} entities match the filter`, () => { - beforeEach(() => { - filterFn.mockReturnValue(false); + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalled(), + ); + + await expect(() => + waitFor(() => expect(updateFilters).toHaveBeenCalled()), + ).rejects.toThrow(); }); - it('does not reset the filter while entities are loading', () => { - render(picker({ loading: true })); + it('does not reset the filter while owned entities are loading', async () => { + mockCatalogApi.queryEntities?.mockImplementation(request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['relations.ownedBy'] + ) { + return new Promise(() => {}); + } + return mockQueryEntitiesImplementation(request); + }); + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); expect(updateFilters).not.toHaveBeenCalledWith({ - user: new UserListFilter( - 'all', - mockIsOwnedEntity, - mockIsStarredEntity, - ), + user: expect.any(Object), }); }); - it('does not reset the filter while owned entities are loading', () => { - const isOwnedEntity = jest.fn(() => false); - (useEntityOwnership as jest.Mock).mockReturnValueOnce({ - loading: true, - isOwnedEntity, + it('resets the filter to "all" when entities are loaded', async () => { + mockCatalogApi.queryEntities?.mockImplementation(async request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['relations.ownedBy'] + ) { + return { items: [], totalItems: 0, pageInfo: {} }; + } + return mockQueryEntitiesImplementation(request); }); - render(picker({ loading: false })); - expect(updateFilters).not.toHaveBeenCalledWith({ - user: new UserListFilter('all', isOwnedEntity, mockIsStarredEntity), - }); - }); + render(); - it('resets the filter to "all" when entities are loaded', () => { - render(picker({ loading: false })); - - expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter( - 'all', - mockIsOwnedEntity, - mockIsStarredEntity, - ), - }); + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.all(), + }), + ); }); }); - describe(`when there are some ${type} entities present`, () => { - beforeEach(() => { - filterFn.mockReturnValue(true); + describe(`when there are no starred entities match the filter`, () => { + it('does not reset the filter while entities are loading', async () => { + mockCatalogApi.queryEntities?.mockImplementation( + () => new Promise(() => {}), + ); + + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalled(), + ); + expect(updateFilters).not.toHaveBeenCalled(); }); - it('does not reset the filter while entities are loading', () => { - render(picker({ loading: true })); + it('does not reset the filter while starred entities are loading', async () => { + mockCatalogApi.queryEntities?.mockImplementation(request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['metadata.name'] + ) { + return new Promise(() => {}); + } + return mockQueryEntitiesImplementation(request); + }); + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); expect(updateFilters).not.toHaveBeenCalledWith({ - user: new UserListFilter( - 'all', - mockIsOwnedEntity, - mockIsStarredEntity, - ), + user: expect.any(Object), }); }); - it('does not reset the filter when entities are loaded', () => { - render(picker({ loading: false })); - - expect(updateFilters).toHaveBeenLastCalledWith({ - user: new UserListFilter( - type, - mockIsOwnedEntity, - mockIsStarredEntity, - ), + it('resets the filter to "all" when entities are loaded', async () => { + mockCatalogApi.queryEntities?.mockImplementation(async request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['metadata.name'] + ) { + return { items: [], totalItems: 0, pageInfo: {} }; + } + return mockQueryEntitiesImplementation(request); }); + + render(); + + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.all(), + }), + ); + }); + }); + + describe(`when there are some owned entities present`, () => { + it('does not reset the filter while entities are loading', async () => { + mockCatalogApi.queryEntities?.mockImplementation(request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['relations.ownedBy'] + ) { + return new Promise(() => {}); + } + return mockQueryEntitiesImplementation(request); + }); + + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); + expect(updateFilters).not.toHaveBeenCalledWith({ + user: EntityUserFilter.all(), + }); + }); + + it('does not reset the filter when entities are loaded', async () => { + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); + + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.owned(expect.any(Array)), + }), + ); + }); + }); + + describe(`when there are some starred entities present`, () => { + it('does not reset the filter while entities are loading', async () => { + mockCatalogApi.queryEntities?.mockImplementation(request => { + if ( + ( + (request as QueryEntitiesInitialRequest).filter as Record< + string, + string + > + )['metadata.name'] + ) { + return new Promise(() => {}); + } + return mockQueryEntitiesImplementation(request); + }); + + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); + expect(updateFilters).not.toHaveBeenCalledWith({ + user: EntityUserFilter.all(), + }); + }); + + it('does not reset the filter when entities are loaded', async () => { + render(); + + await waitFor(() => + expect(mockCatalogApi.queryEntities).toHaveBeenCalledTimes(3), + ); + + await waitFor(() => + expect(updateFilters).toHaveBeenLastCalledWith({ + user: EntityUserFilter.starred([ + 'component:default/e-1', + 'component:default/e-2', + ]), + }), + ); }); }); }); diff --git a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx index e7c306e0d0..854a1cb1b7 100644 --- a/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx +++ b/plugins/catalog-react/src/components/UserListPicker/UserListPicker.tsx @@ -32,16 +32,13 @@ import { } from '@material-ui/core'; import SettingsIcon from '@material-ui/icons/Settings'; import StarIcon from '@material-ui/icons/Star'; -import { compact } from 'lodash'; import React, { Fragment, useEffect, useMemo, useState } from 'react'; -import { UserListFilter } from '../../filters'; -import { - useEntityList, - useStarredEntities, - useEntityOwnership, -} from '../../hooks'; +import { EntityUserFilter } from '../../filters'; +import { useEntityList } from '../../hooks'; import { UserListFilterKind } from '../../types'; -import { reduceEntityFilters } from '../../utils'; +import { useOwnedEntitiesCount } from './useOwnedEntitiesCount'; +import { useAllEntitiesCount } from './useAllEntitiesCount'; +import { useStarredEntitiesCount } from './useStarredEntitiesCount'; /** @public */ export type CatalogReactUserListPickerClassKey = @@ -133,9 +130,7 @@ export const UserListPicker = (props: UserListPickerProps) => { const { filters, updateFilters, - backendEntities, queryParameters: { kind: kindParameter, user: userParameter }, - loading: loadingBackendEntities, } = useEntityList(); // Remove group items that aren't in availableFilters and exclude @@ -153,21 +148,17 @@ export const UserListPicker = (props: UserListPickerProps) => { })) .filter(({ items }) => !!items.length); - const { isStarredEntity } = useStarredEntities(); - const { isOwnedEntity, loading: loadingEntityOwnership } = - useEntityOwnership(); - - const loading = loadingBackendEntities || loadingEntityOwnership; - - // Static filters; used for generating counts of potentially unselected kinds - const ownedFilter = useMemo( - () => new UserListFilter('owned', isOwnedEntity, isStarredEntity), - [isOwnedEntity, isStarredEntity], - ); - const starredFilter = useMemo( - () => new UserListFilter('starred', isOwnedEntity, isStarredEntity), - [isOwnedEntity, isStarredEntity], - ); + const { + count: ownedEntitiesCount, + loading: loadingOwnedEntities, + filter: ownedEntitiesFilter, + } = useOwnedEntitiesCount(); + const { count: allCount } = useAllEntitiesCount(); + const { + count: starredEntitiesCount, + filter: starredEntitiesFilter, + loading: loadingStarredEntities, + } = useStarredEntitiesCount(); const queryParamUserFilter = useMemo( () => [userParameter].flat()[0], @@ -175,33 +166,16 @@ export const UserListPicker = (props: UserListPickerProps) => { ); const [selectedUserFilter, setSelectedUserFilter] = useState( - queryParamUserFilter ?? initialFilter, + (queryParamUserFilter as UserListFilterKind) ?? initialFilter, ); - // To show proper counts for each section, apply all other frontend filters _except_ the user - // filter that's controlled by this picker. - const entitiesWithoutUserFilter = useMemo( - () => - backendEntities.filter( - reduceEntityFilters( - compact(Object.values({ ...filters, user: undefined })), - ), - ), - [filters, backendEntities], - ); - - const filterCounts = useMemo>( - () => ({ - all: entitiesWithoutUserFilter.length, - starred: entitiesWithoutUserFilter.filter(entity => - starredFilter.filterEntity(entity), - ).length, - owned: entitiesWithoutUserFilter.filter(entity => - ownedFilter.filterEntity(entity), - ).length, - }), - [entitiesWithoutUserFilter, starredFilter, ownedFilter], - ); + const filterCounts = useMemo(() => { + return { + all: allCount, + starred: starredEntitiesCount, + owned: ownedEntitiesCount, + }; + }, [starredEntitiesCount, ownedEntitiesCount, allCount]); // Set selected user filter on query parameter updates; this happens at initial page load and from // external updates to the page location. @@ -211,6 +185,8 @@ export const UserListPicker = (props: UserListPickerProps) => { } }, [queryParamUserFilter]); + const loading = loadingOwnedEntities || loadingStarredEntities; + useEffect(() => { if ( !loading && @@ -223,16 +199,32 @@ export const UserListPicker = (props: UserListPickerProps) => { }, [loading, filterCounts, selectedUserFilter, setSelectedUserFilter]); useEffect(() => { - updateFilters({ - user: selectedUserFilter - ? new UserListFilter( - selectedUserFilter as UserListFilterKind, - isOwnedEntity, - isStarredEntity, - ) - : undefined, - }); - }, [selectedUserFilter, isOwnedEntity, isStarredEntity, updateFilters]); + if (!selectedUserFilter) { + return; + } + if (loading) { + return; + } + + const getFilter = () => { + if (selectedUserFilter === 'owned') { + return ownedEntitiesFilter; + } + if (selectedUserFilter === 'starred') { + return starredEntitiesFilter; + } + return EntityUserFilter.all(); + }; + + updateFilters({ user: getFilter() }); + }, [ + selectedUserFilter, + starredEntitiesFilter, + ownedEntitiesFilter, + updateFilters, + + loading, + ]); return ( diff --git a/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.test.tsx b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.test.tsx new file mode 100644 index 0000000000..dc3bc5d2bd --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.test.tsx @@ -0,0 +1,105 @@ +/* + * 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 React, { PropsWithChildren } from 'react'; +import { CatalogApi } from '@backstage/catalog-client'; +import { useAllEntitiesCount } from './useAllEntitiesCount'; +import { renderHook, waitFor } from '@testing-library/react'; +import { EntityListProvider, useEntityList } from '../../hooks'; +import { catalogApiRef } from '../../api'; +import { ApiRef } from '@backstage/core-plugin-api'; +import { MemoryRouter } from 'react-router-dom'; +import { EntityOwnerFilter } from '../../filters'; +import { useMountEffect } from '@react-hookz/web'; + +const mockQueryEntities: jest.MockedFn = jest.fn(); +const mockCatalogApi: jest.Mocked> = { + queryEntities: mockQueryEntities, +}; + +jest.mock('@backstage/core-plugin-api', () => { + const actual = jest.requireActual('@backstage/core-plugin-api'); + return { + ...actual, + useApi: (ref: ApiRef) => + ref === catalogApiRef ? mockCatalogApi : actual.useApi(ref), + }; +}); + +describe('useAllEntitiesCount', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return the count', async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + function WrapFilters(props: PropsWithChildren<{}>) { + const { updateFilters } = useEntityList(); + + useMountEffect(() => { + updateFilters({ + owners: new EntityOwnerFilter(['user:default/owner']), + }); + }); + return <>{props.children}; + } + + const { result } = renderHook(() => useAllEntitiesCount(), { + wrapper: ({ children }) => ( + + + {children} + + + ), + }); + + await waitFor(() => + expect(mockQueryEntities).toHaveBeenCalledWith({ + filter: { + 'relations.ownedBy': ['user:default/owner'], + }, + limit: 0, + }), + ); + expect(result.current).toEqual({ count: 10, loading: false }); + }); + + it(`shouldn't invoke the endpoint at startup, when filters are missing`, async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + const { result } = renderHook(() => useAllEntitiesCount(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + await expect( + waitFor(() => expect(mockQueryEntities).toHaveBeenCalled()), + ).rejects.toThrow(); + expect(result.current).toEqual({ count: 0, loading: false }); + }); +}); diff --git a/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts new file mode 100644 index 0000000000..ba971b088d --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useAllEntitiesCount.ts @@ -0,0 +1,61 @@ +/* + * 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 { QueryEntitiesInitialRequest } from '@backstage/catalog-client'; +import { useApi } from '@backstage/core-plugin-api'; +import { compact, isEqual } from 'lodash'; +import { useMemo, useRef } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { catalogApiRef } from '../../api'; +import { useEntityList } from '../../hooks'; +import { reduceCatalogFilters } from '../../utils'; + +export function useAllEntitiesCount() { + const catalogApi = useApi(catalogApiRef); + const { filters } = useEntityList(); + + const prevRequest = useRef(); + const request = useMemo(() => { + const { user, ...allFilters } = filters; + const compacted = compact(Object.values(allFilters)); + const filter = reduceCatalogFilters(compacted); + const newRequest: QueryEntitiesInitialRequest = { + filter, + limit: 0, + }; + + if (Object.keys(filter).length === 0) { + prevRequest.current = undefined; + return prevRequest.current; + } + + if (isEqual(newRequest, prevRequest.current)) { + return prevRequest.current; + } + prevRequest.current = newRequest; + return newRequest; + }, [filters]); + + const { value: count, loading } = useAsync(async () => { + if (request === undefined) { + return 0; + } + const { totalItems } = await catalogApi.queryEntities(request); + + return totalItems; + }, [request]); + + return { count, loading }; +} diff --git a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx new file mode 100644 index 0000000000..1f6fbfd53d --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.test.tsx @@ -0,0 +1,235 @@ +/* + * 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 React, { PropsWithChildren } from 'react'; +import { CatalogApi } from '@backstage/catalog-client'; +import { renderHook, waitFor } from '@testing-library/react'; +import { + DefaultEntityFilters, + EntityListProvider, + useEntityList, +} from '../../hooks'; +import { catalogApiRef } from '../../api'; +import { + ApiRef, + IdentityApi, + identityApiRef, +} from '@backstage/core-plugin-api'; +import { MemoryRouter } from 'react-router-dom'; +import { useOwnedEntitiesCount } from './useOwnedEntitiesCount'; +import { + EntityNamespaceFilter, + EntityOwnerFilter, + EntityUserFilter, +} from '../../filters'; +import { useMountEffect } from '@react-hookz/web'; + +const mockQueryEntities: jest.MockedFn = jest.fn(); +const mockCatalogApi: jest.Mocked> = { + queryEntities: mockQueryEntities, +}; + +const mockGetBackstageIdentity: jest.MockedFn< + IdentityApi['getBackstageIdentity'] +> = jest.fn(); + +jest.mock('@backstage/core-plugin-api', () => { + const actual = jest.requireActual('@backstage/core-plugin-api'); + return { + ...actual, + useApi: (ref: ApiRef) => { + if (ref === catalogApiRef) { + return mockCatalogApi; + } + if (ref === identityApiRef) { + return { + getBackstageIdentity: mockGetBackstageIdentity, + }; + } + + return actual.useApi(ref); + }, + }; +}); + +describe('useOwnedEntitiesCount', () => { + beforeEach(() => { + jest.clearAllMocks(); + + mockGetBackstageIdentity.mockResolvedValue({ + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + userEntityRef: 'user:default/spiderman', + type: 'user', + }); + }); + + it(`shouldn't invoke queryEntities when filters are loading`, async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + const { result } = renderHook(() => useOwnedEntitiesCount(), { + wrapper: createWrapperWithInitialFilters({}), + }); + + await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + + await expect( + waitFor(() => expect(mockQueryEntities).toHaveBeenCalled()), + ).rejects.toThrow(); + + expect(result.current).toEqual({ + count: 0, + loading: false, + filter: EntityUserFilter.owned([ + 'user:default/spiderman', + 'user:group/a-group', + ]), + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + }); + }); + + it(`should properly apply the filters`, async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + const { result } = renderHook(() => useOwnedEntitiesCount(), { + wrapper: createWrapperWithInitialFilters({ + namespace: new EntityNamespaceFilter(['a-namespace']), + }), + }); + + await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + + await waitFor(() => + expect(mockQueryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.namespace': ['a-namespace'], + 'relations.ownedBy': ['user:default/spiderman', 'user:group/a-group'], + }, + limit: 0, + }), + ); + + expect(result.current).toEqual({ + count: 10, + loading: false, + filter: EntityUserFilter.owned([ + 'user:default/spiderman', + 'user:group/a-group', + ]), + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + }); + }); + + it(`should return count 0 without invoking queryEntities if owners filter doesn't have claims in common with logged in user`, async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + const { result } = renderHook(() => useOwnedEntitiesCount(), { + wrapper: createWrapperWithInitialFilters({ + namespace: new EntityNamespaceFilter(['a-namespace']), + owners: new EntityOwnerFilter(['group:default/monsters']), + }), + }); + + await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + + await expect( + waitFor(() => expect(mockQueryEntities).toHaveBeenCalled()), + ).rejects.toThrow(); + + expect(result.current).toEqual({ + count: 0, + loading: false, + filter: EntityUserFilter.owned([ + 'user:default/spiderman', + 'user:group/a-group', + ]), + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + }); + }); + + it(`should send claims in common between owners filter and logged in user`, async () => { + mockQueryEntities.mockResolvedValue({ + items: [], + totalItems: 10, + pageInfo: {}, + }); + + const { result } = renderHook(() => useOwnedEntitiesCount(), { + wrapper: createWrapperWithInitialFilters({ + namespace: new EntityNamespaceFilter(['a-namespace']), + owners: new EntityOwnerFilter([ + 'group:default/monsters', + 'user:group/a-group', + ]), + }), + }); + + await waitFor(() => expect(mockGetBackstageIdentity).toHaveBeenCalled()); + + await waitFor(() => + expect(mockQueryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.namespace': ['a-namespace'], + 'relations.ownedBy': ['user:group/a-group'], + }, + limit: 0, + }), + ); + + expect(result.current).toEqual({ + count: 10, + loading: false, + filter: EntityUserFilter.owned([ + 'user:default/spiderman', + 'user:group/a-group', + ]), + ownershipEntityRefs: ['user:default/spiderman', 'user:group/a-group'], + }); + }); +}); + +function createWrapperWithInitialFilters( + filters: Partial, +) { + function WrapFilters(props: PropsWithChildren<{}>) { + const { updateFilters } = useEntityList(); + + useMountEffect(() => { + updateFilters(filters); + }); + return <>{props.children}; + } + + return function Wrapper(props: PropsWithChildren<{}>) { + return ( + + + {props.children} + + + ); + }; +} diff --git a/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts new file mode 100644 index 0000000000..1adab49361 --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useOwnedEntitiesCount.ts @@ -0,0 +1,133 @@ +/* + * 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 { QueryEntitiesInitialRequest } from '@backstage/catalog-client'; +import { identityApiRef, useApi } from '@backstage/core-plugin-api'; +import { compact, intersection, isEqual } from 'lodash'; +import { useEffect, useMemo, useRef } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { catalogApiRef } from '../../api'; +import { EntityOwnerFilter, EntityUserFilter } from '../../filters'; +import { useEntityList } from '../../hooks'; +import { reduceCatalogFilters } from '../../utils'; +import useAsyncFn from 'react-use/lib/useAsyncFn'; + +export function useOwnedEntitiesCount() { + const identityApi = useApi(identityApiRef); + const catalogApi = useApi(catalogApiRef); + + const { filters } = useEntityList(); + + const { value: ownershipEntityRefs, loading: loadingEntityRefs } = useAsync( + async () => (await identityApi.getBackstageIdentity()).ownershipEntityRefs, + // load only on mount + [], + ); + + const prevRequest = useRef(); + + const request = useMemo(() => { + const { user, owners, ...allFilters } = filters; + const compacted = compact(Object.values(allFilters)); + const allFilter = reduceCatalogFilters(compacted); + const { ['metadata.name']: metadata, ...filter } = allFilter; + + const countFilter = getOwnedCountClaims(owners, ownershipEntityRefs); + + if ( + ownershipEntityRefs?.length === 0 || + countFilter === undefined || + Object.keys(filter).length === 0 + ) { + prevRequest.current = undefined; + return undefined; + } + const newRequest: QueryEntitiesInitialRequest = { + filter: { + ...filter, + 'relations.ownedBy': countFilter, + }, + limit: 0, + }; + + if (isEqual(newRequest, prevRequest.current)) { + return prevRequest.current; + } + + prevRequest.current = newRequest; + + return newRequest; + }, [filters, ownershipEntityRefs]); + + const [{ value: count, loading: loadingEntityOwnership }, fetchEntities] = + useAsyncFn( + async ( + req: QueryEntitiesInitialRequest | undefined, + ownershipEntityRefsParam: string[], + ) => { + if (ownershipEntityRefsParam && !req) { + // this implicitly means that there aren't claims in common with + // the logged in users, so avoid invoking the queryEntities endpoint + // which will implicitly returns 0 + return 0; + } + const { totalItems } = await catalogApi.queryEntities(req); + return totalItems; + }, + [], + { loading: true }, + ); + + useEffect(() => { + if (ownershipEntityRefs) { + if (request && Object.keys(request).length === 0) { + return; + } + fetchEntities(request, ownershipEntityRefs); + } + }, [fetchEntities, request, ownershipEntityRefs]); + + const loading = loadingEntityRefs || loadingEntityOwnership; + const filter = useMemo( + () => EntityUserFilter.owned(ownershipEntityRefs ?? []), + [ownershipEntityRefs], + ); + + return { + count, + loading, + filter, + ownershipEntityRefs, + }; +} + +function getOwnedCountClaims( + owners: EntityOwnerFilter | undefined, + ownershipEntityRefs: string[] | undefined, +) { + if (ownershipEntityRefs === undefined) { + return undefined; + } + const ownersRefs = owners?.values ?? []; + if (ownersRefs.length) { + const commonOwnedBy = intersection(ownersRefs, ownershipEntityRefs); + if (commonOwnedBy.length === 0) { + return undefined; + } + return commonOwnedBy; + } + return ownershipEntityRefs; +} diff --git a/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.test.tsx b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.test.tsx new file mode 100644 index 0000000000..5fe648e1bc --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.test.tsx @@ -0,0 +1,127 @@ +/* + * 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 React from 'react'; +import { CatalogApi } from '@backstage/catalog-client'; +import { EntityListProvider, useStarredEntities } from '../../hooks'; +import { catalogApiRef } from '../../api'; +import { ApiRef } from '@backstage/core-plugin-api'; +import { MemoryRouter } from 'react-router-dom'; +import { useStarredEntitiesCount } from './useStarredEntitiesCount'; +import { renderHook, waitFor } from '@testing-library/react'; + +const mockQueryEntities: jest.MockedFn = jest.fn(); +const mockCatalogApi: jest.Mocked> = { + queryEntities: mockQueryEntities, +}; + +const mockStarredEntities: jest.MockedFn<() => Set> = jest.fn(); + +const mockUseStarredEntities: ReturnType = { + get starredEntities() { + return mockStarredEntities(); + }, +} as ReturnType; + +jest.mock('../../hooks', () => { + const actual = jest.requireActual('../../hooks'); + return { ...actual, useStarredEntities: () => mockUseStarredEntities }; +}); + +jest.mock('@backstage/core-plugin-api', () => { + const actual = jest.requireActual('@backstage/core-plugin-api'); + return { + ...actual, + useApi: (ref: ApiRef) => + ref === catalogApiRef ? mockCatalogApi : actual.useApi(ref), + }; +}); + +describe('useStarredEntitiesCount', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + it('should return the count', async () => { + mockStarredEntities.mockReturnValue( + new Set(['component:default/favourite1', 'component:default/favourite2']), + ); + mockQueryEntities.mockResolvedValue({ + items: [ + { + apiVersion: '1', + kind: 'component', + metadata: { name: 'favourite1' }, + }, + { + apiVersion: '1', + kind: 'component', + metadata: { name: 'favourite2' }, + }, + ], + totalItems: 2, + pageInfo: {}, + }); + + const { result } = renderHook(() => useStarredEntitiesCount(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + await waitFor(() => { + expect(mockQueryEntities).toHaveBeenCalledWith({ + filter: { + 'metadata.name': ['favourite1', 'favourite2'], + }, + limit: 1000, + }); + expect(result.current).toEqual({ + count: 2, + loading: false, + filter: { + refs: [ + 'component:default/favourite1', + 'component:default/favourite2', + ], + value: 'starred', + }, + }); + }); + }); + + it(`shouldn't invoke the endpoint if there are no starred entities`, async () => { + mockStarredEntities.mockReturnValue(new Set()); + + const { result } = renderHook(() => useStarredEntitiesCount(), { + wrapper: ({ children }) => ( + + {children} + + ), + }); + + await expect( + waitFor(() => expect(mockQueryEntities).toHaveBeenCalled()), + ).rejects.toThrow(); + expect(result.current).toEqual({ + count: 0, + loading: false, + filter: { refs: [], value: 'starred' }, + }); + }); +}); diff --git a/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts new file mode 100644 index 0000000000..f7b2b101f3 --- /dev/null +++ b/plugins/catalog-react/src/components/UserListPicker/useStarredEntitiesCount.ts @@ -0,0 +1,95 @@ +/* + * 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 { QueryEntitiesInitialRequest } from '@backstage/catalog-client'; +import { parseEntityRef, stringifyEntityRef } from '@backstage/catalog-model'; +import { useApi } from '@backstage/core-plugin-api'; +import { compact, isEqual } from 'lodash'; +import { useMemo, useRef } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import { catalogApiRef } from '../../api'; +import { EntityUserFilter } from '../../filters'; +import { useEntityList, useStarredEntities } from '../../hooks'; +import { reduceCatalogFilters } from '../../utils'; + +export function useStarredEntitiesCount() { + const catalogApi = useApi(catalogApiRef); + const { filters } = useEntityList(); + const { starredEntities } = useStarredEntities(); + + const prevRequest = useRef(); + const request = useMemo(() => { + const { user, ...allFilters } = filters; + const compacted = compact(Object.values(allFilters)); + const filter = reduceCatalogFilters(compacted); + + const facet = 'metadata.name'; + + const newRequest: QueryEntitiesInitialRequest = { + filter: { + ...filter, + /** + * here we are filtering entities by `name`. Given this filter, + * the response might contain more entities than expected, in case multiple entities + * of different kind or namespace share the same name. Those extra entities are filtered out + * client side by `EntityUserFilter`, so they won't be visible to the user. + */ + [facet]: Array.from(starredEntities).map(e => parseEntityRef(e).name), + }, + /** + * limit is set to a high value as we are not expecting many starred entities + */ + limit: 1000, + }; + if (isEqual(newRequest, prevRequest.current)) { + return prevRequest.current; + } + prevRequest.current = newRequest; + + return newRequest; + }, [filters, starredEntities]); + + const { value: count, loading } = useAsync(async () => { + if (!starredEntities.size) { + return 0; + } + + /** + * given a list of starred entity refs and some filters coming from CatalogPage, + * it reduces the list of starred entities, to a list of entities that matches the + * provided filters. It won't be possible to getEntitiesByRefs + * as the method doesn't accept any filter. + */ + const response = await catalogApi.queryEntities(request); + + return response.items + .map(e => + stringifyEntityRef({ + kind: e.kind, + namespace: e.metadata.namespace, + name: e.metadata.name, + }), + ) + .filter(e => starredEntities.has(e)).length; + }, [request, starredEntities]); + + const filter = useMemo( + () => EntityUserFilter.starred(Array.from(starredEntities)), + [starredEntities], + ); + + return { count, loading, filter }; +} diff --git a/plugins/catalog-react/src/components/index.ts b/plugins/catalog-react/src/components/index.ts index f77d13d2d3..5b7247cdbd 100644 --- a/plugins/catalog-react/src/components/index.ts +++ b/plugins/catalog-react/src/components/index.ts @@ -18,6 +18,7 @@ export * from './CatalogFilterLayout'; export * from './EntityKindPicker'; export * from './EntityLifecyclePicker'; export * from './EntityOwnerPicker'; +export * from './EntityDisplayName'; export * from './EntityRefLink'; export * from './EntityPeekAheadPopover'; export * from './EntitySearchBar'; diff --git a/plugins/catalog-react/src/filters.ts b/plugins/catalog-react/src/filters.ts index c717a35202..55983d124b 100644 --- a/plugins/catalog-react/src/filters.ts +++ b/plugins/catalog-react/src/filters.ts @@ -72,6 +72,10 @@ export class EntityTagFilter implements EntityFilter { return this.values.every(v => (entity.metadata.tags ?? []).includes(v)); } + getCatalogFilters(): Record { + return { 'metadata.tags': this.values }; + } + toQueryValue(): string[] { return this.values; } @@ -136,6 +140,10 @@ export class EntityOwnerFilter implements EntityFilter { }, [] as string[]); } + getCatalogFilters(): Record { + return { 'relations.ownedBy': this.values }; + } + filterEntity(entity: Entity): boolean { return this.values.some(v => getEntityRelations(entity, RELATION_OWNED_BY).some( @@ -160,6 +168,10 @@ export class EntityOwnerFilter implements EntityFilter { export class EntityLifecycleFilter implements EntityFilter { constructor(readonly values: string[]) {} + getCatalogFilters(): Record { + return { 'spec.lifecycle': this.values }; + } + filterEntity(entity: Entity): boolean { return this.values.some(v => entity.spec?.lifecycle === v); } @@ -176,6 +188,9 @@ export class EntityLifecycleFilter implements EntityFilter { export class EntityNamespaceFilter implements EntityFilter { constructor(readonly values: string[]) {} + getCatalogFilters(): Record { + return { 'metadata.namespace': this.values }; + } filterEntity(entity: Entity): boolean { return this.values.some(v => entity.metadata.namespace === v); } @@ -185,8 +200,66 @@ export class EntityNamespaceFilter implements EntityFilter { } } +/** + * @public + */ +export class EntityUserFilter implements EntityFilter { + private constructor( + readonly value: UserListFilterKind, + readonly refs?: string[], + ) {} + + static owned(ownershipEntityRefs: string[]) { + return new EntityUserFilter('owned', ownershipEntityRefs); + } + + static all() { + return new EntityUserFilter('all'); + } + + static starred(starredEntityRefs: string[]) { + return new EntityUserFilter('starred', starredEntityRefs); + } + + getCatalogFilters(): Record { + if (this.value === 'owned') { + return { 'relations.ownedBy': this.refs ?? [] }; + } + if (this.value === 'starred') { + return { + 'metadata.name': this.refs?.map(e => parseEntityRef(e).name) ?? [], + }; + } + return {}; + } + + filterEntity(entity: Entity) { + if (this.value === 'starred') { + return this.refs?.includes(stringifyEntityRef(entity)) ?? true; + } + // used only for retro-compatibility with non paginated data. + // This is supposed to return always true for paginated + // owned entities, since the filters are applied server side. + if (this.value === 'owned') { + const relations = getEntityRelations(entity, RELATION_OWNED_BY); + + return ( + this.refs?.some(v => + relations.some(o => stringifyEntityRef(o) === v), + ) ?? false + ); + } + return true; + } + + toQueryValue(): string { + return this.value; + } +} + /** * Filters entities based on whatever the user has starred or owns them. + * @deprecated use EntityUserFilter * @public */ export class UserListFilter implements EntityFilter { @@ -218,6 +291,14 @@ export class UserListFilter implements EntityFilter { */ export class EntityOrphanFilter implements EntityFilter { constructor(readonly value: boolean) {} + + getCatalogFilters(): Record { + if (this.value) { + return { 'metadata.annotations.backstage.io/orphan': String(this.value) }; + } + return {}; + } + filterEntity(entity: Entity): boolean { const orphan = entity.metadata.annotations?.['backstage.io/orphan']; return orphan !== undefined && this.value.toString() === orphan; @@ -230,6 +311,7 @@ export class EntityOrphanFilter implements EntityFilter { */ export class EntityErrorFilter implements EntityFilter { constructor(readonly value: boolean) {} + filterEntity(entity: Entity): boolean { const error = ((entity as AlphaEntity)?.status?.items?.length as number) > 0; diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx index d77655ebcc..edbfb65c0c 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.test.tsx @@ -32,7 +32,11 @@ import { MemoryRouter } from 'react-router-dom'; import { catalogApiRef } from '../api'; import { starredEntitiesApiRef, MockStarredEntitiesApi } from '../apis'; import { EntityKindPicker, UserListPicker } from '../components'; -import { EntityKindFilter, EntityTypeFilter, UserListFilter } from '../filters'; +import { + EntityKindFilter, + EntityTypeFilter, + EntityUserFilter, +} from '../filters'; import { UserListFilterKind } from '../types'; import { EntityListProvider, useEntityList } from './useEntityListProvider'; @@ -62,11 +66,14 @@ const entities: Entity[] = [ const mockConfigApi = { getOptionalString: () => '', } as Partial; + +const ownershipEntityRefs = ['user:default/guest']; + const mockIdentityApi: Partial = { getBackstageIdentity: async () => ({ type: 'user', userEntityRef: 'user:default/guest', - ownershipEntityRefs: [], + ownershipEntityRefs, }), getCredentials: async () => ({ token: undefined }), }; @@ -148,11 +155,7 @@ describe('', () => { act(() => result.current.updateFilters({ - user: new UserListFilter( - 'owned', - entity => entity.metadata.name === 'component-1', - () => true, - ), + user: EntityUserFilter.owned(ownershipEntityRefs), }), ); @@ -193,11 +196,7 @@ describe('', () => { act(() => result.current.updateFilters({ - user: new UserListFilter( - 'owned', - entity => entity.metadata.name === 'component-1', - () => true, - ), + user: EntityUserFilter.owned(ownershipEntityRefs), }), ); diff --git a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx index b4c464a26c..948efc969f 100644 --- a/plugins/catalog-react/src/hooks/useEntityListProvider.tsx +++ b/plugins/catalog-react/src/hooks/useEntityListProvider.tsx @@ -41,16 +41,17 @@ import { EntityTypeFilter, UserListFilter, EntityNamespaceFilter, + EntityUserFilter, } from '../filters'; import { EntityFilter } from '../types'; -import { reduceCatalogFilters, reduceEntityFilters } from '../utils'; +import { reduceBackendCatalogFilters, reduceEntityFilters } from '../utils'; import { useApi } from '@backstage/core-plugin-api'; /** @public */ export type DefaultEntityFilters = { kind?: EntityKindFilter; type?: EntityTypeFilter; - user?: UserListFilter; + user?: UserListFilter | EntityUserFilter; owners?: EntityOwnerFilter; lifecycles?: EntityLifecycleFilter; tags?: EntityTagFilter; @@ -156,8 +157,8 @@ export const EntityListProvider = ( async () => { const compacted = compact(Object.values(requestedFilters)); const entityFilter = reduceEntityFilters(compacted); - const backendFilter = reduceCatalogFilters(compacted); - const previousBackendFilter = reduceCatalogFilters( + const backendFilter = reduceBackendCatalogFilters(compacted); + const previousBackendFilter = reduceBackendCatalogFilters( compact(Object.values(outputState.appliedFilters)), ); diff --git a/plugins/catalog-react/src/hooks/useEntityOwnership.ts b/plugins/catalog-react/src/hooks/useEntityOwnership.ts index f8a37a2c83..fdd73434ec 100644 --- a/plugins/catalog-react/src/hooks/useEntityOwnership.ts +++ b/plugins/catalog-react/src/hooks/useEntityOwnership.ts @@ -41,13 +41,18 @@ export function useEntityOwnership(): { const identityApi = useApi(identityApiRef); // Trigger load only on mount - const { loading, value: refs } = useAsync(async () => { - const { ownershipEntityRefs } = await identityApi.getBackstageIdentity(); - return ownershipEntityRefs; - }, []); + const { loading, value: refs } = useAsync( + async () => { + const { ownershipEntityRefs } = await identityApi.getBackstageIdentity(); + return ownershipEntityRefs; + }, + // load only on mount + [], + ); const isOwnedEntity = useMemo(() => { const myOwnerRefs = new Set(refs ?? []); + return (entity: Entity) => { const entityOwnerRefs = getEntityRelations(entity, RELATION_OWNED_BY).map( stringifyEntityRef, @@ -61,5 +66,5 @@ export function useEntityOwnership(): { }; }, [refs]); - return useMemo(() => ({ loading, isOwnedEntity }), [loading, isOwnedEntity]); + return { loading, isOwnedEntity }; } diff --git a/plugins/catalog-react/src/hooks/useStarredEntities.test.tsx b/plugins/catalog-react/src/hooks/useStarredEntities.test.tsx index e3010a0f4a..07738fd3d9 100644 --- a/plugins/catalog-react/src/hooks/useStarredEntities.test.tsx +++ b/plugins/catalog-react/src/hooks/useStarredEntities.test.tsx @@ -16,7 +16,8 @@ import { Entity } from '@backstage/catalog-model'; import { TestApiProvider } from '@backstage/test-utils'; -import { act, renderHook, waitFor } from '@testing-library/react'; +import { act, renderHook } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; import React, { PropsWithChildren } from 'react'; import { starredEntitiesApiRef, diff --git a/plugins/catalog-react/src/hooks/useStarredEntity.test.tsx b/plugins/catalog-react/src/hooks/useStarredEntity.test.tsx index e64a9bbdf8..44a4c7aaa3 100644 --- a/plugins/catalog-react/src/hooks/useStarredEntity.test.tsx +++ b/plugins/catalog-react/src/hooks/useStarredEntity.test.tsx @@ -16,7 +16,8 @@ import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; import { TestApiProvider } from '@backstage/test-utils'; -import { renderHook, waitFor } from '@testing-library/react'; +import { waitFor } from '@testing-library/react'; +import { renderHook } from '@testing-library/react'; import React, { PropsWithChildren } from 'react'; import Observable from 'zen-observable'; import { StarredEntitiesApi, starredEntitiesApiRef } from '../apis'; diff --git a/plugins/catalog-react/src/overridableComponents.ts b/plugins/catalog-react/src/overridableComponents.ts index c3b392fcfe..e94af1c4fb 100644 --- a/plugins/catalog-react/src/overridableComponents.ts +++ b/plugins/catalog-react/src/overridableComponents.ts @@ -13,11 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Overrides } from '@material-ui/core/styles/overrides'; import { StyleRules } from '@material-ui/core/styles/withStyles'; import { CatalogReactUserListPickerClassKey, + CatalogReactEntityDisplayNameClassKey, CatalogReactEntityLifecyclePickerClassKey, CatalogReactEntitySearchBarClassKey, CatalogReactEntityTagPickerClassKey, @@ -28,6 +30,7 @@ import { /** @public */ export type CatalogReactComponentsNameToClassKey = { CatalogReactUserListPicker: CatalogReactUserListPickerClassKey; + CatalogReactEntityDisplayName: CatalogReactEntityDisplayNameClassKey; CatalogReactEntityLifecyclePicker: CatalogReactEntityLifecyclePickerClassKey; CatalogReactEntitySearchBar: CatalogReactEntitySearchBarClassKey; CatalogReactEntityTagPicker: CatalogReactEntityTagPickerClassKey; diff --git a/plugins/catalog-react/src/utils/filters.ts b/plugins/catalog-react/src/utils/filters.ts index 109e864c52..580d7b0b76 100644 --- a/plugins/catalog-react/src/utils/filters.ts +++ b/plugins/catalog-react/src/utils/filters.ts @@ -16,6 +16,16 @@ import { Entity } from '@backstage/catalog-model'; import { EntityFilter } from '../types'; +import { + EntityLifecycleFilter, + EntityNamespaceFilter, + EntityOrphanFilter, + EntityOwnerFilter, + EntityTagFilter, + EntityTextFilter, + EntityUserFilter, + UserListFilter, +} from '../filters'; export function reduceCatalogFilters( filters: EntityFilter[], @@ -28,6 +38,38 @@ export function reduceCatalogFilters( }, {} as Record); } +/** + * This function computes and returns an object containing the filters to be sent + * to the backend. Any filter coming from `EntityKindFilter` and `EntityTypeFilter`, together + * with custom filter set by the adopters is allowed. This function is used by `EntityListProvider` + * and it won't be needed anymore in the future once pagination is implemented, as all the filters + * will be applied backend-side. + */ +export function reduceBackendCatalogFilters(filters: EntityFilter[]) { + const backendCatalogFilters: Record< + string, + string | symbol | (string | symbol)[] + > = {}; + + filters.forEach(filter => { + if ( + filter instanceof EntityTagFilter || + filter instanceof EntityOwnerFilter || + filter instanceof EntityLifecycleFilter || + filter instanceof EntityNamespaceFilter || + filter instanceof EntityUserFilter || + filter instanceof EntityOrphanFilter || + filter instanceof EntityTextFilter || + filter instanceof UserListFilter + ) { + return; + } + Object.assign(backendCatalogFilters, filter.getCatalogFilters?.() || {}); + }); + + return backendCatalogFilters; +} + export function reduceEntityFilters( filters: EntityFilter[], ): (entity: Entity) => boolean { diff --git a/plugins/catalog-unprocessed-entities/CHANGELOG.md b/plugins/catalog-unprocessed-entities/CHANGELOG.md index afd3890a12..a3039ed051 100644 --- a/plugins/catalog-unprocessed-entities/CHANGELOG.md +++ b/plugins/catalog-unprocessed-entities/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-catalog-unprocessed-entities +## 0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.4 ### Patch Changes diff --git a/plugins/catalog-unprocessed-entities/package.json b/plugins/catalog-unprocessed-entities/package.json index b4d95ac20a..113d0ed648 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.1.4", + "version": "0.1.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/catalog/CHANGELOG.md b/plugins/catalog/CHANGELOG.md index 6b5ba8b956..1bbe6ed199 100644 --- a/plugins/catalog/CHANGELOG.md +++ b/plugins/catalog/CHANGELOG.md @@ -1,5 +1,43 @@ # @backstage/plugin-catalog +## 1.15.0-next.0 + +### Minor Changes + +- 1e5b7d993a: Added the `DefaultEntityPresentationApi`, which is an implementation of the + `EntityPresentationApi` that `@backstage/plugin-catalog-react` exposes through + its `entityPresentationApiRef`. This implementation is also by default made + available automatically by the catalog plugin, unless you replace it with a + custom one. It batch fetches and caches data from the catalog as needed for + display, and is customizable by adopters to add their own rendering functions. + +### Patch Changes + +- 8a8445663b: Migrate catalog entity cards to new frontend system extension format. +- e964c17db9: Use default extensions boundary and suspense on the alpha declarative `createCatalogFilterExtension` extension factory. +- 71c97e7d73: The `spec.lifecycle' field in entities will now always be rendered as a string. +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Initial entity page implementation for new frontend system at `/alpha`, with an overview page enabled by default and the about card available as an optional card. +- bb98953cb9: Create declarative extensions for the `Catalog` plugin; this initial plugin preset contains sidebar item, index page and filter extensions, all distributed via `/alpha` subpath. + + The `EntityPage` will be migrated in a follow-up patch. + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-search-common@1.2.7 + ## 1.14.0 ### Minor Changes diff --git a/plugins/catalog/alpha-api-report.md b/plugins/catalog/alpha-api-report.md index aba33fc092..43e589bdc3 100644 --- a/plugins/catalog/alpha-api-report.md +++ b/plugins/catalog/alpha-api-report.md @@ -12,14 +12,6 @@ import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; import { PortableSchema } from '@backstage/frontend-plugin-api'; import { RouteRef } from '@backstage/frontend-plugin-api'; -// @alpha (undocumented) -export const CatalogApi: Extension<{}>; - -// @alpha (undocumented) -export const CatalogSearchResultListItemExtension: Extension<{ - noTrack?: boolean | undefined; -}>; - // @alpha (undocumented) export function createCatalogFilterExtension< TInputs extends AnyExtensionInputMap, @@ -62,8 +54,5 @@ const _default: BackstagePlugin< >; export default _default; -// @alpha (undocumented) -export const StarredEntitiesApi: Extension<{}>; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 8173c395ba..6647f8deaa 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -7,11 +7,16 @@ import { ApiHolder } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { CatalogApi } from '@backstage/plugin-catalog-react'; import { ComponentEntity } from '@backstage/catalog-model'; import { CompoundEntityRef } from '@backstage/catalog-model'; import { Entity } from '@backstage/catalog-model'; import { EntityOwnerPickerProps } from '@backstage/plugin-catalog-react'; +import { EntityPresentationApi } from '@backstage/plugin-catalog-react'; +import { EntityRefPresentation } from '@backstage/plugin-catalog-react'; +import { EntityRefPresentationSnapshot } from '@backstage/plugin-catalog-react'; import { ExternalRouteRef } from '@backstage/core-plugin-api'; +import { HumanDuration } from '@backstage/types'; import { IconComponent } from '@backstage/core-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { InfoCardVariants } from '@backstage/core-components'; @@ -196,6 +201,7 @@ export interface CatalogTableRow { // (undocumented) resolved: { name: string; + entityRef: string; partOfSystemRelationTitle?: string; partOfSystemRelations: CompoundEntityRef[]; ownedByRelationsTitle?: string; @@ -224,6 +230,47 @@ export interface DefaultCatalogPageProps { tableOptions?: TableProps['options']; } +// @public +export class DefaultEntityPresentationApi implements EntityPresentationApi { + static create( + options: DefaultEntityPresentationApiOptions, + ): EntityPresentationApi; + static createLocal(): EntityPresentationApi; + // (undocumented) + forEntity( + entityOrRef: Entity | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, + ): EntityRefPresentation; +} + +// @public +export interface DefaultEntityPresentationApiOptions { + batchDelay?: HumanDuration; + cacheTtl?: HumanDuration; + catalogApi?: CatalogApi; + kindIcons?: Record; + renderer?: DefaultEntityPresentationApiRenderer; +} + +// @public +export interface DefaultEntityPresentationApiRenderer { + async?: boolean; + render: (options: { + entityRef: string; + loading: boolean; + entity: Entity | undefined; + context: { + defaultKind?: string; + defaultNamespace?: string; + }; + }) => { + snapshot: Omit; + }; +} + // @public export class DefaultStarredEntitiesApi implements StarredEntitiesApi { constructor(opts: { storageApi: StorageApi }); diff --git a/plugins/catalog/package.json b/plugins/catalog/package.json index 56ae771314..013f49ba2e 100644 --- a/plugins/catalog/package.json +++ b/plugins/catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-catalog", "description": "The Backstage plugin for browsing the Backstage catalog", - "version": "1.14.0", + "version": "1.15.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -10,13 +10,13 @@ }, "exports": { ".": "./src/index.ts", - "./alpha": "./src/alpha.tsx", + "./alpha": "./src/alpha.ts", "./package.json": "./package.json" }, "typesVersions": { "*": { "alpha": [ - "src/alpha.tsx" + "src/alpha.ts" ], "package.json": [ "package.json" @@ -64,6 +64,8 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@types/react": "^16.13.1 || ^17.0.0", + "dataloader": "^2.0.0", + "expiry-map": "^2.0.0", "history": "^5.0.0", "lodash": "^4.17.21", "pluralize": "^8.0.0", diff --git a/plugins/catalog-react/src/alpha.ts b/plugins/catalog/src/alpha.ts similarity index 85% rename from plugins/catalog-react/src/alpha.ts rename to plugins/catalog/src/alpha.ts index e8ff21609e..e80f131817 100644 --- a/plugins/catalog-react/src/alpha.ts +++ b/plugins/catalog/src/alpha.ts @@ -14,5 +14,5 @@ * limitations under the License. */ -export { isOwnerOf } from './utils'; -export { useEntityPermission } from './hooks/useEntityPermission'; +export * from './alpha/index'; +export { default } from './alpha/index'; diff --git a/plugins/catalog/src/alpha.tsx b/plugins/catalog/src/alpha.tsx deleted file mode 100644 index 31551c1930..0000000000 --- a/plugins/catalog/src/alpha.tsx +++ /dev/null @@ -1,285 +0,0 @@ -/* - * 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 React from 'react'; -import HomeIcon from '@material-ui/icons/Home'; -import { - createApiFactory, - discoveryApiRef, - fetchApiRef, - storageApiRef, -} from '@backstage/core-plugin-api'; -import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha'; -import { CatalogClient } from '@backstage/catalog-client'; -import { - createSchemaFromZod, - createApiExtension, - createPageExtension, - createPlugin, - createNavItemExtension, - createExtension, - coreExtensionData, - AnyExtensionInputMap, - PortableSchema, - ExtensionBoundary, - createExtensionInput, -} from '@backstage/frontend-plugin-api'; -import { - AsyncEntityProvider, - catalogApiRef, - entityRouteRef, - starredEntitiesApiRef, -} from '@backstage/plugin-catalog-react'; -import { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha'; -import { DefaultStarredEntitiesApi } from './apis'; -import { - createComponentRouteRef, - createFromTemplateRouteRef, - rootRouteRef, - viewTechDocRouteRef, -} from './routes'; -import { useEntityFromUrl } from './components/CatalogEntityPage/useEntityFromUrl'; - -/** @alpha */ -export const CatalogApi = createApiExtension({ - factory: createApiFactory({ - api: catalogApiRef, - deps: { - discoveryApi: discoveryApiRef, - fetchApi: fetchApiRef, - }, - factory: ({ discoveryApi, fetchApi }) => - new CatalogClient({ discoveryApi, fetchApi }), - }), -}); - -/** @alpha */ -export const StarredEntitiesApi = createApiExtension({ - factory: createApiFactory({ - api: starredEntitiesApiRef, - deps: { storageApi: storageApiRef }, - factory: ({ storageApi }) => new DefaultStarredEntitiesApi({ storageApi }), - }), -}); - -/** @alpha */ -export const CatalogSearchResultListItemExtension = - createSearchResultListItemExtension({ - id: 'catalog', - predicate: result => result.type === 'software-catalog', - component: () => - import('./components/CatalogSearchResultListItem').then( - m => m.CatalogSearchResultListItem, - ), - }); - -/** @alpha */ -export function createCatalogFilterExtension< - TInputs extends AnyExtensionInputMap, - TConfig = never, ->(options: { - id: string; - inputs?: TInputs; - configSchema?: PortableSchema; - loader: (options: { config: TConfig }) => Promise; -}) { - const id = `catalog.filter.${options.id}`; - - return createExtension({ - id, - attachTo: { id: 'plugin.catalog.page.index', input: 'filters' }, - inputs: options.inputs ?? {}, - configSchema: options.configSchema, - output: { - element: coreExtensionData.reactElement, - }, - factory({ bind, config, source }) { - const ExtensionComponent = React.lazy(() => - options - .loader({ config }) - .then(element => ({ default: () => element })), - ); - - bind({ - element: ( - - - - ), - }); - }, - }); -} - -const CatalogEntityTagFilter = createCatalogFilterExtension({ - id: 'entity.tag', - loader: async () => { - const { EntityTagPicker } = await import('@backstage/plugin-catalog-react'); - return ; - }, -}); - -const CatalogEntityKindFilter = createCatalogFilterExtension({ - id: 'entity.kind', - configSchema: createSchemaFromZod(z => - z.object({ - initialFilter: z.string().default('component'), - }), - ), - loader: async ({ config }) => { - const { EntityKindPicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogEntityTypeFilter = createCatalogFilterExtension({ - id: 'entity.type', - loader: async () => { - const { EntityTypePicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogEntityOwnerFilter = createCatalogFilterExtension({ - id: 'entity.mode', - configSchema: createSchemaFromZod(z => - z.object({ - mode: z.enum(['owners-only', 'all']).optional(), - }), - ), - loader: async ({ config }) => { - const { EntityOwnerPicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogEntityNamespaceFilter = createCatalogFilterExtension({ - id: 'entity.namespace', - loader: async () => { - const { EntityNamespacePicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogEntityLifecycleFilter = createCatalogFilterExtension({ - id: 'entity.lifecycle', - loader: async () => { - const { EntityLifecyclePicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogEntityProcessingStatusFilter = createCatalogFilterExtension({ - id: 'entity.processing.status', - loader: async () => { - const { EntityProcessingStatusPicker } = await import( - '@backstage/plugin-catalog-react' - ); - return ; - }, -}); - -const CatalogUserListFilter = createCatalogFilterExtension({ - id: 'user.list', - configSchema: createSchemaFromZod(z => - z.object({ - initialFilter: z.enum(['owned', 'starred', 'all']).default('owned'), - }), - ), - loader: async ({ config }) => { - const { UserListPicker } = await import('@backstage/plugin-catalog-react'); - return ; - }, -}); - -const CatalogIndexPage = createPageExtension({ - id: 'plugin.catalog.page.index', - defaultPath: '/catalog', - routeRef: convertLegacyRouteRef(rootRouteRef), - inputs: { - filters: createExtensionInput({ - element: coreExtensionData.reactElement, - }), - }, - loader: async ({ inputs }) => { - const { BaseCatalogPage } = await import('./components/CatalogPage'); - const filters = inputs.filters.map(filter => filter.element); - return {filters}} />; - }, -}); - -const CatalogEntityPage = createPageExtension({ - id: 'plugin.catalog.page.entity', - defaultPath: '/catalog/:namespace/:kind/:name', - routeRef: convertLegacyRouteRef(entityRouteRef), - loader: async () => { - const Component = () => { - return ( - -
🚧 Work In Progress
-
- ); - }; - return ; - }, -}); - -const CatalogNavItem = createNavItemExtension({ - id: 'catalog.nav.index', - routeRef: convertLegacyRouteRef(rootRouteRef), - title: 'Catalog', - icon: HomeIcon, -}); - -/** @alpha */ -export default createPlugin({ - id: 'catalog', - routes: { - catalogIndex: convertLegacyRouteRef(rootRouteRef), - catalogEntity: convertLegacyRouteRef(entityRouteRef), - }, - externalRoutes: { - viewTechDoc: convertLegacyRouteRef(viewTechDocRouteRef), - createComponent: convertLegacyRouteRef(createComponentRouteRef), - createFromTemplate: convertLegacyRouteRef(createFromTemplateRouteRef), - }, - extensions: [ - CatalogApi, - StarredEntitiesApi, - CatalogSearchResultListItemExtension, - CatalogEntityKindFilter, - CatalogEntityTypeFilter, - CatalogUserListFilter, - CatalogEntityOwnerFilter, - CatalogEntityLifecycleFilter, - CatalogEntityTagFilter, - CatalogEntityProcessingStatusFilter, - CatalogEntityNamespaceFilter, - CatalogIndexPage, - CatalogEntityPage, - CatalogNavItem, - ], -}); diff --git a/plugins/catalog/src/alpha/EntityOverviewPage.tsx b/plugins/catalog/src/alpha/EntityOverviewPage.tsx new file mode 100644 index 0000000000..baa93845ae --- /dev/null +++ b/plugins/catalog/src/alpha/EntityOverviewPage.tsx @@ -0,0 +1,42 @@ +/* + * 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 React from 'react'; +import { useEntity } from '@backstage/plugin-catalog-react'; +import { Entity } from '@backstage/catalog-model'; +import Grid from '@material-ui/core/Grid'; + +interface EntityOverviewPageProps { + cards: Array<{ + element: React.JSX.Element; + filter: (ctx: { entity: Entity }) => boolean; + }>; +} + +export function EntityOverviewPage(props: EntityOverviewPageProps) { + const { entity } = useEntity(); + return ( + + {props.cards + .filter(card => card.filter({ entity })) + .map(card => ( + + {card.element} + + ))} + + ); +} diff --git a/plugins/catalog/src/alpha/apis.tsx b/plugins/catalog/src/alpha/apis.tsx new file mode 100644 index 0000000000..a887a33a04 --- /dev/null +++ b/plugins/catalog/src/alpha/apis.tsx @@ -0,0 +1,51 @@ +/* + * 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 { + createApiFactory, + discoveryApiRef, + fetchApiRef, + storageApiRef, +} from '@backstage/core-plugin-api'; +import { CatalogClient } from '@backstage/catalog-client'; +import { createApiExtension } from '@backstage/frontend-plugin-api'; +import { + catalogApiRef, + starredEntitiesApiRef, +} from '@backstage/plugin-catalog-react'; +import { DefaultStarredEntitiesApi } from '../apis'; + +export const CatalogApi = createApiExtension({ + factory: createApiFactory({ + api: catalogApiRef, + deps: { + discoveryApi: discoveryApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ discoveryApi, fetchApi }) => + new CatalogClient({ discoveryApi, fetchApi }), + }), +}); + +export const StarredEntitiesApi = createApiExtension({ + factory: createApiFactory({ + api: starredEntitiesApiRef, + deps: { storageApi: storageApiRef }, + factory: ({ storageApi }) => new DefaultStarredEntitiesApi({ storageApi }), + }), +}); + +export default [CatalogApi, StarredEntitiesApi]; diff --git a/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx new file mode 100644 index 0000000000..cf3dbd318a --- /dev/null +++ b/plugins/catalog/src/alpha/createCatalogFilterExtension.tsx @@ -0,0 +1,62 @@ +/* + * 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 React, { lazy } from 'react'; +import { + AnyExtensionInputMap, + ExtensionBoundary, + PortableSchema, + coreExtensionData, + createExtension, +} from '@backstage/frontend-plugin-api'; + +/** @alpha */ +export function createCatalogFilterExtension< + TInputs extends AnyExtensionInputMap, + TConfig = never, +>(options: { + id: string; + inputs?: TInputs; + configSchema?: PortableSchema; + loader: (options: { config: TConfig }) => Promise; +}) { + const id = `catalog.filter.${options.id}`; + + return createExtension({ + id, + attachTo: { id: 'plugin.catalog.page.index', input: 'filters' }, + inputs: options.inputs ?? {}, + configSchema: options.configSchema, + output: { + element: coreExtensionData.reactElement, + }, + factory({ config, source }) { + const ExtensionComponent = lazy(() => + options + .loader({ config }) + .then(element => ({ default: () => element })), + ); + + return { + element: ( + + + + ), + }; + }, + }); +} diff --git a/plugins/catalog/src/alpha/entityCards.tsx b/plugins/catalog/src/alpha/entityCards.tsx new file mode 100644 index 0000000000..c7b75679a7 --- /dev/null +++ b/plugins/catalog/src/alpha/entityCards.tsx @@ -0,0 +1,104 @@ +/* + * 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 React from 'react'; +import { createEntityCardExtension } from '@backstage/plugin-catalog-react/alpha'; + +export const EntityAboutCard = createEntityCardExtension({ + id: 'about', + loader: async () => + import('../components/AboutCard').then(m => ( + + )), +}); + +export const EntityLinksCard = createEntityCardExtension({ + id: 'links', + filter: ({ entity }) => Boolean(entity.metadata.links), + loader: async () => + import('../components/EntityLinksCard').then(m => { + return ; + }), +}); + +export const EntityLabelsCard = createEntityCardExtension({ + id: 'labels', + filter: ({ entity }) => Boolean(entity.metadata.labels), + loader: async () => + import('../components/EntityLabelsCard').then(m => ( + + )), +}); + +export const EntityDependsOnComponentsCard = createEntityCardExtension({ + id: 'dependsOn.components', + loader: async () => + import('../components/DependsOnComponentsCard').then(m => ( + + )), +}); + +export const EntityDependsOnResourcesCard = createEntityCardExtension({ + id: 'dependsOn.resources', + loader: async () => + import('../components/DependsOnResourcesCard').then(m => ( + + )), +}); + +export const EntityHasComponentsCard = createEntityCardExtension({ + id: 'has.components', + loader: async () => + import('../components/HasComponentsCard').then(m => ( + + )), +}); + +export const EntityHasResourcesCard = createEntityCardExtension({ + id: 'has.resources', + loader: async () => + import('../components/HasResourcesCard').then(m => ( + + )), +}); + +export const EntityHasSubcomponentsCard = createEntityCardExtension({ + id: 'has.subcomponents', + loader: async () => + import('../components/HasSubcomponentsCard').then(m => ( + + )), +}); + +export const EntityHasSystemsCard = createEntityCardExtension({ + id: 'has.systems', + loader: async () => + import('../components/HasSystemsCard').then(m => ( + + )), +}); + +export default [ + EntityAboutCard, + EntityLinksCard, + EntityLabelsCard, + EntityDependsOnComponentsCard, + EntityDependsOnResourcesCard, + EntityHasComponentsCard, + EntityHasResourcesCard, + EntityHasSubcomponentsCard, + EntityHasSystemsCard, +]; diff --git a/plugins/catalog/src/alpha/entityContents.tsx b/plugins/catalog/src/alpha/entityContents.tsx new file mode 100644 index 0000000000..9bef4a5e0d --- /dev/null +++ b/plugins/catalog/src/alpha/entityContents.tsx @@ -0,0 +1,44 @@ +/* + * 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 React from 'react'; +import { + coreExtensionData, + createExtensionInput, +} from '@backstage/frontend-plugin-api'; +import { + createEntityContentExtension, + entityFilterExtensionDataRef, +} from '@backstage/plugin-catalog-react/alpha'; + +export const OverviewEntityContent = createEntityContentExtension({ + id: 'overview', + defaultPath: '/', + defaultTitle: 'Overview', + disabled: false, + inputs: { + cards: createExtensionInput({ + element: coreExtensionData.reactElement, + filter: entityFilterExtensionDataRef, + }), + }, + loader: async ({ inputs }) => + import('./EntityOverviewPage').then(m => ( + + )), +}); + +export default [OverviewEntityContent]; diff --git a/plugins/catalog/src/alpha/filters.tsx b/plugins/catalog/src/alpha/filters.tsx new file mode 100644 index 0000000000..7b73c2d14b --- /dev/null +++ b/plugins/catalog/src/alpha/filters.tsx @@ -0,0 +1,121 @@ +/* + * 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 React from 'react'; +import { createCatalogFilterExtension } from './createCatalogFilterExtension'; +import { createSchemaFromZod } from '@backstage/frontend-plugin-api'; + +const CatalogEntityTagFilter = createCatalogFilterExtension({ + id: 'entity.tag', + loader: async () => { + const { EntityTagPicker } = await import('@backstage/plugin-catalog-react'); + return ; + }, +}); + +const CatalogEntityKindFilter = createCatalogFilterExtension({ + id: 'entity.kind', + configSchema: createSchemaFromZod(z => + z.object({ + initialFilter: z.string().default('component'), + }), + ), + loader: async ({ config }) => { + const { EntityKindPicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogEntityTypeFilter = createCatalogFilterExtension({ + id: 'entity.type', + loader: async () => { + const { EntityTypePicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogEntityOwnerFilter = createCatalogFilterExtension({ + id: 'entity.mode', + configSchema: createSchemaFromZod(z => + z.object({ + mode: z.enum(['owners-only', 'all']).optional(), + }), + ), + loader: async ({ config }) => { + const { EntityOwnerPicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogEntityNamespaceFilter = createCatalogFilterExtension({ + id: 'entity.namespace', + loader: async () => { + const { EntityNamespacePicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogEntityLifecycleFilter = createCatalogFilterExtension({ + id: 'entity.lifecycle', + loader: async () => { + const { EntityLifecyclePicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogEntityProcessingStatusFilter = createCatalogFilterExtension({ + id: 'entity.processing.status', + loader: async () => { + const { EntityProcessingStatusPicker } = await import( + '@backstage/plugin-catalog-react' + ); + return ; + }, +}); + +const CatalogUserListFilter = createCatalogFilterExtension({ + id: 'user.list', + configSchema: createSchemaFromZod(z => + z.object({ + initialFilter: z.enum(['owned', 'starred', 'all']).default('owned'), + }), + ), + loader: async ({ config }) => { + const { UserListPicker } = await import('@backstage/plugin-catalog-react'); + return ; + }, +}); + +export default [ + CatalogEntityTagFilter, + CatalogEntityKindFilter, + CatalogEntityTypeFilter, + CatalogEntityOwnerFilter, + CatalogEntityNamespaceFilter, + CatalogEntityLifecycleFilter, + CatalogEntityProcessingStatusFilter, + CatalogUserListFilter, +]; diff --git a/plugins/catalog/src/alpha/index.ts b/plugins/catalog/src/alpha/index.ts new file mode 100644 index 0000000000..06a78ec6ea --- /dev/null +++ b/plugins/catalog/src/alpha/index.ts @@ -0,0 +1,18 @@ +/* + * 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. + */ + +export { default } from './plugin'; +export { createCatalogFilterExtension } from './createCatalogFilterExtension'; diff --git a/plugins/catalog/src/alpha/navItems.tsx b/plugins/catalog/src/alpha/navItems.tsx new file mode 100644 index 0000000000..b6481fd764 --- /dev/null +++ b/plugins/catalog/src/alpha/navItems.tsx @@ -0,0 +1,29 @@ +/* + * 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 HomeIcon from '@material-ui/icons/Home'; +import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha'; +import { createNavItemExtension } from '@backstage/frontend-plugin-api'; +import { rootRouteRef } from '../routes'; + +export const CatalogIndexNavItem = createNavItemExtension({ + id: 'catalog.nav.index', + routeRef: convertLegacyRouteRef(rootRouteRef), + title: 'Catalog', + icon: HomeIcon, +}); + +export default [CatalogIndexNavItem]; diff --git a/plugins/catalog/src/alpha/pages.tsx b/plugins/catalog/src/alpha/pages.tsx new file mode 100644 index 0000000000..6270ae777d --- /dev/null +++ b/plugins/catalog/src/alpha/pages.tsx @@ -0,0 +1,83 @@ +/* + * 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 React from 'react'; +import { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha'; +import { + createPageExtension, + coreExtensionData, + createExtensionInput, +} from '@backstage/frontend-plugin-api'; +import { + AsyncEntityProvider, + entityRouteRef, +} from '@backstage/plugin-catalog-react'; +import { entityContentTitleExtensionDataRef } from '@backstage/plugin-catalog-react/alpha'; +import { rootRouteRef } from '../routes'; +import { useEntityFromUrl } from '../components/CatalogEntityPage/useEntityFromUrl'; + +export const CatalogIndexPage = createPageExtension({ + id: 'plugin.catalog.page.index', + defaultPath: '/catalog', + routeRef: convertLegacyRouteRef(rootRouteRef), + inputs: { + filters: createExtensionInput({ + element: coreExtensionData.reactElement, + }), + }, + loader: async ({ inputs }) => { + const { BaseCatalogPage } = await import('../components/CatalogPage'); + const filters = inputs.filters.map(filter => filter.element); + return {filters}} />; + }, +}); + +export const CatalogEntityPage = createPageExtension({ + id: 'plugin.catalog.page.entity', + defaultPath: '/catalog/:namespace/:kind/:name', + routeRef: convertLegacyRouteRef(entityRouteRef), + inputs: { + contents: createExtensionInput({ + element: coreExtensionData.reactElement, + path: coreExtensionData.routePath, + routeRef: coreExtensionData.routeRef.optional(), + title: entityContentTitleExtensionDataRef, + }), + }, + loader: async ({ inputs }) => { + const { EntityLayout } = await import('../components/EntityLayout'); + const Component = () => { + return ( + + + {inputs.contents.map(content => ( + + {content.element} + + ))} + + + ); + }; + return ; + }, +}); + +export default [CatalogIndexPage, CatalogEntityPage]; diff --git a/plugins/catalog/src/alpha/plugin.tsx b/plugins/catalog/src/alpha/plugin.tsx new file mode 100644 index 0000000000..2f019ca33e --- /dev/null +++ b/plugins/catalog/src/alpha/plugin.tsx @@ -0,0 +1,58 @@ +/* + * 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 { convertLegacyRouteRef } from '@backstage/core-plugin-api/alpha'; +import { createPlugin } from '@backstage/frontend-plugin-api'; + +import { entityRouteRef } from '@backstage/plugin-catalog-react'; + +import { + createComponentRouteRef, + createFromTemplateRouteRef, + rootRouteRef, + viewTechDocRouteRef, +} from '../routes'; + +import apis from './apis'; +import pages from './pages'; +import filters from './filters'; +import navItems from './navItems'; +import entityCards from './entityCards'; +import entityContents from './entityContents'; +import searchResultItems from './searchResultItems'; + +/** @alpha */ +export default createPlugin({ + id: 'catalog', + routes: { + catalogIndex: convertLegacyRouteRef(rootRouteRef), + catalogEntity: convertLegacyRouteRef(entityRouteRef), + }, + externalRoutes: { + viewTechDoc: convertLegacyRouteRef(viewTechDocRouteRef), + createComponent: convertLegacyRouteRef(createComponentRouteRef), + createFromTemplate: convertLegacyRouteRef(createFromTemplateRouteRef), + }, + extensions: [ + ...apis, + ...pages, + ...filters, + ...navItems, + ...entityCards, + ...entityContents, + ...searchResultItems, + ], +}); diff --git a/plugins/catalog/src/alpha/searchResultItems.tsx b/plugins/catalog/src/alpha/searchResultItems.tsx new file mode 100644 index 0000000000..f677869136 --- /dev/null +++ b/plugins/catalog/src/alpha/searchResultItems.tsx @@ -0,0 +1,29 @@ +/* + * 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 { createSearchResultListItemExtension } from '@backstage/plugin-search-react/alpha'; + +export const CatalogSearchResultListItemExtension = + createSearchResultListItemExtension({ + id: 'catalog', + predicate: result => result.type === 'software-catalog', + component: () => + import('../components/CatalogSearchResultListItem').then( + m => m.CatalogSearchResultListItem, + ), + }); + +export default [CatalogSearchResultListItemExtension]; diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts new file mode 100644 index 0000000000..42ff777cbd --- /dev/null +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.test.ts @@ -0,0 +1,178 @@ +/* + * 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 { CatalogApi } from '@backstage/catalog-client'; +import { Entity } from '@backstage/catalog-model'; +import { + EntityRefPresentation, + EntityRefPresentationSnapshot, +} from '@backstage/plugin-catalog-react'; +import { DefaultEntityPresentationApi } from './DefaultEntityPresentationApi'; + +describe('DefaultEntityPresentationApi', () => { + it('works in local mode', () => { + const api = DefaultEntityPresentationApi.createLocal(); + + expect(api.forEntity('component:default/test')).toEqual({ + snapshot: { + entityRef: 'component:default/test', + entity: undefined, + primaryTitle: 'test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }, + update$: undefined, + }); + + expect( + api.forEntity('component:default/test', { defaultKind: 'Other' }), + ).toEqual({ + snapshot: { + entityRef: 'component:default/test', + entity: undefined, + primaryTitle: 'component:test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }, + update$: undefined, + }); + + expect( + api.forEntity('component:default/test', { + defaultNamespace: 'other', + }), + ).toEqual({ + snapshot: { + entityRef: 'component:default/test', + entity: undefined, + primaryTitle: 'default/test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }, + update$: undefined, + }); + + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + }, + spec: { + type: 'service', + }, + }; + + expect(api.forEntity(entity)).toEqual({ + snapshot: { + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test | service', + Icon: expect.anything(), + }, + update$: undefined, + }); + }); + + it('works in catalog mode', async () => { + const catalogApi = { + getEntitiesByRefs: jest.fn(), + }; + const api = DefaultEntityPresentationApi.create({ + catalogApi: catalogApi as Partial as any, + }); + + catalogApi.getEntitiesByRefs.mockResolvedValueOnce({ + items: [ + { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test', + namespace: 'default', + etag: 'something', + }, + spec: { + type: 'service', + }, + }, + ], + }); + + // return simple presentation, call catalog, return full presentation + await expect( + consumePresentation(api.forEntity('component:default/test')), + ).resolves.toEqual([ + { + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test', + Icon: expect.anything(), + }, + { + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test | service', + Icon: expect.anything(), + }, + ]); + + // use cached entity, immediately return full presentation + await expect( + consumePresentation(api.forEntity('component:default/test')), + ).resolves.toEqual([ + { + entityRef: 'component:default/test', + primaryTitle: 'test', + secondaryTitle: 'component:default/test | service', + Icon: expect.anything(), + }, + ]); + + expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledTimes(1); + expect(catalogApi.getEntitiesByRefs).toHaveBeenCalledWith( + expect.objectContaining({ + entityRefs: ['component:default/test'], + }), + ); + }); +}); + +async function consumePresentation( + presentation: EntityRefPresentation, +): Promise { + const result: EntityRefPresentationSnapshot[] = []; + const { snapshot, update$ } = presentation; + + result.push(snapshot); + + if (update$) { + await new Promise(resolve => { + const sub = update$.subscribe({ + next: newSnapshot => { + result.push(newSnapshot); + }, + complete: () => { + sub.unsubscribe(); + resolve(); + }, + }); + }); + } + + return result; +} diff --git a/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts new file mode 100644 index 0000000000..5f1119e598 --- /dev/null +++ b/plugins/catalog/src/apis/EntityPresentationApi/DefaultEntityPresentationApi.ts @@ -0,0 +1,401 @@ +/* + * 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 { + Entity, + parseEntityRef, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { IconComponent } from '@backstage/core-plugin-api'; +import { + CatalogApi, + EntityPresentationApi, + EntityRefPresentation, + EntityRefPresentationSnapshot, +} from '@backstage/plugin-catalog-react'; +import { HumanDuration, durationToMilliseconds } from '@backstage/types'; +import DataLoader from 'dataloader'; +import ExpiryMap from 'expiry-map'; +import ObservableImpl from 'zen-observable'; +import { + DEFAULT_BATCH_DELAY, + DEFAULT_CACHE_TTL, + DEFAULT_ICONS, + createDefaultRenderer, +} from './defaults'; + +/** + * A custom renderer for the {@link DefaultEntityPresentationApi}. + * + * @public + */ +export interface DefaultEntityPresentationApiRenderer { + /** + * Whether to request the entity from the catalog API asynchronously. + * + * @remarks + * + * If this is set to true, entity data will be streamed in from the catalog + * whenever needed, and the render function may be called more than once: + * first when no entity data existed (or with old cached data), and then again + * at a later point when data is loaded from the catalog that proved to be + * different from the old one. + * + * @defaultValue true + */ + async?: boolean; + + /** + * The actual render function. + * + * @remarks + * + * This function may be called multiple times. + * + * The loading flag signals that the framework MAY be trying to load more + * entity data from the catalog and call the render function again, if it + * succeeds. In some cases you may want to render a loading state in that + * case. + * + * The entity may or may not be given. If the caller of the presentation API + * did present an entity upfront, then that's what will be passed in here. + * Otherwise, it may be a server-side entity that either comes from a local + * cache or directly from the server. + * + * In either case, the renderer should return a presentation that is the most + * useful possible for the end user, given the data that is available. + */ + render: (options: { + entityRef: string; + loading: boolean; + entity: Entity | undefined; + context: { + defaultKind?: string; + defaultNamespace?: string; + }; + }) => { + snapshot: Omit; + }; +} + +/** + * Options for the {@link DefaultEntityPresentationApi}. + * + * @public + */ +export interface DefaultEntityPresentationApiOptions { + /** + * The catalog API to use. If you want to use any asynchronous features, you + * must supply one. + */ + catalogApi?: CatalogApi; + + /** + * When to expire entities that have been loaded from the catalog API and + * cached for a while. + * + * @defaultValue 10 seconds + * @remarks + * + * The higher this value, the lower the load on the catalog API, but also the + * higher the risk of users seeing stale data. + */ + cacheTtl?: HumanDuration; + + /** + * For how long to wait before sending a batch of entity references to the + * catalog API. + * + * @defaultValue 50 milliseconds + * @remarks + * + * The higher this value, the greater the chance of batching up requests from + * across a page, but also the longer the lag time before displaying accurate + * information. + */ + batchDelay?: HumanDuration; + + /** + * A mapping from kinds to icons. + * + * @remarks + * + * The keys are kinds (case insensitive) that map to icon values to represent + * kinds by. + * + * If you do not supply a set of icons here, a set of fallback icons will be + * used. If you supply the empty object, no fallback icons will be used. + */ + kindIcons?: Record; + + /** + * A custom renderer, if any. + */ + renderer?: DefaultEntityPresentationApiRenderer; +} + +interface CacheEntry { + updatedAt: number; + entity: Entity | undefined; +} + +/** + * Default implementation of the {@link @backstage/plugin-catalog-react#EntityPresentationApi}. + * + * @public + */ +export class DefaultEntityPresentationApi implements EntityPresentationApi { + /** + * Creates a new presentation API that does not reach out to the catalog. + */ + static createLocal(): EntityPresentationApi { + return new DefaultEntityPresentationApi({ + renderer: createDefaultRenderer({ async: false }), + }); + } + + /** + * Creates a new presentation API that calls out to the catalog as needed to + * get additional information about entities. + */ + static create( + options: DefaultEntityPresentationApiOptions, + ): EntityPresentationApi { + return new DefaultEntityPresentationApi(options); + } + + // This cache holds on to all entity data ever loaded, no matter how old. Each + // entry is tagged with a timestamp of when it was inserted. We use this map + // to be able to always render SOME data even though the information is old. + // Entities change very rarely, so it's likely that the rendered information + // was perfectly fine in the first place. + readonly #cache: Map; + readonly #cacheTtlMs: number; + readonly #loader: DataLoader | undefined; + readonly #kindIcons: Record; // lowercased kinds + readonly #renderer: DefaultEntityPresentationApiRenderer; + + private constructor(options: DefaultEntityPresentationApiOptions) { + const cacheTtl = options.cacheTtl ?? DEFAULT_CACHE_TTL; + const batchDelay = options.batchDelay ?? DEFAULT_BATCH_DELAY; + const renderer = options.renderer ?? createDefaultRenderer({ async: true }); + + const kindIcons: Record = {}; + Object.entries(options.kindIcons ?? DEFAULT_ICONS).forEach( + ([kind, icon]) => { + kindIcons[kind.toLocaleLowerCase('en-US')] = icon; + }, + ); + + if (renderer.async) { + if (!options.catalogApi) { + throw new TypeError(`Asynchronous rendering requires a catalog API`); + } + this.#loader = this.#createLoader({ + cacheTtl, + batchDelay, + renderer, + catalogApi: options.catalogApi, + }); + } + + this.#cacheTtlMs = durationToMilliseconds(cacheTtl); + this.#cache = new Map(); + this.#kindIcons = kindIcons; + this.#renderer = renderer; + } + + /** {@inheritdoc @backstage/plugin-catalog-react#EntityPresentationApi.forEntity} */ + forEntity( + entityOrRef: Entity | string, + context?: { + defaultKind?: string; + defaultNamespace?: string; + }, + ): EntityRefPresentation { + const { entityRef, kind, entity, needsLoad } = + this.#getEntityForInitialRender(entityOrRef); + + // Make a wrapping helper for rendering + const render = (options: { + loading: boolean; + entity?: Entity; + }): EntityRefPresentationSnapshot => { + const { snapshot } = this.#renderer.render({ + entityRef: entityRef, + loading: options.loading, + entity: options.entity, + context: context || {}, + }); + return { + ...snapshot, + entityRef: entityRef, + Icon: this.#maybeFallbackIcon(snapshot.Icon, kind), + }; + }; + + // First the initial render + let initialSnapshot: EntityRefPresentationSnapshot; + try { + initialSnapshot = render({ + loading: needsLoad, + entity: entity, + }); + } catch { + // This is what gets presented if the renderer throws an error + initialSnapshot = { + primaryTitle: entityRef, + entityRef: entityRef, + }; + } + + // And then the following snapshot + const observable = !needsLoad + ? undefined + : new ObservableImpl(subscriber => { + let aborted = false; + + Promise.resolve() + .then(() => this.#loader?.load(entityRef)) + .then(newEntity => { + if ( + !aborted && + newEntity && + newEntity.metadata.etag !== entity?.metadata.etag + ) { + const updatedSnapshot = render({ + loading: false, + entity: newEntity, + }); + subscriber.next(updatedSnapshot); + } + }) + .catch(() => { + // Intentionally ignored - we do not propagate errors to the + // observable here. The presentation API should be error free and + // always return SOMETHING that makes sense to render, and we have + // already ensured above that the initial snapshot was that. + }) + .finally(() => { + if (!aborted) { + subscriber.complete(); + } + }); + + return () => { + aborted = true; + }; + }); + + return { + snapshot: initialSnapshot, + update$: observable, + }; + } + + #getEntityForInitialRender(entityOrRef: Entity | string): { + entity: Entity | undefined; + kind: string; + entityRef: string; + needsLoad: boolean; + } { + // If we were given an entity in the first place, we use it for a single + // pass of rendering and assume that it's up to date and not partial (i.e. + // we expect that it wasn't fetched in such a way that the required fields + // of the renderer were excluded) + if (typeof entityOrRef !== 'string') { + return { + entity: entityOrRef, + kind: entityOrRef.kind, + entityRef: stringifyEntityRef(entityOrRef), + needsLoad: false, + }; + } + + const cached = this.#cache.get(entityOrRef); + const cachedEntity: Entity | undefined = cached?.entity; + const cacheNeedsUpdate = + !cached || Date.now() - cached.updatedAt > this.#cacheTtlMs; + const needsLoad = + cacheNeedsUpdate && + this.#renderer.async !== false && + this.#loader !== undefined; + + return { + entity: cachedEntity, + kind: parseEntityRef(entityOrRef).kind, + entityRef: entityOrRef, + needsLoad, + }; + } + + #createLoader(options: { + catalogApi: CatalogApi; + cacheTtl: HumanDuration; + batchDelay: HumanDuration; + renderer: DefaultEntityPresentationApiRenderer; + }): DataLoader { + const cacheTtlMs = durationToMilliseconds(options.cacheTtl); + const batchDelayMs = durationToMilliseconds(options.batchDelay); + + return new DataLoader( + async (entityRefs: readonly string[]) => { + const { items } = await options.catalogApi!.getEntitiesByRefs({ + entityRefs: entityRefs as string[], + }); + + const now = Date.now(); + entityRefs.forEach((entityRef, index) => { + this.#cache.set(entityRef, { + updatedAt: now, + entity: items[index], + }); + }); + + return items; + }, + { + name: 'DefaultEntityPresentationApi', + // This cache is the one that the data loader uses internally for + // memoizing requests; essentially what it achieves is that multiple + // requests for the same entity ref will be batched up into a single + // request and then the resulting promises are held on to. We put an + // expiring map here, which makes it so that it re-fetches data with the + // expiry cadence of that map. Otherwise it would only fetch a given ref + // once and then never try again. This cache does therefore not fulfill + // the same purpose as the one that is in the root of the class. + cacheMap: new ExpiryMap(cacheTtlMs), + maxBatchSize: 100, + batchScheduleFn: batchDelayMs + ? cb => setTimeout(cb, batchDelayMs) + : undefined, + }, + ); + } + + #maybeFallbackIcon( + renderedIcon: IconComponent | false | undefined, + kind: string, + ): IconComponent | false | undefined { + if (renderedIcon) { + return renderedIcon; + } else if (renderedIcon === false) { + return false; + } + + return this.#kindIcons[kind.toLocaleLowerCase('en-US')]; + } +} diff --git a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx new file mode 100644 index 0000000000..afcd1f5464 --- /dev/null +++ b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx @@ -0,0 +1,65 @@ +/* + * 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 { IconComponent } from '@backstage/core-plugin-api'; +import { defaultEntityPresentation } from '@backstage/plugin-catalog-react'; +import { HumanDuration } from '@backstage/types'; +import ApartmentIcon from '@material-ui/icons/Apartment'; +import BusinessIcon from '@material-ui/icons/Business'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HelpIcon from '@material-ui/icons/Help'; +import LibraryAddIcon from '@material-ui/icons/LibraryAdd'; +import LocationOnIcon from '@material-ui/icons/LocationOn'; +import MemoryIcon from '@material-ui/icons/Memory'; +import PeopleIcon from '@material-ui/icons/People'; +import PersonIcon from '@material-ui/icons/Person'; +import { DefaultEntityPresentationApiRenderer } from './DefaultEntityPresentationApi'; + +export const DEFAULT_CACHE_TTL: HumanDuration = { seconds: 10 }; + +export const DEFAULT_BATCH_DELAY: HumanDuration = { milliseconds: 50 }; + +export const UNKNOWN_KIND_ICON: IconComponent = HelpIcon; + +export const DEFAULT_ICONS: Record = { + api: ExtensionIcon, + component: MemoryIcon, + system: BusinessIcon, + domain: ApartmentIcon, + location: LocationOnIcon, + user: PersonIcon, + group: PeopleIcon, + template: LibraryAddIcon, +}; + +export function createDefaultRenderer(options: { + async: boolean; +}): DefaultEntityPresentationApiRenderer { + return { + async: options.async, + + render: ({ entityRef, entity, context }) => { + const presentation = defaultEntityPresentation( + entity || entityRef, + context, + ); + return { + snapshot: presentation, + loadEntity: options.async, + }; + }, + }; +} diff --git a/plugins/catalog/src/apis/EntityPresentationApi/index.ts b/plugins/catalog/src/apis/EntityPresentationApi/index.ts new file mode 100644 index 0000000000..c1c6426ea0 --- /dev/null +++ b/plugins/catalog/src/apis/EntityPresentationApi/index.ts @@ -0,0 +1,21 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { + DefaultEntityPresentationApi, + type DefaultEntityPresentationApiOptions, + type DefaultEntityPresentationApiRenderer, +} from './DefaultEntityPresentationApi'; diff --git a/plugins/catalog/src/apis/index.ts b/plugins/catalog/src/apis/index.ts index 5c7e980890..271d749afc 100644 --- a/plugins/catalog/src/apis/index.ts +++ b/plugins/catalog/src/apis/index.ts @@ -14,4 +14,5 @@ * limitations under the License. */ +export * from './EntityPresentationApi'; export * from './StarredEntitiesApi'; diff --git a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx index b7d3ca3ed8..0ced712344 100644 --- a/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx +++ b/plugins/catalog/src/components/CatalogPage/DefaultCatalogPage.test.tsx @@ -14,7 +14,10 @@ * limitations under the License. */ -import { CatalogApi } from '@backstage/catalog-client'; +import { + CatalogApi, + QueryEntitiesInitialRequest, +} from '@backstage/catalog-client'; import { RELATION_OWNED_BY } from '@backstage/catalog-model'; import { TableColumn, TableProps } from '@backstage/core-components'; import { @@ -36,7 +39,7 @@ import { renderInTestApp, } from '@backstage/test-utils'; import DashboardIcon from '@material-ui/icons/Dashboard'; -import { fireEvent, screen } from '@testing-library/react'; +import { fireEvent, screen, waitFor } from '@testing-library/react'; import React from 'react'; import { createComponentRouteRef } from '../../routes'; import { CatalogTableRow } from '../CatalogTable'; @@ -49,10 +52,12 @@ describe('DefaultCatalogPage', () => { }); afterEach(() => { window.history.replaceState = origReplaceState; + + jest.clearAllMocks(); }); - const catalogApi: Partial = { - getEntities: () => + const catalogApi: jest.Mocked> = { + getEntities: jest.fn().mockImplementation(() => Promise.resolve({ items: [ { @@ -78,6 +83,7 @@ describe('DefaultCatalogPage', () => { kind: 'Component', metadata: { name: 'Entity2', + namespace: 'default', }, spec: { owner: 'not-tools', @@ -97,17 +103,47 @@ describe('DefaultCatalogPage', () => { }, ], }), - getLocationByRef: () => - Promise.resolve({ id: 'id', type: 'url', target: 'url' }), - getEntityFacets: async () => ({ + ), + getLocationByRef: jest + .fn() + .mockImplementation(() => + Promise.resolve({ id: 'id', type: 'url', target: 'url' }), + ), + getEntityFacets: jest.fn().mockImplementation(async () => ({ facets: { 'relations.ownedBy': [ { count: 1, value: 'group:default/not-tools' }, { count: 1, value: 'group:default/tools' }, ], }, - }), + })), + queryEntities: jest + .fn() + .mockImplementation(async (request: QueryEntitiesInitialRequest) => { + if ((request.filter as any)['relations.ownedBy']) { + // owned entities + return { items: [], totalItems: 3, pageInfo: {} }; + } + + if ((request.filter as any)['metadata.name']) { + // starred entities + return { + items: [ + { + apiVersion: '1', + kind: 'component', + metadata: { name: 'Entity1', namespace: 'default' }, + }, + ], + totalItems: 1, + pageInfo: {}, + }; + } + // all items + return { items: [], totalItems: 2, pageInfo: {} }; + }), }; + const testProfile: Partial = { displayName: 'Display Name', }; @@ -183,6 +219,8 @@ describe('DefaultCatalogPage', () => { it('should render the default actions of an item in the grid', async () => { await renderWrapped(); + await waitFor(() => expect(catalogApi.queryEntities).toHaveBeenCalled()); + fireEvent.click(screen.getByTestId('user-picker-owned')); await expect( screen.findByText(/Owned components \(1\)/), @@ -215,6 +253,8 @@ describe('DefaultCatalogPage', () => { ]; await renderWrapped(); + await waitFor(() => expect(catalogApi.queryEntities).toHaveBeenCalled()); + fireEvent.click(screen.getByTestId('user-picker-owned')); await expect( screen.findByText(/Owned components \(1\)/), @@ -231,7 +271,10 @@ describe('DefaultCatalogPage', () => { // https://github.com/mbrn/material-table/issues/1293 it('should render', async () => { await renderWrapped(); + await waitFor(() => expect(catalogApi.queryEntities).toHaveBeenCalled()); + fireEvent.click(screen.getByTestId('user-picker-owned')); + await expect( screen.findByText(/Owned components \(1\)/), ).resolves.toBeInTheDocument(); @@ -252,6 +295,8 @@ describe('DefaultCatalogPage', () => { // entities defaulting to "owned" filter and not based on the selected filter it('should render the correct entities filtered on the selected filter', async () => { await renderWrapped(); + await waitFor(() => expect(catalogApi.queryEntities).toHaveBeenCalled()); + fireEvent.click(screen.getByTestId('user-picker-owned')); await expect( screen.findByText(/Owned components \(1\)/), @@ -275,10 +320,9 @@ describe('DefaultCatalogPage', () => { // Now that we've starred an entity, the "Starred" menu option should be // enabled. - expect(screen.getByTestId('user-picker-starred')).not.toHaveAttribute( - 'aria-disabled', - 'true', - ); + expect( + await screen.findByTestId('user-picker-starred'), + ).not.toHaveAttribute('aria-disabled', 'true'); fireEvent.click(screen.getByTestId('user-picker-starred')); await expect( screen.findByText(/Starred components \(1\)/), diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 67579d5273..24ccb22908 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -19,6 +19,7 @@ import { Entity, RELATION_OWNED_BY, RELATION_PART_OF, + stringifyEntityRef, } from '@backstage/catalog-model'; import { CodeSnippet, @@ -203,9 +204,13 @@ export const CatalogTable = (props: CatalogTableProps) => { return { entity, resolved: { + // This name is here for backwards compatibility mostly; the + // presentation of refs in the table should in general be handled with + // EntityRefLink / EntityName components name: humanizeEntityRef(entity, { defaultKind: 'Component', }), + entityRef: stringifyEntityRef(entity), ownedByRelationsTitle: ownedByRelations .map(r => humanizeEntityRef(r, { defaultKind: 'group' })) .join(', '), diff --git a/plugins/catalog/src/components/CatalogTable/columns.tsx b/plugins/catalog/src/components/CatalogTable/columns.tsx index fc6e1aaf6b..3c31fcadf1 100644 --- a/plugins/catalog/src/components/CatalogTable/columns.tsx +++ b/plugins/catalog/src/components/CatalogTable/columns.tsx @@ -43,7 +43,7 @@ export const columnFactories = Object.freeze({ return { title: 'Name', - field: 'resolved.name', + field: 'resolved.entityRef', highlight: true, customSort({ entity: entity1 }, { entity: entity2 }) { // TODO: We could implement this more efficiently by comparing field by field. @@ -54,7 +54,6 @@ export const columnFactories = Object.freeze({ ), }; diff --git a/plugins/catalog/src/components/CatalogTable/types.ts b/plugins/catalog/src/components/CatalogTable/types.ts index 5bbb3af122..f2b3324169 100644 --- a/plugins/catalog/src/components/CatalogTable/types.ts +++ b/plugins/catalog/src/components/CatalogTable/types.ts @@ -20,7 +20,11 @@ import { Entity, CompoundEntityRef } from '@backstage/catalog-model'; export interface CatalogTableRow { entity: Entity; resolved: { + // This name is here for backwards compatibility mostly; the presentation of + // refs in the table should in general be handled with EntityRefLink / + // EntityName components name: string; + entityRef: string; partOfSystemRelationTitle?: string; partOfSystemRelations: CompoundEntityRef[]; ownedByRelationsTitle?: string; diff --git a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx index 5e6e8ff3a9..9cd377b447 100644 --- a/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx +++ b/plugins/catalog/src/components/EntityContextMenu/EntityContextMenu.tsx @@ -24,7 +24,7 @@ import { Popover, Tooltip, } from '@material-ui/core'; -import { makeStyles } from '@material-ui/core/styles'; +import { Theme, makeStyles } from '@material-ui/core/styles'; import BugReportIcon from '@material-ui/icons/BugReport'; import MoreVert from '@material-ui/icons/MoreVert'; import FileCopyTwoToneIcon from '@material-ui/icons/FileCopyTwoTone'; @@ -32,7 +32,6 @@ import React, { useCallback, useState } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; import { useEntityPermission } from '@backstage/plugin-catalog-react/alpha'; import { catalogEntityDeletePermission } from '@backstage/plugin-catalog-common/alpha'; -import { BackstageTheme } from '@backstage/theme'; import { UnregisterEntity, UnregisterEntityOptions } from './UnregisterEntity'; import { useApi, alertApiRef } from '@backstage/core-plugin-api'; @@ -40,7 +39,7 @@ import { useApi, alertApiRef } from '@backstage/core-plugin-api'; export type EntityContextMenuClassKey = 'button'; const useStyles = makeStyles( - (theme: BackstageTheme) => { + (theme: Theme) => { return { button: { color: theme.page.fontColor, diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx index 08c751c6c6..33414152be 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.test.tsx @@ -79,6 +79,7 @@ describe('EntityLayout', () => { kind: 'MyKind', metadata: { name: 'my-entity', + namespace: 'default', title: 'My Entity', }, } as Entity; diff --git a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx index 9505dad889..adad5d22af 100644 --- a/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx +++ b/plugins/catalog/src/components/EntityLayout/EntityLayout.tsx @@ -36,6 +36,7 @@ import { useRouteRefParams, } from '@backstage/core-plugin-api'; import { + EntityDisplayName, EntityRefLinks, entityRouteRef, FavoriteEntity, @@ -78,7 +79,7 @@ function EntityLayoutTitle(props: { whiteSpace="nowrap" overflow="hidden" > - {title} + {entity ? : title} {entity && } diff --git a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx index ec4d13a2f2..f3cc265653 100644 --- a/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx +++ b/plugins/catalog/src/components/SystemDiagramCard/SystemDiagramCard.tsx @@ -33,7 +33,6 @@ import { Box, makeStyles, Typography, useTheme } from '@material-ui/core'; import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; import React from 'react'; import useAsync from 'react-use/lib/useAsync'; -import { BackstageTheme } from '@backstage/theme'; import { DependencyGraph, @@ -55,7 +54,7 @@ export type SystemDiagramCardClassKey = | 'resourceNode'; const useStyles = makeStyles( - (theme: BackstageTheme) => ({ + theme => ({ domainNode: { fill: theme.palette.primary.main, stroke: theme.palette.border, diff --git a/plugins/catalog/src/plugin.ts b/plugins/catalog/src/plugin.ts index 3410984ec3..d310639fb1 100644 --- a/plugins/catalog/src/plugin.ts +++ b/plugins/catalog/src/plugin.ts @@ -18,6 +18,7 @@ import { CatalogClient } from '@backstage/catalog-client'; import { Entity } from '@backstage/catalog-model'; import { catalogApiRef, + entityPresentationApiRef, entityRouteRef, starredEntitiesApiRef, } from '@backstage/plugin-catalog-react'; @@ -52,6 +53,7 @@ import { HasSystemsCardProps } from './components/HasSystemsCard'; import { RelatedEntitiesCardProps } from './components/RelatedEntitiesCard'; import { CatalogSearchResultListItemProps } from './components/CatalogSearchResultListItem'; import { rootRouteRef } from './routes'; +import { DefaultEntityPresentationApi } from './apis/EntityPresentationApi'; /** @public */ export const catalogPlugin = createPlugin({ @@ -72,6 +74,12 @@ export const catalogPlugin = createPlugin({ factory: ({ storageApi }) => new DefaultStarredEntitiesApi({ storageApi }), }), + createApiFactory({ + api: entityPresentationApiRef, + deps: { catalogApi: catalogApiRef }, + factory: ({ catalogApi }) => + DefaultEntityPresentationApi.create({ catalogApi }), + }), ], routes: { catalogIndex: rootRouteRef, diff --git a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md index b0cb0fb020..fd6d68e389 100644 --- a/plugins/cicd-statistics-module-gitlab/CHANGELOG.md +++ b/plugins/cicd-statistics-module-gitlab/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-cicd-statistics-module-gitlab +## 0.1.22-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-cicd-statistics@0.1.28-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.21 ### Patch Changes diff --git a/plugins/cicd-statistics-module-gitlab/package.json b/plugins/cicd-statistics-module-gitlab/package.json index 6c7f3c150f..9c763bbedc 100644 --- a/plugins/cicd-statistics-module-gitlab/package.json +++ b/plugins/cicd-statistics-module-gitlab/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics-module-gitlab", "description": "CI/CD Statistics plugin module; Gitlab CICD", - "version": "0.1.21", + "version": "0.1.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cicd-statistics/CHANGELOG.md b/plugins/cicd-statistics/CHANGELOG.md index 9fe1717192..2ae84785d1 100644 --- a/plugins/cicd-statistics/CHANGELOG.md +++ b/plugins/cicd-statistics/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-cicd-statistics +## 0.1.28-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.27 ### Patch Changes diff --git a/plugins/cicd-statistics/package.json b/plugins/cicd-statistics/package.json index fb8b736035..a19523580e 100644 --- a/plugins/cicd-statistics/package.json +++ b/plugins/cicd-statistics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cicd-statistics", "description": "A frontend plugin visualizing CI/CD pipeline statistics (build time)", - "version": "0.1.27", + "version": "0.1.28-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/circleci/CHANGELOG.md b/plugins/circleci/CHANGELOG.md index c8ba1ae27f..5ed6d4d6e8 100644 --- a/plugins/circleci/CHANGELOG.md +++ b/plugins/circleci/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-circleci +## 0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.3.25 ### Patch Changes diff --git a/plugins/circleci/package.json b/plugins/circleci/package.json index ddec70c2de..22ff9d8238 100644 --- a/plugins/circleci/package.json +++ b/plugins/circleci/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-circleci", "description": "A Backstage plugin that integrates towards Circle CI", - "version": "0.3.25", + "version": "0.3.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cloudbuild/CHANGELOG.md b/plugins/cloudbuild/CHANGELOG.md index a322da009c..da292577a2 100644 --- a/plugins/cloudbuild/CHANGELOG.md +++ b/plugins/cloudbuild/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-cloudbuild +## 0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.3.25 ### Patch Changes diff --git a/plugins/cloudbuild/package.json b/plugins/cloudbuild/package.json index 8b64387aa8..e156694938 100644 --- a/plugins/cloudbuild/package.json +++ b/plugins/cloudbuild/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cloudbuild", "description": "A Backstage plugin that integrates towards Google Cloud Build", - "version": "0.3.25", + "version": "0.3.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-climate/CHANGELOG.md b/plugins/code-climate/CHANGELOG.md index 65dda2e3f5..430efe264e 100644 --- a/plugins/code-climate/CHANGELOG.md +++ b/plugins/code-climate/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-code-climate +## 0.1.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.25 ### Patch Changes diff --git a/plugins/code-climate/package.json b/plugins/code-climate/package.json index dc01c52a13..cb154399f1 100644 --- a/plugins/code-climate/package.json +++ b/plugins/code-climate/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-code-climate", - "version": "0.1.25", + "version": "0.1.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx b/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx index 5aae7d4194..ca5b24565c 100644 --- a/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx +++ b/plugins/code-climate/src/components/CodeClimateTable/CodeClimateTable.tsx @@ -17,10 +17,9 @@ import React from 'react'; import { CodeClimateData } from '../../api'; import { Link } from '@backstage/core-components'; -import { Box, makeStyles, Typography } from '@material-ui/core'; -import { BackstageTheme } from '@backstage/theme'; +import { Box, makeStyles, Theme, Typography } from '@material-ui/core'; -const letterStyle = (theme: BackstageTheme) => ({ +const letterStyle = (theme: Theme) => ({ color: theme.palette.common.white, border: 0, borderRadius: '3px', @@ -32,7 +31,7 @@ const fontSize = { fontSize: '25px', }; -const letterColor = (letter: string, theme: BackstageTheme) => { +const letterColor = (letter: string, theme: Theme) => { if (letter === 'A') { return theme.palette.success.main || '#45d298'; } else if (letter === 'B') { @@ -49,7 +48,7 @@ const letterColor = (letter: string, theme: BackstageTheme) => { }; const useStyles = makeStyles< - BackstageTheme, + Theme, { maintainabilityLetter: string; testCoverageLetter: string; diff --git a/plugins/code-coverage-backend/CHANGELOG.md b/plugins/code-coverage-backend/CHANGELOG.md index 2cc9d85f3b..358ba9c483 100644 --- a/plugins/code-coverage-backend/CHANGELOG.md +++ b/plugins/code-coverage-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-code-coverage-backend +## 0.2.21-next.0 + +### Patch Changes + +- 11f671eaa9: Added support for new backend system +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.2.20 ### Patch Changes diff --git a/plugins/code-coverage-backend/README.md b/plugins/code-coverage-backend/README.md index 54cf201015..2b22b0ad1d 100644 --- a/plugins/code-coverage-backend/README.md +++ b/plugins/code-coverage-backend/README.md @@ -67,6 +67,16 @@ diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts apiRouter.use(notFoundHandler()); ``` +## New Backend System + +The code coverage backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff ++ backend.add(import('@backstage/plugin-explore-backend')); +``` + ## Configuring your entity In order to use this plugin, you must set the `backstage.io/code-coverage` annotation. diff --git a/plugins/code-coverage-backend/api-report.md b/plugins/code-coverage-backend/api-report.md index 440af50714..8a64361391 100644 --- a/plugins/code-coverage-backend/api-report.md +++ b/plugins/code-coverage-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import express from 'express'; @@ -11,6 +12,10 @@ import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { UrlReader } from '@backstage/backend-common'; +// @public +const codeCoveragePlugin: () => BackendFeature; +export default codeCoveragePlugin; + // @public export function createRouter(options: RouterOptions): Promise; diff --git a/plugins/code-coverage-backend/package.json b/plugins/code-coverage-backend/package.json index 9ee98d52ad..7f667c50b1 100644 --- a/plugins/code-coverage-backend/package.json +++ b/plugins/code-coverage-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage-backend", "description": "A Backstage backend plugin that helps you keep track of your code coverage", - "version": "0.2.20", + "version": "0.2.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,6 +30,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-client": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", diff --git a/plugins/code-coverage-backend/src/index.ts b/plugins/code-coverage-backend/src/index.ts index 8511c9b71e..31de2ab3e3 100644 --- a/plugins/code-coverage-backend/src/index.ts +++ b/plugins/code-coverage-backend/src/index.ts @@ -22,3 +22,4 @@ export { createRouter } from './service/router'; export type { RouterOptions } from './service/router'; +export { codeCoveragePlugin as default } from './plugin'; diff --git a/plugins/code-coverage-backend/src/plugin.ts b/plugins/code-coverage-backend/src/plugin.ts new file mode 100644 index 0000000000..340280795f --- /dev/null +++ b/plugins/code-coverage-backend/src/plugin.ts @@ -0,0 +1,60 @@ +/* + * 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 { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service/router'; + +/** + * Code coverage backend plugin + * + * @public + */ +export const codeCoveragePlugin = createBackendPlugin({ + pluginId: 'codeCoverage', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + logger: coreServices.logger, + urlReader: coreServices.urlReader, + httpRouter: coreServices.httpRouter, + discovery: coreServices.discovery, + database: coreServices.database, + }, + async init({ + config, + logger, + urlReader, + httpRouter, + discovery, + database, + }) { + httpRouter.use( + await createRouter({ + config, + logger: loggerToWinstonLogger(logger), + urlReader, + discovery, + database, + }), + ); + }, + }); + }, +}); diff --git a/plugins/code-coverage/CHANGELOG.md b/plugins/code-coverage/CHANGELOG.md index f5ba60eeb4..111e4ad512 100644 --- a/plugins/code-coverage/CHANGELOG.md +++ b/plugins/code-coverage/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-code-coverage +## 0.2.19-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 71c97e7d73: The warning for missing code coverage will now render the entity as a reference. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.18 ### Patch Changes diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 15343a8212..2f74877a50 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-code-coverage", "description": "A Backstage plugin that helps you keep track of your code coverage", - "version": "0.2.18", + "version": "0.2.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/codescene/CHANGELOG.md b/plugins/codescene/CHANGELOG.md index 29f0700e65..b9d2faff0c 100644 --- a/plugins/codescene/CHANGELOG.md +++ b/plugins/codescene/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-codescene +## 0.1.19-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.18 ### Patch Changes diff --git a/plugins/codescene/package.json b/plugins/codescene/package.json index e36c8e5575..77f4b36f58 100644 --- a/plugins/codescene/package.json +++ b/plugins/codescene/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-codescene", - "version": "0.1.18", + "version": "0.1.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/config-schema/CHANGELOG.md b/plugins/config-schema/CHANGELOG.md index 3e09c6dd98..19eb742821 100644 --- a/plugins/config-schema/CHANGELOG.md +++ b/plugins/config-schema/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-config-schema +## 0.1.47-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.1.46 ### Patch Changes diff --git a/plugins/config-schema/package.json b/plugins/config-schema/package.json index 387921650b..9d89e7c9c1 100644 --- a/plugins/config-schema/package.json +++ b/plugins/config-schema/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-config-schema", "description": "A Backstage plugin that lets you browse the configuration schema of your app", - "version": "0.1.46", + "version": "0.1.47-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cost-insights/CHANGELOG.md b/plugins/cost-insights/CHANGELOG.md index a60e531966..4885ff4bc8 100644 --- a/plugins/cost-insights/CHANGELOG.md +++ b/plugins/cost-insights/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-cost-insights +## 0.12.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-cost-insights-common@0.1.2 + ## 0.12.14 ### Patch Changes diff --git a/plugins/cost-insights/package.json b/plugins/cost-insights/package.json index 5cd7c3458b..8bb278588e 100644 --- a/plugins/cost-insights/package.json +++ b/plugins/cost-insights/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-cost-insights", "description": "A Backstage plugin that helps you keep track of your cloud spend", - "version": "0.12.14", + "version": "0.12.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsThemeProvider.tsx b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsThemeProvider.tsx index 7c64c41116..40745c4301 100644 --- a/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsThemeProvider.tsx +++ b/plugins/cost-insights/src/components/CostInsightsPage/CostInsightsThemeProvider.tsx @@ -17,9 +17,9 @@ import React, { PropsWithChildren } from 'react'; import { createTheme as createMuiTheme, + Theme, ThemeProvider, } from '@material-ui/core'; -import { BackstageTheme } from '@backstage/theme'; import { costInsightsDarkTheme, costInsightsLightTheme, @@ -31,7 +31,7 @@ export const CostInsightsThemeProvider = ({ }: PropsWithChildren<{}>) => { return ( + theme={(theme: Theme) => createMuiTheme({ ...theme, palette: { diff --git a/plugins/cost-insights/src/utils/styles.ts b/plugins/cost-insights/src/utils/styles.ts index 6ccc81c963..cec9c0150d 100644 --- a/plugins/cost-insights/src/utils/styles.ts +++ b/plugins/cost-insights/src/utils/styles.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createStyles, emphasize, @@ -21,8 +22,8 @@ import { lighten, makeStyles, } from '@material-ui/core'; -import { BackstageTheme } from '@backstage/theme'; import { CostInsightsTheme, CostInsightsThemeOptions } from '../types'; +import { BackstageTheme } from '@backstage/theme'; export const costInsightsLightTheme = { palette: { @@ -155,7 +156,7 @@ export const useBarChartStyles = (theme: CostInsightsTheme) => ({ }, }); -export const useBarChartLayoutStyles = makeStyles(theme => +export const useBarChartLayoutStyles = makeStyles(theme => createStyles({ wrapper: { display: 'flex', @@ -197,7 +198,7 @@ export const useBarChartStepperButtonStyles = makeStyles( }), ); -export const useBarChartLabelStyles = makeStyles(theme => +export const useBarChartLabelStyles = makeStyles(theme => createStyles({ foreignObject: { textAlign: 'center', @@ -222,7 +223,7 @@ export const useBarChartLabelStyles = makeStyles(theme => }), ); -export const useCostInsightsStyles = makeStyles(theme => ({ +export const useCostInsightsStyles = makeStyles(theme => ({ h6Subtle: { ...theme.typography.h6, fontWeight: 'normal', @@ -230,84 +231,80 @@ export const useCostInsightsStyles = makeStyles(theme => ({ }, })); -export const useCostInsightsTabsStyles = makeStyles( - (theme: BackstageTheme) => ({ - tabs: { - borderBottom: `1px solid ${theme.palette.textVerySubtle}`, - backgroundColor: brighten(theme.palette.background.default), - padding: theme.spacing(0, 4), +export const useCostInsightsTabsStyles = makeStyles(theme => ({ + tabs: { + borderBottom: `1px solid ${theme.palette.textVerySubtle}`, + backgroundColor: brighten(theme.palette.background.default), + padding: theme.spacing(0, 4), + }, + tab: { + minHeight: 68, + minWidth: 180, + padding: theme.spacing(1, 4), + '&:hover': { + color: 'inherit', + backgroundColor: 'inherit', }, - tab: { - minHeight: 68, - minWidth: 180, - padding: theme.spacing(1, 4), - '&:hover': { - color: 'inherit', - backgroundColor: 'inherit', - }, + }, + indicator: { + backgroundColor: theme.palette.navigation.indicator, + height: 4, + }, + tabLabel: { + display: 'flex', + alignItems: 'center', + }, + tabLabelText: { + fontSize: theme.typography.fontSize, + fontWeight: theme.typography.fontWeightBold, + }, + menuItem: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + minWidth: 180, + padding: theme.spacing(2, 2), + }, + menuItemSelected: { + backgroundColor: lighten(theme.palette.background.default, 0.3), + }, +})); + +export const useAlertCardActionHeaderStyles = makeStyles(theme => + createStyles({ + root: { + paddingBottom: theme.spacing(2), + borderRadius: 'unset', }, - indicator: { - backgroundColor: theme.palette.navigation.indicator, - height: 4, - }, - tabLabel: { - display: 'flex', - alignItems: 'center', - }, - tabLabelText: { + title: { fontSize: theme.typography.fontSize, fontWeight: theme.typography.fontWeightBold, + lineHeight: 1.5, }, - menuItem: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - minWidth: 180, - padding: theme.spacing(2, 2), - }, - menuItemSelected: { - backgroundColor: lighten(theme.palette.background.default, 0.3), + action: { + margin: 0, }, }), ); -export const useAlertCardActionHeaderStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - root: { - paddingBottom: theme.spacing(2), - borderRadius: 'unset', - }, - title: { - fontSize: theme.typography.fontSize, - fontWeight: theme.typography.fontWeightBold, - lineHeight: 1.5, - }, - action: { - margin: 0, - }, - }), +export const useCostGrowthStyles = makeStyles(theme => + createStyles({ + excess: { + color: theme.palette.status.error, + }, + savings: { + color: theme.palette.status.ok, + }, + indicator: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'flex-end', + }, + }), ); -export const useCostGrowthStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - excess: { - color: theme.palette.status.error, - }, - savings: { - color: theme.palette.status.ok, - }, - indicator: { - display: 'flex', - flexDirection: 'row', - alignItems: 'center', - justifyContent: 'flex-end', - }, - }), -); - -export const useCostGrowthLegendStyles = makeStyles(theme => ({ +export const useCostGrowthLegendStyles = makeStyles(theme => ({ h5: { ...theme.typography.h5, fontWeight: 500, @@ -339,44 +336,43 @@ export const useCostGrowthLegendStyles = makeStyles(theme => ({ }, })); -export const useBarChartStepperStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - paper: { - display: 'flex', - flexDirection: 'row', - justifyContent: 'space-around', - alignItems: 'center', - background: 'transparent', - padding: 8, - }, - step: { - backgroundColor: theme.palette.action.disabled, - borderRadius: '50%', - width: 9, - height: 9, - margin: '0 2px', - }, - stepActive: { - backgroundColor: theme.palette.primary.main, - }, - steps: { - display: 'flex', - flexDirection: 'row', - }, - backButton: { - position: 'absolute', - left: 0, - top: 'calc(50% - 60px)', - zIndex: 100, - }, - nextButton: { - position: 'absolute', - right: 0, - top: 'calc(50% - 60px)', - zIndex: 100, - }, - }), +export const useBarChartStepperStyles = makeStyles(theme => + createStyles({ + paper: { + display: 'flex', + flexDirection: 'row', + justifyContent: 'space-around', + alignItems: 'center', + background: 'transparent', + padding: 8, + }, + step: { + backgroundColor: theme.palette.action.disabled, + borderRadius: '50%', + width: 9, + height: 9, + margin: '0 2px', + }, + stepActive: { + backgroundColor: theme.palette.primary.main, + }, + steps: { + display: 'flex', + flexDirection: 'row', + }, + backButton: { + position: 'absolute', + left: 0, + top: 'calc(50% - 60px)', + zIndex: 100, + }, + nextButton: { + position: 'absolute', + right: 0, + top: 'calc(50% - 60px)', + zIndex: 100, + }, + }), ); export const useNavigationStyles = makeStyles( @@ -451,32 +447,30 @@ export const useTooltipStyles = makeStyles( }), ); -export const useAlertInsightsSectionStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - button: { - backgroundColor: theme.palette.textVerySubtle, - color: theme.palette.text.primary, - }, - }), +export const useAlertInsightsSectionStyles = makeStyles(theme => + createStyles({ + button: { + backgroundColor: theme.palette.textVerySubtle, + color: theme.palette.text.primary, + }, + }), ); -export const useSelectStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - select: { - minWidth: 200, - textAlign: 'start', - backgroundColor: theme.palette.background.paper, +export const useSelectStyles = makeStyles(theme => + createStyles({ + select: { + minWidth: 200, + textAlign: 'start', + backgroundColor: theme.palette.background.paper, + }, + menuItem: { + minWidth: 200, + padding: theme.spacing(2), + '&.compact': { + padding: theme.spacing(1, 2), }, - menuItem: { - minWidth: 200, - padding: theme.spacing(2), - '&.compact': { - padding: theme.spacing(1, 2), - }, - }, - }), + }, + }), ); export const useActionItemCardStyles = makeStyles( @@ -513,52 +507,48 @@ export const useActionItemCardStyles = makeStyles( }), ); -export const useProductInsightsCardStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - root: { - padding: theme.spacing(2, 2, 2, 2.5), // restore backstage default padding - }, - action: { - margin: 0, // unset negative margins - }, - }), +export const useProductInsightsCardStyles = makeStyles(theme => + createStyles({ + root: { + padding: theme.spacing(2, 2, 2, 2.5), // restore backstage default padding + }, + action: { + margin: 0, // unset negative margins + }, + }), ); -export const useProductInsightsChartStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - indicator: { - fontWeight: theme.typography.fontWeightBold, - fontSize: '1.25rem', - }, - actions: { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }, - }), +export const useProductInsightsChartStyles = makeStyles(theme => + createStyles({ + indicator: { + fontWeight: theme.typography.fontWeightBold, + fontSize: '1.25rem', + }, + actions: { + display: 'flex', + justifyContent: 'space-between', + alignItems: 'center', + }, + }), ); -export const useBackdropStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - root: { - zIndex: theme.zIndex.modal, - }, - }), +export const useBackdropStyles = makeStyles(theme => + createStyles({ + root: { + zIndex: theme.zIndex.modal, + }, + }), ); -export const useSubtleTypographyStyles = makeStyles( - (theme: BackstageTheme) => - createStyles({ - root: { - color: theme.palette.textSubtle, - }, - }), +export const useSubtleTypographyStyles = makeStyles(theme => + createStyles({ + root: { + color: theme.palette.textSubtle, + }, + }), ); -export const useEntityDialogStyles = makeStyles(theme => +export const useEntityDialogStyles = makeStyles(theme => createStyles({ dialogContent: { padding: 0, @@ -597,7 +587,7 @@ export const useEntityDialogStyles = makeStyles(theme => }), ); -export const useAlertDialogStyles = makeStyles((theme: BackstageTheme) => +export const useAlertDialogStyles = makeStyles(theme => createStyles({ content: { padding: theme.spacing(0, 5, 2, 5), diff --git a/plugins/devtools-backend/CHANGELOG.md b/plugins/devtools-backend/CHANGELOG.md index fd9108d28d..e7dbc7427c 100644 --- a/plugins/devtools-backend/CHANGELOG.md +++ b/plugins/devtools-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-devtools-backend +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/config-loader@1.5.2-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/cli-common@0.1.13 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-devtools-common@0.1.5 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.2.3 ### Patch Changes diff --git a/plugins/devtools-backend/package.json b/plugins/devtools-backend/package.json index acf7788d8c..9938bec6e7 100644 --- a/plugins/devtools-backend/package.json +++ b/plugins/devtools-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools-backend", - "version": "0.2.3", + "version": "0.2.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/devtools/CHANGELOG.md b/plugins/devtools/CHANGELOG.md index b200af4d82..0496e56359 100644 --- a/plugins/devtools/CHANGELOG.md +++ b/plugins/devtools/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-devtools +## 0.1.6-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-devtools-common@0.1.5 + ## 0.1.5 ### Patch Changes diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 41ea821dea..4523be5278 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-devtools", - "version": "0.1.5", + "version": "0.1.6-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/dynatrace/CHANGELOG.md b/plugins/dynatrace/CHANGELOG.md index 2de9b4555b..7b9e8b609f 100644 --- a/plugins/dynatrace/CHANGELOG.md +++ b/plugins/dynatrace/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-dynatrace +## 8.0.0-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 7.0.5 ### Patch Changes diff --git a/plugins/dynatrace/package.json b/plugins/dynatrace/package.json index f8b35eafaa..86b7ea87b8 100644 --- a/plugins/dynatrace/package.json +++ b/plugins/dynatrace/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-dynatrace", - "version": "7.0.5", + "version": "8.0.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback-backend/CHANGELOG.md b/plugins/entity-feedback-backend/CHANGELOG.md index acfdaf360f..5ef3f5e709 100644 --- a/plugins/entity-feedback-backend/CHANGELOG.md +++ b/plugins/entity-feedback-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-entity-feedback-backend +## 0.2.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.3 ### Patch Changes diff --git a/plugins/entity-feedback-backend/package.json b/plugins/entity-feedback-backend/package.json index c493be7075..77e49a7086 100644 --- a/plugins/entity-feedback-backend/package.json +++ b/plugins/entity-feedback-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback-backend", - "version": "0.2.3", + "version": "0.2.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-feedback/CHANGELOG.md b/plugins/entity-feedback/CHANGELOG.md index 469ff16ca2..d813946f1f 100644 --- a/plugins/entity-feedback/CHANGELOG.md +++ b/plugins/entity-feedback/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-entity-feedback +## 0.2.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-entity-feedback-common@0.1.3 + ## 0.2.8 ### Patch Changes diff --git a/plugins/entity-feedback/package.json b/plugins/entity-feedback/package.json index f2a4697454..67ea558503 100644 --- a/plugins/entity-feedback/package.json +++ b/plugins/entity-feedback/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-feedback", - "version": "0.2.8", + "version": "0.2.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/entity-validation/CHANGELOG.md b/plugins/entity-validation/CHANGELOG.md index 4aeb6cfd03..3c0b1fe96d 100644 --- a/plugins/entity-validation/CHANGELOG.md +++ b/plugins/entity-validation/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-entity-validation +## 0.1.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + ## 0.1.10 ### Patch Changes diff --git a/plugins/entity-validation/package.json b/plugins/entity-validation/package.json index 1fee0c7892..ec2e36d06b 100644 --- a/plugins/entity-validation/package.json +++ b/plugins/entity-validation/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-entity-validation", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-aws-sqs/CHANGELOG.md b/plugins/events-backend-module-aws-sqs/CHANGELOG.md index 304271a0a4..ea404bd4da 100644 --- a/plugins/events-backend-module-aws-sqs/CHANGELOG.md +++ b/plugins/events-backend-module-aws-sqs/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-events-backend-module-aws-sqs +## 0.2.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.2.9 ### Patch Changes diff --git a/plugins/events-backend-module-aws-sqs/package.json b/plugins/events-backend-module-aws-sqs/package.json index a114136c7e..877af9b03c 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.2.9", + "version": "0.2.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-azure/CHANGELOG.md b/plugins/events-backend-module-azure/CHANGELOG.md index 593b77bdf3..0b8ef3e3a3 100644 --- a/plugins/events-backend-module-azure/CHANGELOG.md +++ b/plugins/events-backend-module-azure/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-azure +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-module-azure/package.json b/plugins/events-backend-module-azure/package.json index 175f952a4b..d81d912ed4 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.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md index 2bfd659892..4fe98ad304 100644 --- a/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md +++ b/plugins/events-backend-module-bitbucket-cloud/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-bitbucket-cloud +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-module-bitbucket-cloud/package.json b/plugins/events-backend-module-bitbucket-cloud/package.json index e81bea9415..d6a33fbed2 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.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gerrit/CHANGELOG.md b/plugins/events-backend-module-gerrit/CHANGELOG.md index fd49c7df3b..c63b5224dc 100644 --- a/plugins/events-backend-module-gerrit/CHANGELOG.md +++ b/plugins/events-backend-module-gerrit/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-events-backend-module-gerrit +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-module-gerrit/package.json b/plugins/events-backend-module-gerrit/package.json index d30063e14f..48dace1744 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.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-github/CHANGELOG.md b/plugins/events-backend-module-github/CHANGELOG.md index f87a77ea7e..d1eca166f9 100644 --- a/plugins/events-backend-module-github/CHANGELOG.md +++ b/plugins/events-backend-module-github/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-github +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-module-github/package.json b/plugins/events-backend-module-github/package.json index bf9caa3bfd..cf48d2c008 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.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-module-gitlab/CHANGELOG.md b/plugins/events-backend-module-gitlab/CHANGELOG.md index 90f9ce8e5d..9c1be3735c 100644 --- a/plugins/events-backend-module-gitlab/CHANGELOG.md +++ b/plugins/events-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-events-backend-module-gitlab +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-module-gitlab/package.json b/plugins/events-backend-module-gitlab/package.json index 9ce53681d0..7cfb42ad5b 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.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend-test-utils/CHANGELOG.md b/plugins/events-backend-test-utils/CHANGELOG.md index fd4afbc91c..3ddcce8a1f 100644 --- a/plugins/events-backend-test-utils/CHANGELOG.md +++ b/plugins/events-backend-test-utils/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-backend-test-utils +## 0.1.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.1.16 ### Patch Changes diff --git a/plugins/events-backend-test-utils/package.json b/plugins/events-backend-test-utils/package.json index f7cfa76cef..508ef0cddc 100644 --- a/plugins/events-backend-test-utils/package.json +++ b/plugins/events-backend-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-backend-test-utils", "description": "The plugin-events-backend-test-utils for @backstage/plugin-events-node", - "version": "0.1.16", + "version": "0.1.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-backend/CHANGELOG.md b/plugins/events-backend/CHANGELOG.md index 0ecbc4461e..256bffd508 100644 --- a/plugins/events-backend/CHANGELOG.md +++ b/plugins/events-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-events-backend +## 0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-events-node@0.2.16-next.0 + ## 0.2.15 ### Patch Changes diff --git a/plugins/events-backend/package.json b/plugins/events-backend/package.json index 6c330ce996..2221d1bf13 100644 --- a/plugins/events-backend/package.json +++ b/plugins/events-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-events-backend", - "version": "0.2.15", + "version": "0.2.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/events-node/CHANGELOG.md b/plugins/events-node/CHANGELOG.md index cf2c846c93..c35980b9f6 100644 --- a/plugins/events-node/CHANGELOG.md +++ b/plugins/events-node/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/plugin-events-node +## 0.2.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + ## 0.2.15 ### Patch Changes diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 804eabe491..df9b89aa9c 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-events-node", "description": "The plugin-events-node module for @backstage/plugin-events-backend", - "version": "0.2.15", + "version": "0.2.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/example-todo-list-backend/CHANGELOG.md b/plugins/example-todo-list-backend/CHANGELOG.md index 3ce8bb2103..b1d1ebabc7 100644 --- a/plugins/example-todo-list-backend/CHANGELOG.md +++ b/plugins/example-todo-list-backend/CHANGELOG.md @@ -1,5 +1,16 @@ # @internal/plugin-todo-list-backend +## 1.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 1.0.18 ### Patch Changes diff --git a/plugins/example-todo-list-backend/api-report.md b/plugins/example-todo-list-backend/api-report.md index d78f5e0796..4c27733124 100644 --- a/plugins/example-todo-list-backend/api-report.md +++ b/plugins/example-todo-list-backend/api-report.md @@ -11,7 +11,7 @@ import { Logger } from 'winston'; // @public export function createRouter(options: RouterOptions): Promise; -// @alpha +// @public const exampleTodoListPlugin: () => BackendFeature; export default exampleTodoListPlugin; diff --git a/plugins/example-todo-list-backend/package.json b/plugins/example-todo-list-backend/package.json index c13bb571e2..e020ca765d 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.18", + "version": "1.0.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -17,12 +17,11 @@ "publishConfig": { "access": "public", "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "types": "dist/index.d.ts" }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/example-todo-list-backend/src/plugin.ts b/plugins/example-todo-list-backend/src/plugin.ts index 42985f6e88..6bdae201f1 100644 --- a/plugins/example-todo-list-backend/src/plugin.ts +++ b/plugins/example-todo-list-backend/src/plugin.ts @@ -24,7 +24,7 @@ import { createRouter } from './service/router'; /** * The example TODO list backend plugin. * - * @alpha + * @public */ export const exampleTodoListPlugin = createBackendPlugin({ pluginId: 'exampleTodoList', diff --git a/plugins/example-todo-list/CHANGELOG.md b/plugins/example-todo-list/CHANGELOG.md index 1a4c10fb2b..239785c44b 100644 --- a/plugins/example-todo-list/CHANGELOG.md +++ b/plugins/example-todo-list/CHANGELOG.md @@ -1,5 +1,14 @@ # @internal/plugin-todo-list +## 1.0.19-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 1.0.18 ### Patch Changes diff --git a/plugins/example-todo-list/package.json b/plugins/example-todo-list/package.json index 7bfc3f898c..5d60daaacd 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.18", + "version": "1.0.19-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-backend/CHANGELOG.md b/plugins/explore-backend/CHANGELOG.md index c02ad2da28..04214e66ed 100644 --- a/plugins/explore-backend/CHANGELOG.md +++ b/plugins/explore-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-explore-backend +## 0.0.17-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-backend-module-explore@0.1.11-next.0 + - @backstage/plugin-search-common@1.2.7 + ## 0.0.16 ### Patch Changes diff --git a/plugins/explore-backend/package.json b/plugins/explore-backend/package.json index 8c1ae12922..1ade42b078 100644 --- a/plugins/explore-backend/package.json +++ b/plugins/explore-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-explore-backend", - "version": "0.0.16", + "version": "0.0.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore-react/CHANGELOG.md b/plugins/explore-react/CHANGELOG.md index ba70624ec9..006d3cf7e9 100644 --- a/plugins/explore-react/CHANGELOG.md +++ b/plugins/explore-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-explore-react +## 0.0.33-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-explore-common@0.0.2 + ## 0.0.32 ### Patch Changes diff --git a/plugins/explore-react/package.json b/plugins/explore-react/package.json index 7599da4ba8..ce9f6af869 100644 --- a/plugins/explore-react/package.json +++ b/plugins/explore-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore-react", "description": "A frontend library for Backstage plugins that want to interact with the explore plugin", - "version": "0.0.32", + "version": "0.0.33-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore/CHANGELOG.md b/plugins/explore/CHANGELOG.md index 82e0a66d3c..d4bcc6831d 100644 --- a/plugins/explore/CHANGELOG.md +++ b/plugins/explore/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-explore +## 0.4.12-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-explore-react@0.0.33-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.7 + ## 0.4.11 ### Patch Changes diff --git a/plugins/explore/package.json b/plugins/explore/package.json index 435c5df84d..d8602fd74d 100644 --- a/plugins/explore/package.json +++ b/plugins/explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-explore", "description": "A Backstage plugin for building an exploration page of your software ecosystem", - "version": "0.4.11", + "version": "0.4.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx index 2c8f6f6a6e..ea989c2834 100644 --- a/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx +++ b/plugins/explore/src/components/GroupsExplorerContent/GroupsDiagram.tsx @@ -34,7 +34,6 @@ import { humanizeEntityRef, getEntityRelations, } from '@backstage/plugin-catalog-react'; -import { BackstageTheme } from '@backstage/theme'; import { makeStyles, Typography, useTheme } from '@material-ui/core'; import ZoomOutMap from '@material-ui/icons/ZoomOutMap'; import classNames from 'classnames'; @@ -42,7 +41,7 @@ import React from 'react'; import useAsync from 'react-use/lib/useAsync'; const useStyles = makeStyles( - (theme: BackstageTheme) => ({ + theme => ({ graph: { minHeight: '100%', flex: 1, diff --git a/plugins/firehydrant/CHANGELOG.md b/plugins/firehydrant/CHANGELOG.md index ef28aa04c6..4fb7bf8ad2 100644 --- a/plugins/firehydrant/CHANGELOG.md +++ b/plugins/firehydrant/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-firehydrant +## 0.2.10-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.2.9 ### Patch Changes diff --git a/plugins/firehydrant/package.json b/plugins/firehydrant/package.json index ed723f71cf..994660f424 100644 --- a/plugins/firehydrant/package.json +++ b/plugins/firehydrant/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-firehydrant", "description": "A Backstage plugin that integrates towards FireHydrant", - "version": "0.2.9", + "version": "0.2.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/fossa/CHANGELOG.md b/plugins/fossa/CHANGELOG.md index 099ab5f5a4..ca79919fe4 100644 --- a/plugins/fossa/CHANGELOG.md +++ b/plugins/fossa/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-fossa +## 0.2.58-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.57 ### Patch Changes diff --git a/plugins/fossa/package.json b/plugins/fossa/package.json index 6c08f3d4fd..07f32f9af9 100644 --- a/plugins/fossa/package.json +++ b/plugins/fossa/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-fossa", "description": "A Backstage plugin that integrates towards FOSSA", - "version": "0.2.57", + "version": "0.2.58-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcalendar/CHANGELOG.md b/plugins/gcalendar/CHANGELOG.md index f9ac25d7f6..6e4d9d5b19 100644 --- a/plugins/gcalendar/CHANGELOG.md +++ b/plugins/gcalendar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-gcalendar +## 0.3.20-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + ## 0.3.19 ### Patch Changes diff --git a/plugins/gcalendar/package.json b/plugins/gcalendar/package.json index d84850677b..94435b49b5 100644 --- a/plugins/gcalendar/package.json +++ b/plugins/gcalendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-gcalendar", - "version": "0.3.19", + "version": "0.3.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx b/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx index 4e36eff55a..9008f263bf 100644 --- a/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx +++ b/plugins/gcalendar/src/components/CalendarCard/AttendeeChip.tsx @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; - -import { BackstageTheme } from '@backstage/theme'; - import { Badge, Chip, makeStyles } from '@material-ui/core'; import CancelIcon from '@material-ui/icons/Cancel'; import CheckIcon from '@material-ui/icons/CheckCircle'; - import { EventAttendee, ResponseStatus } from '../../api'; -const useStyles = makeStyles((theme: BackstageTheme) => { +const useStyles = makeStyles(theme => { const getIconColor = (responseStatus?: string) => { if (!responseStatus) return theme.palette.primary.light; diff --git a/plugins/gcp-projects/CHANGELOG.md b/plugins/gcp-projects/CHANGELOG.md index 5a2c289a94..756714b370 100644 --- a/plugins/gcp-projects/CHANGELOG.md +++ b/plugins/gcp-projects/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-gcp-projects +## 0.3.43-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.3.42 ### Patch Changes diff --git a/plugins/gcp-projects/package.json b/plugins/gcp-projects/package.json index 24bb9a99fa..50d16b45b8 100644 --- a/plugins/gcp-projects/package.json +++ b/plugins/gcp-projects/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gcp-projects", "description": "A Backstage plugin that helps you manage projects in GCP", - "version": "0.3.42", + "version": "0.3.43-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/git-release-manager/CHANGELOG.md b/plugins/git-release-manager/CHANGELOG.md index a91f49bbe1..2b568b3f9a 100644 --- a/plugins/git-release-manager/CHANGELOG.md +++ b/plugins/git-release-manager/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-git-release-manager +## 0.3.39-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + ## 0.3.38 ### Patch Changes diff --git a/plugins/git-release-manager/package.json b/plugins/git-release-manager/package.json index 2c0ce60f6a..866eb37c8c 100644 --- a/plugins/git-release-manager/package.json +++ b/plugins/git-release-manager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-git-release-manager", "description": "A Backstage plugin that helps you manage releases in git", - "version": "0.3.38", + "version": "0.3.39-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-actions/CHANGELOG.md b/plugins/github-actions/CHANGELOG.md index f5e9f1bc4c..4b36c5df29 100644 --- a/plugins/github-actions/CHANGELOG.md +++ b/plugins/github-actions/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-github-actions +## 0.6.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + ## 0.6.6 ### Patch Changes diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index ec0d9a03db..d32858250d 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-actions", "description": "A Backstage plugin that integrates towards GitHub Actions", - "version": "0.6.6", + "version": "0.6.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-deployments/CHANGELOG.md b/plugins/github-deployments/CHANGELOG.md index e44c124264..48a7d5cbbf 100644 --- a/plugins/github-deployments/CHANGELOG.md +++ b/plugins/github-deployments/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-github-deployments +## 0.1.57-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.56 ### Patch Changes diff --git a/plugins/github-deployments/package.json b/plugins/github-deployments/package.json index 4833a4df8a..5e966f89d2 100644 --- a/plugins/github-deployments/package.json +++ b/plugins/github-deployments/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-deployments", "description": "A Backstage plugin that integrates towards GitHub Deployments", - "version": "0.1.56", + "version": "0.1.57-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-issues/CHANGELOG.md b/plugins/github-issues/CHANGELOG.md index 11901150bb..520680f50d 100644 --- a/plugins/github-issues/CHANGELOG.md +++ b/plugins/github-issues/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-github-issues +## 0.2.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.14 ### Patch Changes diff --git a/plugins/github-issues/package.json b/plugins/github-issues/package.json index f942ca50bc..1bd8c8eceb 100644 --- a/plugins/github-issues/package.json +++ b/plugins/github-issues/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-github-issues", - "version": "0.2.14", + "version": "0.2.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/github-pull-requests-board/CHANGELOG.md b/plugins/github-pull-requests-board/CHANGELOG.md index 8721accd79..574bb77405 100644 --- a/plugins/github-pull-requests-board/CHANGELOG.md +++ b/plugins/github-pull-requests-board/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-github-pull-requests-board +## 0.1.20-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + ## 0.1.19 ### Patch Changes diff --git a/plugins/github-pull-requests-board/package.json b/plugins/github-pull-requests-board/package.json index 2c985beda7..02ff243382 100644 --- a/plugins/github-pull-requests-board/package.json +++ b/plugins/github-pull-requests-board/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-github-pull-requests-board", "description": "A Backstage plugin that allows you to see all open Pull Requests for all the repositories owned by your team", - "version": "0.1.19", + "version": "0.1.20-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gitops-profiles/CHANGELOG.md b/plugins/gitops-profiles/CHANGELOG.md index f254fd8eac..11102c9c71 100644 --- a/plugins/gitops-profiles/CHANGELOG.md +++ b/plugins/gitops-profiles/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-gitops-profiles +## 0.3.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + ## 0.3.41 ### Patch Changes diff --git a/plugins/gitops-profiles/package.json b/plugins/gitops-profiles/package.json index 5511f1f4fa..298981cabb 100644 --- a/plugins/gitops-profiles/package.json +++ b/plugins/gitops-profiles/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gitops-profiles", "description": "A Backstage plugin that helps you manage GitOps profiles", - "version": "0.3.41", + "version": "0.3.42-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/gocd/CHANGELOG.md b/plugins/gocd/CHANGELOG.md index be089e60a5..5d8808e7ad 100644 --- a/plugins/gocd/CHANGELOG.md +++ b/plugins/gocd/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-gocd +## 0.1.32-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.31 ### Patch Changes diff --git a/plugins/gocd/package.json b/plugins/gocd/package.json index c2f60d7eb6..a9979f9c55 100644 --- a/plugins/gocd/package.json +++ b/plugins/gocd/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-gocd", "description": "A Backstage plugin that integrates towards GoCD", - "version": "0.1.31", + "version": "0.1.32-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/graphiql/CHANGELOG.md b/plugins/graphiql/CHANGELOG.md index dea81e130c..faa69543ec 100644 --- a/plugins/graphiql/CHANGELOG.md +++ b/plugins/graphiql/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-graphiql +## 0.3.0-next.0 + +### Minor Changes + +- 57fda44b90: Upgrade to GraphiQL to 3.0.6 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.2.55 ### Patch Changes diff --git a/plugins/graphiql/package.json b/plugins/graphiql/package.json index 52cff1fdb7..71687f03b9 100644 --- a/plugins/graphiql/package.json +++ b/plugins/graphiql/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphiql", "description": "Backstage plugin for browsing GraphQL APIs", - "version": "0.2.55", + "version": "0.3.0-next.0", "publishConfig": { "access": "public" }, @@ -54,7 +54,7 @@ "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.61", "@types/react": "^16.13.1 || ^17.0.0", - "graphiql": "^1.5.12", + "graphiql": "^3.0.6", "graphql": "^16.0.0", "graphql-ws": "^5.4.1", "react-use": "^17.2.4" diff --git a/plugins/graphiql/src/alpha.tsx b/plugins/graphiql/src/alpha.tsx index 58b6686daa..8b2b962355 100644 --- a/plugins/graphiql/src/alpha.tsx +++ b/plugins/graphiql/src/alpha.tsx @@ -88,10 +88,10 @@ export function createEndpointExtension(options: { output: { endpoint: endpointDataRef, }, - factory({ bind, config }) { - bind({ + factory({ config }) { + return { endpoint: options.factory({ config }).endpoint, - }); + }; }, }); } diff --git a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx index dd38f513be..40c2c168a8 100644 --- a/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx +++ b/plugins/graphiql/src/components/GraphiQLBrowser/GraphiQLBrowser.tsx @@ -77,12 +77,7 @@ export const GraphiQLBrowser = (props: GraphiQLBrowserProps) => {
- +
diff --git a/plugins/graphql-backend/CHANGELOG.md b/plugins/graphql-backend/CHANGELOG.md index dcf6b8f243..83b9464b38 100644 --- a/plugins/graphql-backend/CHANGELOG.md +++ b/plugins/graphql-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-graphql-backend +## 0.2.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-catalog-graphql@0.4.1-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + ## 0.2.0 ### Minor Changes diff --git a/plugins/graphql-backend/package.json b/plugins/graphql-backend/package.json index c0b89ef0ce..b5ef102734 100644 --- a/plugins/graphql-backend/package.json +++ b/plugins/graphql-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-backend", "description": "Deprecated, consider using @frontside/backstage-plugin-graphql-backend instead", - "version": "0.2.0", + "version": "0.2.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/graphql-voyager/CHANGELOG.md b/plugins/graphql-voyager/CHANGELOG.md index 20130f64dd..750561ae80 100644 --- a/plugins/graphql-voyager/CHANGELOG.md +++ b/plugins/graphql-voyager/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-graphql-voyager +## 0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.1.8 ### Patch Changes diff --git a/plugins/graphql-voyager/package.json b/plugins/graphql-voyager/package.json index 51b7020154..5c26365a88 100644 --- a/plugins/graphql-voyager/package.json +++ b/plugins/graphql-voyager/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-graphql-voyager", "description": "Backstage plugin for GraphQL Voyager", - "version": "0.1.8", + "version": "0.1.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/home-react/CHANGELOG.md b/plugins/home-react/CHANGELOG.md index 8124f70308..fa1705caa0 100644 --- a/plugins/home-react/CHANGELOG.md +++ b/plugins/home-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-home-react +## 0.1.5-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + ## 0.1.4 ### Patch Changes diff --git a/plugins/home-react/package.json b/plugins/home-react/package.json index 5c56caf71b..2ca237038b 100644 --- a/plugins/home-react/package.json +++ b/plugins/home-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home-react", "description": "A Backstage plugin that contains react components helps you build a home page", - "version": "0.1.4", + "version": "0.1.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/home/CHANGELOG.md b/plugins/home/CHANGELOG.md index cd27145f6c..36e2e52f11 100644 --- a/plugins/home/CHANGELOG.md +++ b/plugins/home/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-home +## 0.5.10-next.0 + +### Patch Changes + +- 3fdffbb699: Remove the duplicate versions of `@rjsf/*` as they're no longer needed +- 6c2b872153: Add official support for React 18. +- 5b364984bf: Added experimental support for declarative integration via the `/alpha` subpath. +- cc0e8d0b51: Temporarily pin the `react-grid-layout` sub-dependency to version `1.3.4` while the horizontal resizing of the latest version is not fixed. For more details, see [#20712](https://github.com/backstage/backstage/issues/20712). +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 0.5.9 ### Patch Changes diff --git a/plugins/home/package.json b/plugins/home/package.json index edb7e6542d..9ab37dd580 100644 --- a/plugins/home/package.json +++ b/plugins/home/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-home", "description": "A Backstage plugin that helps you build a home page", - "version": "0.5.9", + "version": "0.5.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -67,7 +67,7 @@ "@types/react": "^16.13.1 || ^17.0.0", "lodash": "^4.17.21", "luxon": "^3.4.3", - "react-grid-layout": "^1.3.4", + "react-grid-layout": "1.3.4", "react-resizable": "^3.0.4", "react-use": "^17.2.4", "zod": "^3.21.4" diff --git a/plugins/home/src/api/VisitsStorageApi.ts b/plugins/home/src/api/VisitsStorageApi.ts index daf24d716b..c72d4c5d64 100644 --- a/plugins/home/src/api/VisitsStorageApi.ts +++ b/plugins/home/src/api/VisitsStorageApi.ts @@ -117,23 +117,39 @@ export class VisitsStorageApi implements VisitsApi { } private async persistAll(visits: Array) { - const { userEntityRef } = await this.identityApi.getBackstageIdentity(); - const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`; - + const storageKey = await this.getStorageKey(); return this.storageApi.set>(storageKey, visits); } private async retrieveAll(): Promise> { + const storageKey = await this.getStorageKey(); + // Handles for case when snapshot is and is not referenced per storaged type used + const snapshot = this.storageApi.snapshot>(storageKey); + if (snapshot?.presence !== 'unknown') { + return snapshot?.value ?? []; + } + + return new Promise((resolve, reject) => { + const subsription = this.storageApi + .observe$(storageKey) + .subscribe({ + next: next => { + const visits = next.value ?? []; + subsription.unsubscribe(); + resolve(visits); + }, + error: err => { + subsription.unsubscribe(); + reject(err); + }, + }); + }); + } + + private async getStorageKey(): Promise { const { userEntityRef } = await this.identityApi.getBackstageIdentity(); const storageKey = `${this.storageKeyPrefix}:${userEntityRef}`; - let visits: Array; - - try { - visits = this.storageApi.snapshot>(storageKey).value ?? []; - } catch { - visits = []; - } - return visits; + return storageKey; } // This assumes Visit fields are either numbers or strings diff --git a/plugins/home/src/plugin.test.ts b/plugins/home/src/plugin.test.ts index 920a9076f4..1526bd0c61 100644 --- a/plugins/home/src/plugin.test.ts +++ b/plugins/home/src/plugin.test.ts @@ -13,10 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import packageJson from '../package.json'; import { homePlugin } from './plugin'; describe('home', () => { it('should export plugin', () => { expect(homePlugin).toBeDefined(); }); + + // Temporarily ensure we are installing a working version of the react-grid-layout library + // For more details, see: https://github.com/react-grid-layout/react-grid-layout/issues/1959 + // TODO(@backstage/discoverability-maintainers): Delete this once the sub-dependency issue has been resolved. + it('should pin react-grid-layout version to 1.3.4', async () => { + expect(packageJson.dependencies['react-grid-layout']).toBe('1.3.4'); + }); }); diff --git a/plugins/ilert/CHANGELOG.md b/plugins/ilert/CHANGELOG.md index 8e07fa787f..f11a5dfcd1 100644 --- a/plugins/ilert/CHANGELOG.md +++ b/plugins/ilert/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-ilert +## 0.2.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.14 ### Patch Changes diff --git a/plugins/ilert/package.json b/plugins/ilert/package.json index 42d6a33ab1..e015bba7b8 100644 --- a/plugins/ilert/package.json +++ b/plugins/ilert/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-ilert", "description": "A Backstage plugin that integrates towards iLert", - "version": "0.2.14", + "version": "0.2.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins-backend/CHANGELOG.md b/plugins/jenkins-backend/CHANGELOG.md index 27af4041da..fe59d2d719 100644 --- a/plugins/jenkins-backend/CHANGELOG.md +++ b/plugins/jenkins-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-jenkins-backend +## 0.3.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-jenkins-common@0.1.20 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.3.0 ### Minor Changes diff --git a/plugins/jenkins-backend/package.json b/plugins/jenkins-backend/package.json index 740ef8710a..29bdfa7dfa 100644 --- a/plugins/jenkins-backend/package.json +++ b/plugins/jenkins-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins-backend", "description": "A Backstage backend plugin that integrates towards Jenkins", - "version": "0.3.0", + "version": "0.3.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/jenkins/CHANGELOG.md b/plugins/jenkins/CHANGELOG.md index 5878d16f47..c22cae3af2 100644 --- a/plugins/jenkins/CHANGELOG.md +++ b/plugins/jenkins/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-jenkins +## 0.9.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-jenkins-common@0.1.20 + ## 0.9.0 ### Minor Changes diff --git a/plugins/jenkins/package.json b/plugins/jenkins/package.json index 8be847f002..3515936abf 100644 --- a/plugins/jenkins/package.json +++ b/plugins/jenkins/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-jenkins", "description": "A Backstage plugin that integrates towards Jenkins", - "version": "0.9.0", + "version": "0.9.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kafka-backend/CHANGELOG.md b/plugins/kafka-backend/CHANGELOG.md index 573be0062f..e33a3c0f14 100644 --- a/plugins/kafka-backend/CHANGELOG.md +++ b/plugins/kafka-backend/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-kafka-backend +## 0.3.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.3.3 ### Patch Changes diff --git a/plugins/kafka-backend/alpha-api-report.md b/plugins/kafka-backend/alpha-api-report.md new file mode 100644 index 0000000000..01c035aa0d --- /dev/null +++ b/plugins/kafka-backend/alpha-api-report.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-kafka-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'; + +// @alpha +const _default: () => BackendFeature; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/kafka-backend/api-report.md b/plugins/kafka-backend/api-report.md index b03bad12e5..20c8260b52 100644 --- a/plugins/kafka-backend/api-report.md +++ b/plugins/kafka-backend/api-report.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; @@ -11,10 +10,6 @@ import { Logger } from 'winston'; // @public (undocumented) export function createRouter(options: RouterOptions): Promise; -// @alpha -const kafkaPlugin: () => BackendFeature; -export default kafkaPlugin; - // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/kafka-backend/package.json b/plugins/kafka-backend/package.json index 68060f254c..6626a21412 100644 --- a/plugins/kafka-backend/package.json +++ b/plugins/kafka-backend/package.json @@ -1,15 +1,27 @@ { "name": "@backstage/plugin-kafka-backend", "description": "A Backstage backend plugin that integrates towards Kafka", - "version": "0.3.3", + "version": "0.3.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin" @@ -27,7 +39,7 @@ "configSchema": "config.d.ts", "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/kafka-backend/src/plugin.ts b/plugins/kafka-backend/src/alpha.ts similarity index 96% rename from plugins/kafka-backend/src/plugin.ts rename to plugins/kafka-backend/src/alpha.ts index 0548a5f128..bc627eb9d3 100644 --- a/plugins/kafka-backend/src/plugin.ts +++ b/plugins/kafka-backend/src/alpha.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * * @alpha */ -export const kafkaPlugin = createBackendPlugin({ +export default createBackendPlugin({ pluginId: 'kafka', register(env) { env.registerInit({ diff --git a/plugins/kafka-backend/src/index.ts b/plugins/kafka-backend/src/index.ts index d87d313941..70b7fb45a4 100644 --- a/plugins/kafka-backend/src/index.ts +++ b/plugins/kafka-backend/src/index.ts @@ -22,4 +22,3 @@ export type { RouterOptions } from './service/router'; export { createRouter } from './service/router'; -export { kafkaPlugin as default } from './plugin'; diff --git a/plugins/kafka/CHANGELOG.md b/plugins/kafka/CHANGELOG.md index d24a6db0e2..db25947985 100644 --- a/plugins/kafka/CHANGELOG.md +++ b/plugins/kafka/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kafka +## 0.3.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + ## 0.3.25 ### Patch Changes diff --git a/plugins/kafka/package.json b/plugins/kafka/package.json index 1580caa124..d43e58f97c 100644 --- a/plugins/kafka/package.json +++ b/plugins/kafka/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kafka", "description": "A Backstage plugin that integrates towards Kafka", - "version": "0.3.25", + "version": "0.3.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-backend/CHANGELOG.md b/plugins/kubernetes-backend/CHANGELOG.md index 138b47b345..b53e5adab2 100644 --- a/plugins/kubernetes-backend/CHANGELOG.md +++ b/plugins/kubernetes-backend/CHANGELOG.md @@ -1,5 +1,25 @@ # @backstage/plugin-kubernetes-backend +## 0.13.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + - @backstage/plugin-kubernetes-node@0.1.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.13.0 ### Minor Changes diff --git a/plugins/kubernetes-backend/package.json b/plugins/kubernetes-backend/package.json index cc0b5016e4..4500db50de 100644 --- a/plugins/kubernetes-backend/package.json +++ b/plugins/kubernetes-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-backend", "description": "A Backstage backend plugin that integrates towards Kubernetes", - "version": "0.13.0", + "version": "0.13.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts index 27d57cd854..9bee21867d 100644 --- a/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts +++ b/plugins/kubernetes-backend/src/service/KubernetesProxy.test.ts @@ -123,7 +123,6 @@ describe('KubernetesProxy', () => { }; beforeEach(() => { - jest.resetAllMocks(); authStrategy = { getCredential: jest .fn< diff --git a/plugins/kubernetes-cluster/CHANGELOG.md b/plugins/kubernetes-cluster/CHANGELOG.md index fbb762282e..c8903b1a93 100644 --- a/plugins/kubernetes-cluster/CHANGELOG.md +++ b/plugins/kubernetes-cluster/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-kubernetes-cluster +## 0.0.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-kubernetes-react@0.1.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.0.1 ### Patch Changes diff --git a/plugins/kubernetes-cluster/package.json b/plugins/kubernetes-cluster/package.json index 1949941d47..5df8c4f32c 100644 --- a/plugins/kubernetes-cluster/package.json +++ b/plugins/kubernetes-cluster/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-cluster", "description": "A Backstage plugin that shows details of Kubernetes clusters", - "version": "0.0.1", + "version": "0.0.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-common/CHANGELOG.md b/plugins/kubernetes-common/CHANGELOG.md index 4db1264d6b..e1c8e1b684 100644 --- a/plugins/kubernetes-common/CHANGELOG.md +++ b/plugins/kubernetes-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-kubernetes-common +## 0.7.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + ## 0.7.0 ### Minor Changes diff --git a/plugins/kubernetes-common/package.json b/plugins/kubernetes-common/package.json index 9fe232e68f..9430b190fc 100644 --- a/plugins/kubernetes-common/package.json +++ b/plugins/kubernetes-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-common", "description": "Common functionalities for kubernetes, to be shared between kubernetes and kubernetes-backend plugin", - "version": "0.7.0", + "version": "0.7.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-node/CHANGELOG.md b/plugins/kubernetes-node/CHANGELOG.md index cc128fcd7f..ed31d1ec9a 100644 --- a/plugins/kubernetes-node/CHANGELOG.md +++ b/plugins/kubernetes-node/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-kubernetes-node +## 0.1.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/kubernetes-node/package.json b/plugins/kubernetes-node/package.json index 6f63ba1364..6f36abffa2 100644 --- a/plugins/kubernetes-node/package.json +++ b/plugins/kubernetes-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-node", "description": "Node.js library for the kubernetes plugin", - "version": "0.1.0", + "version": "0.1.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/CHANGELOG.md b/plugins/kubernetes-react/CHANGELOG.md index 13c23a75bf..f3850e88de 100644 --- a/plugins/kubernetes-react/CHANGELOG.md +++ b/plugins/kubernetes-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-kubernetes-react +## 0.1.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.1.0 ### Minor Changes diff --git a/plugins/kubernetes-react/package.json b/plugins/kubernetes-react/package.json index 03f647cfb2..772ca48cc2 100644 --- a/plugins/kubernetes-react/package.json +++ b/plugins/kubernetes-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes-react", "description": "Web library for the kubernetes-react plugin", - "version": "0.1.0", + "version": "0.1.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx b/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx index cee3fb9e6c..22629a1d6e 100644 --- a/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx +++ b/plugins/kubernetes-react/src/components/CustomResources/DefaultCustomResource.tsx @@ -85,7 +85,7 @@ const DefaultCustomResourceAccordion = ({ /> - {customResource.hasOwnProperty('status') && ( + {Object.prototype.hasOwnProperty.call(customResource, 'status') && ( )} diff --git a/plugins/kubernetes/CHANGELOG.md b/plugins/kubernetes/CHANGELOG.md index e208c83b06..c6bc0f7fab 100644 --- a/plugins/kubernetes/CHANGELOG.md +++ b/plugins/kubernetes/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-kubernetes +## 0.11.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-kubernetes-react@0.1.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-kubernetes-common@0.7.1-next.0 + ## 0.11.0 ### Minor Changes diff --git a/plugins/kubernetes/package.json b/plugins/kubernetes/package.json index ed2dfd35ec..47fd25542d 100644 --- a/plugins/kubernetes/package.json +++ b/plugins/kubernetes/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-kubernetes", "description": "A Backstage plugin that integrates towards Kubernetes", - "version": "0.11.0", + "version": "0.11.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/lighthouse-backend/CHANGELOG.md b/plugins/lighthouse-backend/CHANGELOG.md index 76b12fb1f4..95d3f58194 100644 --- a/plugins/lighthouse-backend/CHANGELOG.md +++ b/plugins/lighthouse-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-lighthouse-backend +## 0.3.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.3.3 ### Patch Changes diff --git a/plugins/lighthouse-backend/package.json b/plugins/lighthouse-backend/package.json index e5306695cc..d6acb8facb 100644 --- a/plugins/lighthouse-backend/package.json +++ b/plugins/lighthouse-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse-backend", "description": "Backend functionalities for lighthouse", - "version": "0.3.3", + "version": "0.3.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/lighthouse/CHANGELOG.md b/plugins/lighthouse/CHANGELOG.md index 0b9130363f..b66e678aab 100644 --- a/plugins/lighthouse/CHANGELOG.md +++ b/plugins/lighthouse/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-lighthouse +## 0.4.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-lighthouse-common@0.1.4 + ## 0.4.10 ### Patch Changes diff --git a/plugins/lighthouse/package.json b/plugins/lighthouse/package.json index c039ae6b30..d08b41d483 100644 --- a/plugins/lighthouse/package.json +++ b/plugins/lighthouse/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-lighthouse", "description": "A Backstage plugin that integrates towards Lighthouse", - "version": "0.4.10", + "version": "0.4.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist-backend/CHANGELOG.md b/plugins/linguist-backend/CHANGELOG.md index 74a4eb35a6..c55eaf79f5 100644 --- a/plugins/linguist-backend/CHANGELOG.md +++ b/plugins/linguist-backend/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-linguist-backend +## 0.5.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.5.3 ### Patch Changes diff --git a/plugins/linguist-backend/package.json b/plugins/linguist-backend/package.json index 78aa0868d4..78cdcf7bf5 100644 --- a/plugins/linguist-backend/package.json +++ b/plugins/linguist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist-backend", - "version": "0.5.3", + "version": "0.5.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/linguist/CHANGELOG.md b/plugins/linguist/CHANGELOG.md index 85db830a62..fec35925de 100644 --- a/plugins/linguist/CHANGELOG.md +++ b/plugins/linguist/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-linguist +## 0.1.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-linguist-common@0.1.2 + ## 0.1.10 ### Patch Changes diff --git a/plugins/linguist/package.json b/plugins/linguist/package.json index 3d101548a2..e6b95a3076 100644 --- a/plugins/linguist/package.json +++ b/plugins/linguist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-linguist", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/microsoft-calendar/CHANGELOG.md b/plugins/microsoft-calendar/CHANGELOG.md index 9e8f04b435..94140da499 100644 --- a/plugins/microsoft-calendar/CHANGELOG.md +++ b/plugins/microsoft-calendar/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-microsoft-calendar +## 0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + ## 0.1.8 ### Patch Changes diff --git a/plugins/microsoft-calendar/package.json b/plugins/microsoft-calendar/package.json index 57c4a75c39..82016c58e6 100644 --- a/plugins/microsoft-calendar/package.json +++ b/plugins/microsoft-calendar/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-microsoft-calendar", - "version": "0.1.8", + "version": "0.1.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/microsoft-calendar/src/components/AttendeeChip.tsx b/plugins/microsoft-calendar/src/components/AttendeeChip.tsx index b3b5b98301..25358519e6 100644 --- a/plugins/microsoft-calendar/src/components/AttendeeChip.tsx +++ b/plugins/microsoft-calendar/src/components/AttendeeChip.tsx @@ -13,17 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; - -import { BackstageTheme } from '@backstage/theme'; - import { Badge, Chip, makeStyles } from '@material-ui/core'; import CancelIcon from '@material-ui/icons/Cancel'; import CheckIcon from '@material-ui/icons/CheckCircle'; - import { Attendee, ResponseStatusMap } from '../api'; -const useStyles = makeStyles((theme: BackstageTheme) => { +const useStyles = makeStyles(theme => { const getIconColor = (responseStatus?: string) => { if (!responseStatus) return theme.palette.primary.light; diff --git a/plugins/newrelic-dashboard/CHANGELOG.md b/plugins/newrelic-dashboard/CHANGELOG.md index 1c1a8bc5bf..14e9cbbda3 100644 --- a/plugins/newrelic-dashboard/CHANGELOG.md +++ b/plugins/newrelic-dashboard/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-newrelic-dashboard +## 0.3.1-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.3.0 ### Minor Changes diff --git a/plugins/newrelic-dashboard/package.json b/plugins/newrelic-dashboard/package.json index 15f25d6cc7..6d79b0fda4 100644 --- a/plugins/newrelic-dashboard/package.json +++ b/plugins/newrelic-dashboard/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-newrelic-dashboard", - "version": "0.3.0", + "version": "0.3.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/newrelic/CHANGELOG.md b/plugins/newrelic/CHANGELOG.md index 65d2aad766..a0e8b98414 100644 --- a/plugins/newrelic/CHANGELOG.md +++ b/plugins/newrelic/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-newrelic +## 0.3.42-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.3.41 ### Patch Changes diff --git a/plugins/newrelic/package.json b/plugins/newrelic/package.json index c4da950dee..db7bc65590 100644 --- a/plugins/newrelic/package.json +++ b/plugins/newrelic/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-newrelic", "description": "A Backstage plugin that integrates towards New Relic", - "version": "0.3.41", + "version": "0.3.42-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad-backend/CHANGELOG.md b/plugins/nomad-backend/CHANGELOG.md index b5661c4b40..ade1a26f2c 100644 --- a/plugins/nomad-backend/CHANGELOG.md +++ b/plugins/nomad-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-nomad-backend +## 0.1.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.8 ### Patch Changes diff --git a/plugins/nomad-backend/package.json b/plugins/nomad-backend/package.json index 60ba974467..5c0bd03fac 100644 --- a/plugins/nomad-backend/package.json +++ b/plugins/nomad-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad-backend", - "version": "0.1.8", + "version": "0.1.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/nomad/CHANGELOG.md b/plugins/nomad/CHANGELOG.md index b6dff83c44..6e88468d5f 100644 --- a/plugins/nomad/CHANGELOG.md +++ b/plugins/nomad/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-nomad +## 0.1.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + ## 0.1.6 ### Patch Changes diff --git a/plugins/nomad/package.json b/plugins/nomad/package.json index 617c8e2c5a..d08551da04 100644 --- a/plugins/nomad/package.json +++ b/plugins/nomad/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-nomad", - "version": "0.1.6", + "version": "0.1.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/octopus-deploy/CHANGELOG.md b/plugins/octopus-deploy/CHANGELOG.md index 64b61be538..bf5857ed7a 100644 --- a/plugins/octopus-deploy/CHANGELOG.md +++ b/plugins/octopus-deploy/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-octopus-deploy +## 0.2.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.2.7 ### Patch Changes diff --git a/plugins/octopus-deploy/package.json b/plugins/octopus-deploy/package.json index 3da25dc2c3..6949d1c035 100644 --- a/plugins/octopus-deploy/package.json +++ b/plugins/octopus-deploy/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-octopus-deploy", - "version": "0.2.7", + "version": "0.2.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/opencost/CHANGELOG.md b/plugins/opencost/CHANGELOG.md index b20b34b9c4..8ef77dee1f 100644 --- a/plugins/opencost/CHANGELOG.md +++ b/plugins/opencost/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-opencost +## 0.2.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/opencost/package.json b/plugins/opencost/package.json index f751ed3ba5..a7f298ab19 100644 --- a/plugins/opencost/package.json +++ b/plugins/opencost/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-opencost", - "version": "0.2.1", + "version": "0.2.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org-react/CHANGELOG.md b/plugins/org-react/CHANGELOG.md index 1b39333288..f442dd89fd 100644 --- a/plugins/org-react/CHANGELOG.md +++ b/plugins/org-react/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-org-react +## 0.1.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + ## 0.1.14 ### Patch Changes diff --git a/plugins/org-react/package.json b/plugins/org-react/package.json index 2097c7658a..1d6e88a94f 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.14", + "version": "0.1.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org-react/src/components/GroupListPicker/GroupListPickerButton.tsx b/plugins/org-react/src/components/GroupListPicker/GroupListPickerButton.tsx index 01ffe7e58b..f4255a18be 100644 --- a/plugins/org-react/src/components/GroupListPicker/GroupListPickerButton.tsx +++ b/plugins/org-react/src/components/GroupListPicker/GroupListPickerButton.tsx @@ -15,12 +15,11 @@ */ import React from 'react'; -import { BackstageTheme } from '@backstage/theme'; import { makeStyles, Typography, Button } from '@material-ui/core'; import KeyboardArrowDownIcon from '@material-ui/icons/KeyboardArrowDown'; import PeopleIcon from '@material-ui/icons/People'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ btn: { padding: '10px', width: '100%', diff --git a/plugins/org/CHANGELOG.md b/plugins/org/CHANGELOG.md index 1faaf20dbb..d29d37c94e 100644 --- a/plugins/org/CHANGELOG.md +++ b/plugins/org/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-org +## 0.6.16-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.6.15 ### Patch Changes diff --git a/plugins/org/package.json b/plugins/org/package.json index b9bf7f0c02..9ad92192b5 100644 --- a/plugins/org/package.json +++ b/plugins/org/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-org", "description": "A Backstage plugin that helps you create entity pages for your organization", - "version": "0.6.15", + "version": "0.6.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx index 2725991d0d..cffed95c7a 100644 --- a/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx +++ b/plugins/org/src/components/Cards/OwnershipCard/ComponentsGrid.tsx @@ -22,7 +22,6 @@ import { ResponseErrorPanel, } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; -import { BackstageTheme } from '@backstage/theme'; import { Box, createStyles, @@ -36,7 +35,7 @@ import { catalogIndexRouteRef } from '../../../routes'; import { useGetEntities } from './useGetEntities'; import { EntityRelationAggregation } from './types'; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ card: { border: `1px solid ${theme.palette.divider}`, diff --git a/plugins/org/src/components/Cards/User/UserProfileCard/UserProfileCard.test.tsx b/plugins/org/src/components/Cards/User/UserProfileCard/UserProfileCard.test.tsx index 555815c2de..a18e9980e9 100644 --- a/plugins/org/src/components/Cards/User/UserProfileCard/UserProfileCard.test.tsx +++ b/plugins/org/src/components/Cards/User/UserProfileCard/UserProfileCard.test.tsx @@ -65,7 +65,7 @@ describe('UserSummary Test', () => { 'src', 'https://example.com/staff/calum.jpeg', ); - expect(screen.getByText('examplegroup')).toHaveAttribute( + expect(screen.getByText('examplegroup').closest('a')).toHaveAttribute( 'href', '/catalog/default/group/examplegroup', ); diff --git a/plugins/pagerduty/CHANGELOG.md b/plugins/pagerduty/CHANGELOG.md index 457905f993..8d00f36ada 100644 --- a/plugins/pagerduty/CHANGELOG.md +++ b/plugins/pagerduty/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-pagerduty +## 0.6.7-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.6.6 ### Patch Changes diff --git a/plugins/pagerduty/package.json b/plugins/pagerduty/package.json index 0130724cfa..6ad44ac45b 100644 --- a/plugins/pagerduty/package.json +++ b/plugins/pagerduty/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-pagerduty", "description": "A Backstage plugin that integrates towards PagerDuty", - "version": "0.6.6", + "version": "0.6.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/periskop-backend/CHANGELOG.md b/plugins/periskop-backend/CHANGELOG.md index 03f74fb422..23f65db9d7 100644 --- a/plugins/periskop-backend/CHANGELOG.md +++ b/plugins/periskop-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-periskop-backend +## 0.2.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + ## 0.2.3 ### Patch Changes diff --git a/plugins/periskop-backend/alpha-api-report.md b/plugins/periskop-backend/alpha-api-report.md new file mode 100644 index 0000000000..d6ace05993 --- /dev/null +++ b/plugins/periskop-backend/alpha-api-report.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-periskop-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'; + +// @alpha +const _default: () => BackendFeature; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/periskop-backend/api-report.md b/plugins/periskop-backend/api-report.md index 46d5de8d93..caaed109f9 100644 --- a/plugins/periskop-backend/api-report.md +++ b/plugins/periskop-backend/api-report.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; @@ -11,10 +10,6 @@ import { Logger } from 'winston'; // @public (undocumented) export function createRouter(options: RouterOptions): Promise; -// @alpha -const periskopPlugin: () => BackendFeature; -export default periskopPlugin; - // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/periskop-backend/package.json b/plugins/periskop-backend/package.json index 1ef6c62431..dc6da1c706 100644 --- a/plugins/periskop-backend/package.json +++ b/plugins/periskop-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop-backend", - "version": "0.2.3", + "version": "0.2.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -14,14 +14,26 @@ "directory": "plugins/periskop-backend" }, "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/periskop-backend/src/plugin.ts b/plugins/periskop-backend/src/alpha.ts similarity index 96% rename from plugins/periskop-backend/src/plugin.ts rename to plugins/periskop-backend/src/alpha.ts index 2123dcbece..59271e02fc 100644 --- a/plugins/periskop-backend/src/plugin.ts +++ b/plugins/periskop-backend/src/alpha.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * * @alpha */ -export const periskopPlugin = createBackendPlugin({ +export default createBackendPlugin({ pluginId: 'periskop', register(env) { env.registerInit({ diff --git a/plugins/periskop-backend/src/index.ts b/plugins/periskop-backend/src/index.ts index 17a6d0d054..ca73cb27ba 100644 --- a/plugins/periskop-backend/src/index.ts +++ b/plugins/periskop-backend/src/index.ts @@ -15,4 +15,3 @@ */ export * from './service/router'; -export { periskopPlugin as default } from './plugin'; diff --git a/plugins/periskop/CHANGELOG.md b/plugins/periskop/CHANGELOG.md index 26c40e8b97..6ad4fa1051 100644 --- a/plugins/periskop/CHANGELOG.md +++ b/plugins/periskop/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-periskop +## 0.1.24-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.23 ### Patch Changes diff --git a/plugins/periskop/package.json b/plugins/periskop/package.json index 9ec13e9ce1..8f7d424ac7 100644 --- a/plugins/periskop/package.json +++ b/plugins/periskop/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-periskop", - "version": "0.1.23", + "version": "0.1.24-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md index 63e6e54cc2..e9ea97f2fc 100644 --- a/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md +++ b/plugins/permission-backend-module-policy-allow-all/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-permission-backend-module-allow-all-policy +## 0.1.4-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.1.3 ### 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 6ae8cc459d..048ebc8c4a 100644 --- a/plugins/permission-backend-module-policy-allow-all/package.json +++ b/plugins/permission-backend-module-policy-allow-all/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-backend-module-allow-all-policy", "description": "Allow all policy backend module for the permission plugin.", - "version": "0.1.3", + "version": "0.1.4-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-backend/CHANGELOG.md b/plugins/permission-backend/CHANGELOG.md index a26b60baaf..c84ab82a94 100644 --- a/plugins/permission-backend/CHANGELOG.md +++ b/plugins/permission-backend/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-permission-backend +## 0.5.30-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 0.5.29 ### Patch Changes diff --git a/plugins/permission-backend/package.json b/plugins/permission-backend/package.json index f222d51451..70f2bcc3fd 100644 --- a/plugins/permission-backend/package.json +++ b/plugins/permission-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-permission-backend", - "version": "0.5.29", + "version": "0.5.30-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-node/CHANGELOG.md b/plugins/permission-node/CHANGELOG.md index 222399e878..45fd417987 100644 --- a/plugins/permission-node/CHANGELOG.md +++ b/plugins/permission-node/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-permission-node +## 0.7.18-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + ## 0.7.17 ### Patch Changes diff --git a/plugins/permission-node/package.json b/plugins/permission-node/package.json index 4ed3ceb7ae..ccdcd9b6b9 100644 --- a/plugins/permission-node/package.json +++ b/plugins/permission-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-permission-node", "description": "Common permission and authorization utilities for backend plugins", - "version": "0.7.17", + "version": "0.7.18-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/permission-react/CHANGELOG.md b/plugins/permission-react/CHANGELOG.md index 493ee440b6..c7aa0a2e81 100644 --- a/plugins/permission-react/CHANGELOG.md +++ b/plugins/permission-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-permission-react +## 0.4.17-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + ## 0.4.16 ### Patch Changes diff --git a/plugins/permission-react/package.json b/plugins/permission-react/package.json index 9a9650e762..7e432821b2 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.16", + "version": "0.4.17-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist-backend/CHANGELOG.md b/plugins/playlist-backend/CHANGELOG.md index 2fa8f35268..0d147a9029 100644 --- a/plugins/playlist-backend/CHANGELOG.md +++ b/plugins/playlist-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-playlist-backend +## 0.3.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-playlist-common@0.1.11 + ## 0.3.10 ### Patch Changes diff --git a/plugins/playlist-backend/package.json b/plugins/playlist-backend/package.json index d5b6a5bdaf..0173864dce 100644 --- a/plugins/playlist-backend/package.json +++ b/plugins/playlist-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist-backend", - "version": "0.3.10", + "version": "0.3.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/CHANGELOG.md b/plugins/playlist/CHANGELOG.md index 16c0f02912..07a09f24dd 100644 --- a/plugins/playlist/CHANGELOG.md +++ b/plugins/playlist/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-playlist +## 0.1.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-playlist-common@0.1.11 + ## 0.1.17 ### Patch Changes diff --git a/plugins/playlist/package.json b/plugins/playlist/package.json index de53e9fafe..0fbb73f48c 100644 --- a/plugins/playlist/package.json +++ b/plugins/playlist/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-playlist", - "version": "0.1.17", + "version": "0.1.18-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx index 1bf698f6be..1408607133 100644 --- a/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx +++ b/plugins/playlist/src/components/PlaylistCard/PlaylistCard.tsx @@ -22,7 +22,6 @@ import { import { useRouteRef } from '@backstage/core-plugin-api'; import { EntityRefLinks } from '@backstage/plugin-catalog-react'; import { Playlist } from '@backstage/plugin-playlist-common'; -import { BackstageTheme } from '@backstage/theme'; import { Box, Card, @@ -36,10 +35,9 @@ import { } from '@material-ui/core'; import LockIcon from '@material-ui/icons/Lock'; import React from 'react'; - import { playlistRouteRef } from '../../routes'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ cardHeader: { position: 'relative', }, diff --git a/plugins/proxy-backend/CHANGELOG.md b/plugins/proxy-backend/CHANGELOG.md index 6137545f06..ce965487d9 100644 --- a/plugins/proxy-backend/CHANGELOG.md +++ b/plugins/proxy-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-proxy-backend +## 0.4.5-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + ## 0.4.3 ### Patch Changes diff --git a/plugins/proxy-backend/alpha-api-report.md b/plugins/proxy-backend/alpha-api-report.md new file mode 100644 index 0000000000..73c3052acb --- /dev/null +++ b/plugins/proxy-backend/alpha-api-report.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-proxy-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'; + +// @alpha +const _default: () => BackendFeature; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/proxy-backend/api-report.md b/plugins/proxy-backend/api-report.md index ae327d485a..12e6bfa1c9 100644 --- a/plugins/proxy-backend/api-report.md +++ b/plugins/proxy-backend/api-report.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; @@ -12,10 +11,6 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; // @public export function createRouter(options: RouterOptions): Promise; -// @alpha -const proxyPlugin: () => BackendFeature; -export default proxyPlugin; - // @public (undocumented) export interface RouterOptions { // (undocumented) diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index adbfe9fce9..77f68dd628 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -1,15 +1,27 @@ { "name": "@backstage/plugin-proxy-backend", "description": "A Backstage backend plugin that helps you set up proxy endpoints in the backend", - "version": "0.4.3", + "version": "0.4.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "backend-plugin" @@ -25,7 +37,7 @@ ], "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/alpha.ts similarity index 96% rename from plugins/proxy-backend/src/plugin.ts rename to plugins/proxy-backend/src/alpha.ts index e4c14b0037..9f6bad4832 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/alpha.ts @@ -26,7 +26,7 @@ import { createRouter } from './service/router'; * * @alpha */ -export const proxyPlugin = createBackendPlugin({ +export default createBackendPlugin({ pluginId: 'proxy', register(env) { env.registerInit({ diff --git a/plugins/proxy-backend/src/index.ts b/plugins/proxy-backend/src/index.ts index 3163abd381..8f5d5c79dc 100644 --- a/plugins/proxy-backend/src/index.ts +++ b/plugins/proxy-backend/src/index.ts @@ -21,4 +21,3 @@ */ export * from './service'; -export { proxyPlugin as default } from './plugin'; diff --git a/plugins/puppetdb/CHANGELOG.md b/plugins/puppetdb/CHANGELOG.md index 2d184e7a48..91059ae37e 100644 --- a/plugins/puppetdb/CHANGELOG.md +++ b/plugins/puppetdb/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-puppetdb +## 0.1.9-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.8 ### Patch Changes diff --git a/plugins/puppetdb/package.json b/plugins/puppetdb/package.json index 89cd83fcde..fdc460ffc6 100644 --- a/plugins/puppetdb/package.json +++ b/plugins/puppetdb/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-puppetdb", "description": "Backstage plugin to visualize resource information and Puppet facts from PuppetDB.", - "version": "0.1.8", + "version": "0.1.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar-backend/CHANGELOG.md b/plugins/rollbar-backend/CHANGELOG.md index dd2df54811..3e427756d8 100644 --- a/plugins/rollbar-backend/CHANGELOG.md +++ b/plugins/rollbar-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-rollbar-backend +## 0.1.52-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + ## 0.1.51 ### Patch Changes diff --git a/plugins/rollbar-backend/package.json b/plugins/rollbar-backend/package.json index e5dbd3a997..0a8539fa81 100644 --- a/plugins/rollbar-backend/package.json +++ b/plugins/rollbar-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar-backend", "description": "A Backstage backend plugin that integrates towards Rollbar", - "version": "0.1.51", + "version": "0.1.52-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/rollbar/CHANGELOG.md b/plugins/rollbar/CHANGELOG.md index 46d27e8bad..7d62bc1c6c 100644 --- a/plugins/rollbar/CHANGELOG.md +++ b/plugins/rollbar/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-rollbar +## 0.4.26-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.4.25 ### Patch Changes diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json index d82402c03e..4c19c02d27 100644 --- a/plugins/rollbar/package.json +++ b/plugins/rollbar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-rollbar", "description": "A Backstage plugin that integrates towards Rollbar", - "version": "0.4.25", + "version": "0.4.26-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md index 2b931dd7da..e57d942c2b 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-confluence-to-markdown +## 0.2.8-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.2.7 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json index 9d506d824b..c18d1ae21c 100644 --- a/plugins/scaffolder-backend-module-confluence-to-markdown/package.json +++ b/plugins/scaffolder-backend-module-confluence-to-markdown/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-confluence-to-markdown", "description": "The confluence-to-markdown module for @backstage/plugin-scaffolder-backend", - "version": "0.2.7", + "version": "0.2.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md index 9c68b53d40..8929694ace 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-cookiecutter/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-cookiecutter +## 0.2.31-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.2.30 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-cookiecutter/package.json b/plugins/scaffolder-backend-module-cookiecutter/package.json index 60d9f19a70..f46dac12a9 100644 --- a/plugins/scaffolder-backend-module-cookiecutter/package.json +++ b/plugins/scaffolder-backend-module-cookiecutter/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-cookiecutter", "description": "A module for the scaffolder backend that lets you template projects using cookiecutter", - "version": "0.2.30", + "version": "0.2.31-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md index 2582ed3227..c7fe774851 100644 --- a/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-gitlab/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-scaffolder-backend-module-gitlab +## 0.2.10-next.0 + +### Patch Changes + +- 26ca97ebaa: Add examples for `gitlab:projectAccessToken:create` scaffolder action & improve related tests +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.9 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-gitlab/package.json b/plugins/scaffolder-backend-module-gitlab/package.json index 99c84ed4b9..7973fa4806 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.2.9", + "version": "0.2.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -36,6 +36,7 @@ "@backstage/integration": "workspace:^", "@backstage/plugin-scaffolder-node": "workspace:^", "@gitbeaker/node": "^35.8.0", + "yaml": "^2.0.0", "zod": "^3.21.4" }, "devDependencies": { diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.test.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.test.ts new file mode 100644 index 0000000000..6a809fc386 --- /dev/null +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.test.ts @@ -0,0 +1,303 @@ +/* + * 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 yaml from 'yaml'; +import { createGitlabProjectAccessTokenAction } from './createGitlabProjectAccessTokenAction'; // Adjust the import based on your project structure +import { ScmIntegrations } from '@backstage/integration'; +import { ConfigReader } from '@backstage/config'; +import { getVoidLogger } from '@backstage/backend-common'; +import { PassThrough } from 'stream'; +import { examples } from './createGitlabProjectAccessTokenAction.examples'; + +jest.mock('node-fetch'); + +const mockGitlabClient = { + ProjectDeployTokens: { + create: jest.fn(), + }, +}; + +jest.mock('@gitbeaker/node', () => ({ + Gitlab: class { + constructor() { + return mockGitlabClient; + } + }, +})); + +describe('gitlab:projectAccessToken:create examples', () => { + const config = new ConfigReader({ + integrations: { + gitlab: [ + { + host: 'gitlab.com', + token: 'tokenlols', + apiBaseUrl: 'https://api.gitlab.com', + }, + { + host: 'hosted.gitlab.com', + apiBaseUrl: 'https://api.hosted.gitlab.com', + }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + const action = createGitlabProjectAccessTokenAction({ integrations }); + + const mockContext = { + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + }, + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + }); + + it('Create a GitLab project access token with minimal options.', async () => { + const fetchMock = jest.spyOn(global, 'fetch'); + const mockResponse = { + token: 'mock-access-token', + }; + + fetchMock.mockResolvedValue({ + json: async () => mockResponse, + } as Response); + + jest.mock('../util', () => ({ + getToken: jest.fn().mockReturnValue({ + token: 'mock-api-token', + integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } }, + }), + })); + + const input = yaml.parse(examples[0].example).steps[0].input; + + await action.handler({ + ...mockContext, + input, + }); + + expect(fetchMock).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/456/access_tokens', + { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': 'tokenlols', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: undefined, + scopes: undefined, + access_level: undefined, + }), + }, + ); + expect(mockContext.output).toHaveBeenCalledWith( + 'access_token', + 'mock-access-token', + ); + }); + + it('Create a GitLab project access token with custom scopes.', async () => { + const input = yaml.parse(examples[1].example).steps[0].input; + + const mockResponse = { + token: 'mock-access-token', + }; + + const fetchMock = jest.spyOn(global, 'fetch'); + fetchMock.mockResolvedValue({ + json: async () => mockResponse, + } as Response); + + jest.mock('../util', () => ({ + getToken: jest.fn().mockReturnValue({ + token: 'mock-api-token', + integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } }, + }), + })); + + await action.handler({ + ...mockContext, + input, + }); + + expect(fetchMock).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/789/access_tokens', + { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': 'tokenlols', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: undefined, + scopes: ['read_registry', 'write_repository'], + access_level: undefined, + }), + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith( + 'access_token', + 'mock-access-token', + ); + }); + + it('Create a GitLab project access token with a specified name.', async () => { + const input = yaml.parse(examples[2].example).steps[0].input; + + const mockResponse = { + token: 'mock-access-token', + }; + + const fetchMock = jest.spyOn(global, 'fetch'); + fetchMock.mockResolvedValue({ + json: async () => mockResponse, + } as Response); + + jest.mock('../util', () => ({ + getToken: jest.fn().mockReturnValue({ + token: 'mock-api-token', + integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } }, + }), + })); + + await action.handler({ + ...mockContext, + input, + }); + + expect(fetchMock).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/101112/access_tokens', + { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': 'tokenlols', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: 'my-custom-token', + scopes: undefined, + access_level: undefined, + }), + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith( + 'access_token', + 'mock-access-token', + ); + }); + + it('Create a GitLab project access token with a numeric project ID.', async () => { + const input = yaml.parse(examples[3].example).steps[0].input; + + const mockResponse = { + token: 'mock-access-token', + }; + + const fetchMock = jest.spyOn(global, 'fetch'); + fetchMock.mockResolvedValue({ + json: async () => mockResponse, + } as Response); + + jest.mock('../util', () => ({ + getToken: jest.fn().mockReturnValue({ + token: 'mock-api-token', + integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } }, + }), + })); + + await action.handler({ + ...mockContext, + input, + }); + + expect(fetchMock).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/42/access_tokens', + { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': 'tokenlols', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: undefined, + scopes: undefined, + access_level: undefined, + }), + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith( + 'access_token', + 'mock-access-token', + ); + }); + + it('Create a GitLab project access token using specific GitLab integrations.', async () => { + const input = yaml.parse(examples[4].example).steps[0].input; + + const mockResponse = { + token: 'mock-access-token', + }; + + const fetchMock = jest.spyOn(global, 'fetch'); + fetchMock.mockResolvedValue({ + json: async () => mockResponse, + } as Response); + + jest.mock('../util', () => ({ + getToken: jest.fn().mockReturnValue({ + token: 'mock-api-token', + integrationConfig: { config: { baseUrl: 'https://api.gitlab.com' } }, + }), + })); + + await action.handler({ + ...mockContext, + input, + }); + + expect(fetchMock).toHaveBeenCalledWith( + 'https://gitlab.com/api/v4/projects/123/access_tokens', + { + method: 'POST', + headers: { + 'PRIVATE-TOKEN': 'tokenlols', + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: undefined, + scopes: undefined, + access_level: undefined, + }), + }, + ); + + expect(mockContext.output).toHaveBeenCalledWith( + 'access_token', + 'mock-access-token', + ); + }); +}); diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.ts new file mode 100644 index 0000000000..5fe157badf --- /dev/null +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.examples.ts @@ -0,0 +1,104 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Create a GitLab project access token with minimal options.', + example: yaml.stringify({ + steps: [ + { + id: 'createAccessToken', + action: 'gitlab:projectAccessToken:create', + name: 'Create GitLab Project Access Token', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '456', + }, + }, + ], + }), + }, + { + description: 'Create a GitLab project access token with custom scopes.', + example: yaml.stringify({ + steps: [ + { + id: 'createAccessToken', + action: 'gitlab:projectAccessToken:create', + name: 'Create GitLab Project Access Token', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '789', + scopes: ['read_registry', 'write_repository'], + }, + }, + ], + }), + }, + { + description: 'Create a GitLab project access token with a specified name.', + example: yaml.stringify({ + steps: [ + { + id: 'createAccessToken', + action: 'gitlab:projectAccessToken:create', + name: 'Create GitLab Project Access Token', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '101112', + name: 'my-custom-token', + }, + }, + ], + }), + }, + { + description: + 'Create a GitLab project access token with a numeric project ID.', + example: yaml.stringify({ + steps: [ + { + id: 'createAccessToken', + action: 'gitlab:projectAccessToken:create', + name: 'Create GitLab Project Access Token', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: 42, + }, + }, + ], + }), + }, + { + description: + 'Create a GitLab project access token using specific GitLab integrations.', + example: yaml.stringify({ + steps: [ + { + id: 'createAccessToken', + action: 'gitlab:projectAccessToken:create', + name: 'Create GitLab Project Access Token', + input: { + repoUrl: 'gitlab.com?repo=repo&owner=owner', + projectId: '123', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.ts b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.ts index 614de0e221..a050b3c1b4 100644 --- a/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.ts +++ b/plugins/scaffolder-backend-module-gitlab/src/actions/createGitlabProjectAccessTokenAction.ts @@ -19,6 +19,7 @@ import { ScmIntegrationRegistry } from '@backstage/integration'; import commonGitlabConfig from '../commonGitlabConfig'; import { getToken } from '../util'; import { z } from 'zod'; +import { examples } from './createGitlabProjectAccessTokenAction.examples'; /** * Creates a `gitlab:projectAccessToken:create` Scaffolder action. @@ -32,6 +33,7 @@ export const createGitlabProjectAccessTokenAction = (options: { const { integrations } = options; return createTemplateAction({ id: 'gitlab:projectAccessToken:create', + examples, schema: { input: commonGitlabConfig.merge( z.object({ diff --git a/plugins/scaffolder-backend-module-rails/CHANGELOG.md b/plugins/scaffolder-backend-module-rails/CHANGELOG.md index 70af7932b8..daa3a74d8d 100644 --- a/plugins/scaffolder-backend-module-rails/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-rails/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-scaffolder-backend-module-rails +## 0.4.24-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.4.23 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-rails/package.json b/plugins/scaffolder-backend-module-rails/package.json index 0763690d7c..b1da603d4e 100644 --- a/plugins/scaffolder-backend-module-rails/package.json +++ b/plugins/scaffolder-backend-module-rails/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend-module-rails", "description": "A module for the scaffolder backend that lets you template projects using Rails", - "version": "0.4.23", + "version": "0.4.24-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md index 9e0bd8734b..9e0a389114 100644 --- a/plugins/scaffolder-backend-module-sentry/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-sentry/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-sentry +## 0.1.15-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.1.14 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-sentry/package.json b/plugins/scaffolder-backend-module-sentry/package.json index ce1b8247f8..abad17cda8 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.1.14", + "version": "0.1.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md index fa7774558a..dc01bd58d0 100644 --- a/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md +++ b/plugins/scaffolder-backend-module-yeoman/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-scaffolder-backend-module-yeoman +## 0.2.28-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + ## 0.2.27 ### Patch Changes diff --git a/plugins/scaffolder-backend-module-yeoman/package.json b/plugins/scaffolder-backend-module-yeoman/package.json index 36b8c4e76b..63591cc05e 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.2.27", + "version": "0.2.28-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/CHANGELOG.md b/plugins/scaffolder-backend/CHANGELOG.md index f2258517a6..107b707183 100644 --- a/plugins/scaffolder-backend/CHANGELOG.md +++ b/plugins/scaffolder-backend/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-scaffolder-backend +## 1.19.0-next.0 + +### Minor Changes + +- 5e4127c18e: Allow setting `update: true` in `publish:github:pull-request` scaffolder action + +### Patch Changes + +- 0920fd02ac: Add examples for `github:environment:create` scaffolder action & improve related tests +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- 99d4936f6c: Add examples for `github:webhook` scaffolder action & improve related tests +- f8727ad228: Add examples for `publish:github:pull-request` scaffolder action & improve related tests +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-backend@1.15.0-next.0 + - @backstage/plugin-scaffolder-node@0.2.8-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.4-next.0 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + ## 1.18.0 ### Minor Changes diff --git a/plugins/scaffolder-backend/api-report.md b/plugins/scaffolder-backend/api-report.md index ba7576b1bb..1f8615b477 100644 --- a/plugins/scaffolder-backend/api-report.md +++ b/plugins/scaffolder-backend/api-report.md @@ -659,6 +659,7 @@ export const createPublishGithubPullRequestAction: ( reviewers?: string[] | undefined; teamReviewers?: string[] | undefined; commitMessage?: string | undefined; + update?: boolean | undefined; }, JsonObject >; diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 20c56e9a8d..430cdc84e1 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-backend", "description": "The Backstage backend plugin that helps you create new things", - "version": "1.18.0", + "version": "1.19.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts index 98bd4a1d66..beabaa50aa 100644 --- a/plugins/scaffolder-backend/src/ScaffolderPlugin.ts +++ b/plugins/scaffolder-backend/src/ScaffolderPlugin.ts @@ -22,14 +22,16 @@ import { loggerToWinstonLogger } from '@backstage/backend-common'; import { ScmIntegrations } from '@backstage/integration'; import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import { - scaffolderActionsExtensionPoint, - scaffolderTaskBrokerExtensionPoint, - scaffolderTemplatingExtensionPoint, TaskBroker, TemplateAction, TemplateFilter, TemplateGlobal, } from '@backstage/plugin-scaffolder-node'; +import { + scaffolderActionsExtensionPoint, + scaffolderTaskBrokerExtensionPoint, + scaffolderTemplatingExtensionPoint, +} from '@backstage/plugin-scaffolder-node/alpha'; import { createBuiltinActions } from './scaffolder'; import { createRouter } from './service/router'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/gitHubEnvironment.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/gitHubEnvironment.examples.ts new file mode 100644 index 0000000000..606424bef0 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/gitHubEnvironment.examples.ts @@ -0,0 +1,99 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: + 'Create a GitHub Environment (No Policies, No Variables, No Secrets)', + example: yaml.stringify({ + steps: [ + { + action: 'github:environment:create', + name: 'Create Environment', + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + name: 'envname', + }, + }, + ], + }), + }, + { + description: 'Create a GitHub Environment with Protected Branch Policy', + example: yaml.stringify({ + steps: [ + { + action: 'github:environment:create', + name: 'Create Environment', + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + name: 'envname', + deploymentBranchPolicy: { + protected_branches: true, + custom_branch_policies: false, + }, + }, + }, + ], + }), + }, + { + description: 'Create a GitHub Environment with Custom Branch Policies', + example: yaml.stringify({ + steps: [ + { + action: 'github:environment:create', + name: 'Create Environment', + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + name: 'envname', + deploymentBranchPolicy: { + protected_branches: false, + custom_branch_policies: true, + }, + customBranchPolicyNames: ['main', '*.*.*'], + }, + }, + ], + }), + }, + { + description: + 'Create a GitHub Environment with Environment Variables and Secrets', + example: yaml.stringify({ + steps: [ + { + action: 'github:environment:create', + name: 'Create Environment', + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + name: 'envname', + environmentVariables: { + key1: 'val1', + key2: 'val2', + }, + secrets: { + secret1: 'supersecret1', + secret2: 'supersecret2', + }, + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.test.ts new file mode 100644 index 0000000000..c8e2a5f102 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.test.ts @@ -0,0 +1,111 @@ +/* + * 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 { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { getVoidLogger } from '@backstage/backend-common'; +import { createGithubDeployKeyAction } from './githubDeployKey'; +import yaml from 'yaml'; +import { examples } from './githubDeployKey.examples'; +import { PassThrough } from 'stream'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; + +const mockOctokit = { + rest: { + repos: { + createDeployKey: jest.fn(), + }, + actions: { + getRepoPublicKey: jest.fn(), + createOrUpdateRepoSecret: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + +describe('Usage examples', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + const integrations = ScmIntegrations.fromConfig(config); + let action: TemplateAction; + + const mockContext = { + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + jest.resetAllMocks(); + + action = createGithubDeployKeyAction({ + integrations, + }); + }); + + it('Example 1: Create and store a Deploy Key', async () => { + const input = yaml.parse(examples[0].example).steps[0].input; + + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); + + await action.handler({ + ...mockContext, + input, + }); + + expect(mockOctokit.rest.repos.createDeployKey).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + title: 'Push Tags', + key: 'pubkey', + }); + + expect( + mockOctokit.rest.actions.createOrUpdateRepoSecret, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + secret_name: 'PUSH_TAGS_PRIVATE_KEY', + key_id: 'keyid', + encrypted_value: expect.any(String), + }); + + expect(mockContext.output).toHaveBeenCalledWith( + 'privateKeySecretName', + 'PUSH_TAGS_PRIVATE_KEY', + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.ts new file mode 100644 index 0000000000..be8c0ad7bc --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.examples.ts @@ -0,0 +1,37 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Example 1: Create and store a Deploy Key', + example: yaml.stringify({ + steps: [ + { + action: 'github:deployKey:create', + name: 'Create and store a Deploy Key', + input: { + repoUrl: 'github.com?repo=repository&owner=owner', + publicKey: 'pubkey', + privateKey: 'privkey', + deployKeyName: 'Push Tags', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.ts index 6ad646a89c..1db9746bb4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubDeployKey.ts @@ -21,6 +21,7 @@ import { parseRepoUrl } from '../publish/util'; import { getOctokitOptions } from './helpers'; import { Octokit } from 'octokit'; import Sodium from 'libsodium-wrappers'; +import { examples } from './githubDeployKey.examples'; /** * Creates an `github:deployKey:create` Scaffolder action that creates a Deploy Key @@ -43,6 +44,7 @@ export function createGithubDeployKeyAction(options: { }>({ id: 'github:deployKey:create', description: 'Creates and stores Deploy Keys', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.examples.test.ts new file mode 100644 index 0000000000..5e3da2fcec --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.examples.test.ts @@ -0,0 +1,260 @@ +/* + * 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 { PassThrough } from 'stream'; +import { createGithubEnvironmentAction } from './githubEnvironment'; +import { getVoidLogger } from '@backstage/backend-common'; +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { ConfigReader } from '@backstage/config'; +import { ScmIntegrations } from '@backstage/integration'; +import yaml from 'yaml'; +import { examples } from './gitHubEnvironment.examples'; + +const mockOctokit = { + rest: { + actions: { + getRepoPublicKey: jest.fn(), + createEnvironmentVariable: jest.fn(), + createOrUpdateEnvironmentSecret: jest.fn(), + }, + repos: { + createDeploymentBranchPolicy: jest.fn(), + createOrUpdateEnvironment: jest.fn(), + get: jest.fn(), + }, + }, +}; +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +const publicKey = '2Sg8iYjAxxmI2LvUXpJjkYrMxURPc8r+dB7TJyvvcCU='; + +describe('github:environment:create examples', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + + const integrations = ScmIntegrations.fromConfig(config); + let action: TemplateAction; + + const mockContext = { + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + + beforeEach(() => { + mockOctokit.rest.actions.getRepoPublicKey.mockResolvedValue({ + data: { + key: publicKey, + key_id: 'keyid', + }, + }); + mockOctokit.rest.repos.get.mockResolvedValue({ + data: { + id: 'repoid', + }, + }); + + action = createGithubEnvironmentAction({ + integrations, + }); + }); + + afterEach(jest.resetAllMocks); + + it('Create a GitHub Environment (No Policies, No Variables, No Secrets)', async () => { + const input = yaml.parse(examples[0].example).steps[0].input; + + await action.handler({ + ...mockContext, + input, + }); + + expect( + mockOctokit.rest.repos.createOrUpdateEnvironment, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + deployment_branch_policy: null, + }); + expect( + mockOctokit.rest.repos.createDeploymentBranchPolicy, + ).not.toHaveBeenCalled(); + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).not.toHaveBeenCalled(); + expect(mockOctokit.rest.actions.getRepoPublicKey).not.toHaveBeenCalled(); + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).not.toHaveBeenCalled(); + }); + + it('Create a GitHub Environment with Protected Branch Policy', async () => { + const input = yaml.parse(examples[1].example).steps[0].input; + + await action.handler({ + ...mockContext, + input, + }); + + expect( + mockOctokit.rest.repos.createOrUpdateEnvironment, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + deployment_branch_policy: { + protected_branches: true, + custom_branch_policies: false, + }, + }); + + expect( + mockOctokit.rest.repos.createDeploymentBranchPolicy, + ).not.toHaveBeenCalled(); + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).not.toHaveBeenCalled(); + expect(mockOctokit.rest.actions.getRepoPublicKey).not.toHaveBeenCalled(); + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).not.toHaveBeenCalled(); + }); + + it('Create a GitHub Environment with Custom Branch Policies', async () => { + const input = yaml.parse(examples[2].example).steps[0].input; + + await action.handler({ + ...mockContext, + input, + }); + + expect( + mockOctokit.rest.repos.createOrUpdateEnvironment, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + deployment_branch_policy: { + protected_branches: false, + custom_branch_policies: true, + }, + }); + + expect( + mockOctokit.rest.repos.createDeploymentBranchPolicy, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + name: 'main', + }); + + expect( + mockOctokit.rest.repos.createDeploymentBranchPolicy, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + name: '*.*.*', + }); + + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).not.toHaveBeenCalled(); + + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).not.toHaveBeenCalled(); + }); + + it('Create a GitHub Environment with Environment Variables and Secrets', async () => { + const input = yaml.parse(examples[3].example).steps[0].input; + + await action.handler({ + ...mockContext, + input, + }); + + expect( + mockOctokit.rest.repos.createOrUpdateEnvironment, + ).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repository', + environment_name: 'envname', + deployment_branch_policy: null, + }); + + expect( + mockOctokit.rest.repos.createDeploymentBranchPolicy, + ).not.toHaveBeenCalled(); + + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).toHaveBeenCalledTimes(2); + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).toHaveBeenCalledWith({ + repository_id: 'repoid', + environment_name: 'envname', + name: 'key1', + value: 'val1', + }); + expect( + mockOctokit.rest.actions.createEnvironmentVariable, + ).toHaveBeenCalledWith({ + repository_id: 'repoid', + environment_name: 'envname', + name: 'key2', + value: 'val2', + }); + + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).toHaveBeenCalledTimes(2); + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).toHaveBeenCalledWith({ + repository_id: 'repoid', + environment_name: 'envname', + secret_name: 'secret1', + key_id: 'keyid', + encrypted_value: expect.any(String), + }); + expect( + mockOctokit.rest.actions.createOrUpdateEnvironmentSecret, + ).toHaveBeenCalledWith({ + repository_id: 'repoid', + environment_name: 'envname', + secret_name: 'secret2', + key_id: 'keyid', + encrypted_value: expect.any(String), + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.ts index 04b33ed259..874798e4df 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/github/githubEnvironment.ts @@ -21,6 +21,7 @@ import { parseRepoUrl } from '../publish/util'; import { getOctokitOptions } from './helpers'; import { Octokit } from 'octokit'; import Sodium from 'libsodium-wrappers'; +import { examples } from './gitHubEnvironment.examples'; /** * Creates an `github:environment:create` Scaffolder action that creates a Github Environment. @@ -47,6 +48,7 @@ export function createGithubEnvironmentAction(options: { }>({ id: 'github:environment:create', description: 'Creates Deployment Environments', + examples, schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.test.ts new file mode 100644 index 0000000000..3f13f2ab67 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.test.ts @@ -0,0 +1,552 @@ +/* + * 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 { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { getVoidLogger } from '@backstage/backend-common'; +import { ConfigReader } from '@backstage/config'; +import { + DefaultGithubCredentialsProvider, + GithubCredentialsProvider, + ScmIntegrations, +} from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { + OctokitWithPullRequestPluginClient, + createPublishGithubPullRequestAction, +} from './githubPullRequest'; +import yaml from 'yaml'; +import { examples } from './githubPullRequest.examples'; +import { createMockDirectory } from '@backstage/backend-test-utils'; + +const mockOctokit = { + rest: { + pulls: { + requestReviewers: jest.fn(), + }, + }, +}; + +jest.mock('octokit', () => ({ + Octokit: class { + constructor() { + return mockOctokit; + } + }, +})); + +describe('publish:github:pull-request examples', () => { + const config = new ConfigReader({ + integrations: { + github: [ + { host: 'github.com', token: 'tokenlols' }, + { host: 'ghe.github.com' }, + ], + }, + }); + const integrations = ScmIntegrations.fromConfig(config); + let githubCredentialsProvider: GithubCredentialsProvider; + let action: TemplateAction; + + const mockContext = { + workspacePath: 'lol', + logger: getVoidLogger(), + logStream: new PassThrough(), + output: jest.fn(), + createTemporaryDirectory: jest.fn(), + }; + let fakeClient: { + createPullRequest: jest.Mock; + rest: { + pulls: { requestReviewers: jest.Mock }; + }; + }; + const mockDir = createMockDirectory(); + const workspacePath = mockDir.resolve('workspace'); + + beforeEach(() => { + mockDir.clear(); + jest.resetAllMocks(); + githubCredentialsProvider = + DefaultGithubCredentialsProvider.fromIntegrations(integrations); + fakeClient = { + createPullRequest: jest.fn(async (_: any) => { + return { + url: 'https://api.github.com/myorg/myrepo/pull/123', + headers: {}, + status: 201, + data: { + html_url: 'https://github.com/myorg/myrepo/pull/123', + number: 123, + base: { + ref: 'main', + }, + }, + }; + }), + rest: { + pulls: { + requestReviewers: jest.fn(async (_: any) => ({ data: {} })), + }, + }, + }; + + const clientFactory = jest.fn( + async () => fakeClient as unknown as OctokitWithPullRequestPluginClient, + ); + + mockDir.setContent({ + [workspacePath]: { 'file.txt': 'Hello there!' }, + }); + + action = createPublishGithubPullRequestAction({ + integrations, + githubCredentialsProvider, + clientFactory, + }); + }); + + afterEach(jest.resetAllMocks); + + it('Create a pull request', async () => { + const input = yaml.parse(examples[0].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with target branch name', async () => { + const input = yaml.parse(examples[1].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + base: 'test', + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request as draft', async () => { + const input = yaml.parse(examples[2].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: true, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with target path', async () => { + const input = yaml.parse(examples[3].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'targetPath/file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with source path', async () => { + mockDir.setContent({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + const input = yaml.parse(examples[4].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'foo.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with token', async () => { + const input = yaml.parse(examples[5].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with reviewers', async () => { + const input = yaml.parse(examples[6].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + pull_number: 123, + reviewers: ['foobar'], + }); + + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with team reviewers', async () => { + const input = yaml.parse(examples[7].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Create my new app', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + pull_number: 123, + team_reviewers: ['team-foo'], + }); + + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with commit message', async () => { + const input = yaml.parse(examples[8].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: undefined, + changes: [ + { + commit: 'Custom commit message', + files: { + 'file.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).not.toHaveBeenCalled(); + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); + + it('Create a pull request with all parameters', async () => { + mockDir.setContent({ + [workspacePath]: { + source: { 'foo.txt': 'Hello there!' }, + irrelevant: { 'bar.txt': 'Nothing to see here' }, + }, + }); + const input = yaml.parse(examples[9].example).steps[0].input; + + await action.handler({ + ...mockContext, + workspacePath, + input, + }); + + expect(fakeClient.createPullRequest).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + title: 'Create my new app', + body: 'This PR is really good', + head: 'new-app', + draft: true, + base: 'test', + changes: [ + { + commit: 'Commit for foo changes', + files: { + 'targetPath/foo.txt': { + content: Buffer.from('Hello there!').toString('base64'), + encoding: 'base64', + mode: '100644', + }, + }, + }, + ], + }); + + expect(fakeClient.rest.pulls.requestReviewers).toHaveBeenCalledWith({ + owner: 'owner', + repo: 'repo', + pull_number: 123, + reviewers: ['foobar'], + team_reviewers: ['team-foo'], + }); + + expect(mockContext.output).toHaveBeenCalledTimes(3); + expect(mockContext.output).toHaveBeenCalledWith('targetBranchName', 'main'); + expect(mockContext.output).toHaveBeenCalledWith( + 'remoteUrl', + 'https://github.com/myorg/myrepo/pull/123', + ); + expect(mockContext.output).toHaveBeenCalledWith('pullRequestNumber', 123); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.ts new file mode 100644 index 0000000000..d6138b262b --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.examples.ts @@ -0,0 +1,206 @@ +/* + * 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 { TemplateExample } from '@backstage/plugin-scaffolder-node'; +import yaml from 'yaml'; + +export const examples: TemplateExample[] = [ + { + description: 'Create a pull request', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + }, + }, + ], + }), + }, + { + description: 'Create a pull request with target branch name', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest with target branch name', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + targetBranchName: 'test', + }, + }, + ], + }), + }, + { + description: 'Create a pull request as draft', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest as draft', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + draft: true, + }, + }, + ], + }), + }, + { + description: 'Create a pull request with target path', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest with target path', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + targetPath: 'targetPath', + }, + }, + ], + }), + }, + { + description: 'Create a pull request with source path', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest with source path', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + sourcePath: 'source', + }, + }, + ], + }), + }, + { + description: 'Create a pull request with token', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + token: 'gph_YourGitHubToken', + }, + }, + ], + }), + }, + { + description: 'Create a pull request with reviewers', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest with reviewers', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + reviewers: ['foobar'], + }, + }, + ], + }), + }, + { + description: 'Create a pull request with team reviewers', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest with team reviewers', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + teamReviewers: ['team-foo'], + }, + }, + ], + }), + }, + { + description: 'Create a pull request with commit message', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + commitMessage: 'Custom commit message', + }, + }, + ], + }), + }, + { + description: 'Create a pull request with all parameters', + example: yaml.stringify({ + steps: [ + { + action: 'publish:github:pull-request', + name: 'Create a pull reuqest', + input: { + repoUrl: 'github.com?repo=repo&owner=owner', + branchName: 'new-app', + title: 'Create my new app', + description: 'This PR is really good', + targetBranchName: 'test', + draft: true, + targetPath: 'targetPath', + sourcePath: 'source', + token: 'gph_YourGitHubToken', + reviewers: ['foobar'], + teamReviewers: ['team-foo'], + commitMessage: 'Commit for foo changes', + }, + }, + ], + }), + }, +]; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts index 167fedc88e..3b6e7c3ced 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -31,6 +31,7 @@ import { serializeDirectoryContents, } from '../../../../lib/files'; import { Logger } from 'winston'; +import { examples } from './githubPullRequest.examples'; export type Encoding = 'utf-8' | 'base64'; @@ -141,8 +142,10 @@ export const createPublishGithubPullRequestAction = ( reviewers?: string[]; teamReviewers?: string[]; commitMessage?: string; + update?: boolean; }>({ id: 'publish:github:pull-request', + examples, schema: { input: { required: ['repoUrl', 'title', 'description', 'branchName'], @@ -217,6 +220,11 @@ export const createPublishGithubPullRequestAction = ( title: 'Commit Message', description: 'The commit message for the pull request commit', }, + update: { + type: 'boolean', + title: 'Update', + description: 'Update pull request if already exists', + }, }, }, output: { @@ -254,6 +262,7 @@ export const createPublishGithubPullRequestAction = ( reviewers, teamReviewers, commitMessage, + update, } = ctx.input; const { owner, repo, host } = parseRepoUrl(repoUrl, integrations); @@ -325,6 +334,7 @@ export const createPublishGithubPullRequestAction = ( body: description, head: branchName, draft, + update, }; if (targetBranchName) { createOptions.base = targetBranchName; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 15f7c11eba..194120f0f9 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -363,8 +363,12 @@ export async function createRouter( const template = await authorizeTemplate(req.params, token); const parameters = [template.spec.parameters ?? []].flat(); + + const presentation = template.spec.presentation; + res.json({ title: template.metadata.title ?? template.metadata.name, + ...(presentation ? { presentation } : {}), description: template.metadata.description, 'ui:options': template.metadata['ui:options'], steps: parameters.map(schema => ({ diff --git a/plugins/scaffolder-common/CHANGELOG.md b/plugins/scaffolder-common/CHANGELOG.md index 6e81966514..3e116696f3 100644 --- a/plugins/scaffolder-common/CHANGELOG.md +++ b/plugins/scaffolder-common/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-scaffolder-common +## 1.4.3-next.0 + +### Patch Changes + +- 2e0cef42ab: Add missing required property `type` in `Template.v1beta3.schema.json` schema +- Updated dependencies + - @backstage/catalog-model@1.4.3 + - @backstage/types@1.1.1 + - @backstage/plugin-permission-common@0.7.9 + ## 1.4.2 ### Patch Changes diff --git a/plugins/scaffolder-common/api-report.md b/plugins/scaffolder-common/api-report.md index 6113c72659..8cfbd2aaac 100644 --- a/plugins/scaffolder-common/api-report.md +++ b/plugins/scaffolder-common/api-report.md @@ -66,6 +66,7 @@ export interface TemplateEntityV1beta3 extends Entity { kind: 'Template'; spec: { type: string; + presentation?: TemplatePresentationV1beta3; parameters?: TemplateParametersV1beta3 | TemplateParametersV1beta3[]; steps: Array; output?: { @@ -98,4 +99,13 @@ export interface TemplatePermissionsV1beta3 extends JsonObject { // (undocumented) tags?: string[]; } + +// @public +export interface TemplatePresentationV1beta3 extends JsonObject { + buttonLabels?: { + backButtonText?: string; + createButtonText?: string; + reviewButtonText?: string; + }; +} ``` diff --git a/plugins/scaffolder-common/package.json b/plugins/scaffolder-common/package.json index 0d9ef37999..123adc0956 100644 --- a/plugins/scaffolder-common/package.json +++ b/plugins/scaffolder-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-common", "description": "Common functionalities for the scaffolder, to be shared between scaffolder and scaffolder-backend plugin", - "version": "1.4.2", + "version": "1.4.3-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-common/src/TemplateEntityV1beta3.ts b/plugins/scaffolder-common/src/TemplateEntityV1beta3.ts index ebabb5cad4..23cc3ca3d8 100644 --- a/plugins/scaffolder-common/src/TemplateEntityV1beta3.ts +++ b/plugins/scaffolder-common/src/TemplateEntityV1beta3.ts @@ -45,6 +45,12 @@ export interface TemplateEntityV1beta3 extends Entity { * The type that the Template will create. For example service, website or library. */ type: string; + + /** + * Template specific configuration of the presentation layer. + */ + presentation?: TemplatePresentationV1beta3; + /** * This is a JSONSchema or an array of JSONSchema's which is used to render a form in the frontend * to collect user input and validate it against that schema. This can then be used in the `steps` part below to template @@ -67,6 +73,31 @@ export interface TemplateEntityV1beta3 extends Entity { }; } +/** + * The presentation of the template. + * + * @public + */ +export interface TemplatePresentationV1beta3 extends JsonObject { + /** + * Overrides default buttons' text + */ + buttonLabels?: { + /** + * The text for the button which leads to the previous template page + */ + backButtonText?: string; + /** + * The text for the button which starts the execution of the template + */ + createButtonText?: string; + /** + * The text for the button which opens template's review/summary + */ + reviewButtonText?: string; + }; +} + /** * Step that is part of a Template Entity. * diff --git a/plugins/scaffolder-common/src/index.ts b/plugins/scaffolder-common/src/index.ts index fd34fe6f41..3300aebf46 100644 --- a/plugins/scaffolder-common/src/index.ts +++ b/plugins/scaffolder-common/src/index.ts @@ -27,6 +27,7 @@ export { isTemplateEntityV1beta3, } from './TemplateEntityV1beta3'; export type { + TemplatePresentationV1beta3, TemplateEntityV1beta3, TemplateEntityStepV1beta3, TemplateParametersV1beta3, diff --git a/plugins/scaffolder-node/CHANGELOG.md b/plugins/scaffolder-node/CHANGELOG.md index 33bf8dd380..1c5a99bff8 100644 --- a/plugins/scaffolder-node/CHANGELOG.md +++ b/plugins/scaffolder-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-scaffolder-node +## 0.2.8-next.0 + +### Patch Changes + +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.2.6 ### Patch Changes diff --git a/plugins/scaffolder-node/alpha-api-report.md b/plugins/scaffolder-node/alpha-api-report.md new file mode 100644 index 0000000000..c2a2a1597e --- /dev/null +++ b/plugins/scaffolder-node/alpha-api-report.md @@ -0,0 +1,42 @@ +## API Report File for "@backstage/plugin-scaffolder-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { TaskBroker } from '@backstage/plugin-scaffolder-node'; +import { TemplateAction } from '@backstage/plugin-scaffolder-node'; +import { TemplateFilter } from '@backstage/plugin-scaffolder-node'; +import { TemplateGlobal } from '@backstage/plugin-scaffolder-node'; + +// @alpha +export interface ScaffolderActionsExtensionPoint { + // (undocumented) + addActions(...actions: TemplateAction[]): void; +} + +// @alpha +export const scaffolderActionsExtensionPoint: ExtensionPoint; + +// @alpha +export interface ScaffolderTaskBrokerExtensionPoint { + // (undocumented) + setTaskBroker(taskBroker: TaskBroker): void; +} + +// @alpha +export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint; + +// @alpha +export interface ScaffolderTemplatingExtensionPoint { + // (undocumented) + addTemplateFilters(filters: Record): void; + // (undocumented) + addTemplateGlobals(filters: Record): void; +} + +// @alpha +export const scaffolderTemplatingExtensionPoint: ExtensionPoint; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/scaffolder-node/api-report.md b/plugins/scaffolder-node/api-report.md index 52ce3c8d13..08caf36812 100644 --- a/plugins/scaffolder-node/api-report.md +++ b/plugins/scaffolder-node/api-report.md @@ -5,7 +5,6 @@ ```ts /// -import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { JsonObject } from '@backstage/types'; import { JsonValue } from '@backstage/types'; import { Logger } from 'winston'; @@ -13,11 +12,7 @@ import { Observable } from '@backstage/types'; import { Schema } from 'jsonschema'; import { ScmIntegrations } from '@backstage/integration'; import { SpawnOptionsWithoutStdio } from 'child_process'; -import { TaskBroker as TaskBroker_2 } from '@backstage/plugin-scaffolder-node'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; -import { TemplateAction as TemplateAction_2 } from '@backstage/plugin-scaffolder-node'; -import { TemplateFilter as TemplateFilter_2 } from '@backstage/plugin-scaffolder-node'; -import { TemplateGlobal as TemplateGlobal_2 } from '@backstage/plugin-scaffolder-node'; import { TemplateInfo } from '@backstage/plugin-scaffolder-common'; import { UrlReader } from '@backstage/backend-common'; import { UserEntity } from '@backstage/catalog-model'; @@ -109,35 +104,6 @@ export function fetchFile(options: { outputPath: string; }): Promise; -// @alpha -export interface ScaffolderActionsExtensionPoint { - // (undocumented) - addActions(...actions: TemplateAction_2[]): void; -} - -// @alpha -export const scaffolderActionsExtensionPoint: ExtensionPoint; - -// @alpha -export interface ScaffolderTaskBrokerExtensionPoint { - // (undocumented) - setTaskBroker(taskBroker: TaskBroker_2): void; -} - -// @alpha -export const scaffolderTaskBrokerExtensionPoint: ExtensionPoint; - -// @alpha -export interface ScaffolderTemplatingExtensionPoint { - // (undocumented) - addTemplateFilters(filters: Record): void; - // (undocumented) - addTemplateGlobals(filters: Record): void; -} - -// @alpha -export const scaffolderTemplatingExtensionPoint: ExtensionPoint; - // @public export type SerializedTask = { id: string; diff --git a/plugins/scaffolder-node/package.json b/plugins/scaffolder-node/package.json index 3194f0c0d6..bb02ce31f4 100644 --- a/plugins/scaffolder-node/package.json +++ b/plugins/scaffolder-node/package.json @@ -1,15 +1,27 @@ { "name": "@backstage/plugin-scaffolder-node", "description": "The plugin-scaffolder-node module for @backstage/plugin-scaffolder-backend", - "version": "0.2.6", + "version": "0.2.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "alphaTypes": "dist/index.alpha.d.ts", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "node-library" @@ -22,7 +34,7 @@ }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "clean": "backstage-cli package clean", diff --git a/plugins/scaffolder-node/src/extensions.ts b/plugins/scaffolder-node/src/alpha.ts similarity index 100% rename from plugins/scaffolder-node/src/extensions.ts rename to plugins/scaffolder-node/src/alpha.ts diff --git a/plugins/scaffolder-node/src/index.ts b/plugins/scaffolder-node/src/index.ts index dcce065108..98691c2afa 100644 --- a/plugins/scaffolder-node/src/index.ts +++ b/plugins/scaffolder-node/src/index.ts @@ -23,11 +23,3 @@ export * from './actions'; export * from './tasks'; export type { TemplateFilter, TemplateGlobal } from './types'; -export { - scaffolderActionsExtensionPoint, - type ScaffolderActionsExtensionPoint, - scaffolderTaskBrokerExtensionPoint, - type ScaffolderTaskBrokerExtensionPoint, - scaffolderTemplatingExtensionPoint, - type ScaffolderTemplatingExtensionPoint, -} from './extensions'; diff --git a/plugins/scaffolder-react/CHANGELOG.md b/plugins/scaffolder-react/CHANGELOG.md index 2aed192d7d..ed16201173 100644 --- a/plugins/scaffolder-react/CHANGELOG.md +++ b/plugins/scaffolder-react/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-scaffolder-react +## 1.6.0-next.0 + +### Minor Changes + +- 3fdffbb699: Release design improvements for the `Scaffolder` plugin and support v5 of `@rjsf/*` libraries. + + This change should be non-breaking. If you're seeing typescript issues after migrating please [open an issue](https://github.com/backstage/backstage/issues/new/choose) + + The `next` versions like `createNextFieldExtension` and `NextScaffolderPage` have been promoted to the public interface under `createScaffolderFieldExtension` and `ScaffolderPage`, so any older imports which are no longer found will need updating from `@backstage/plugin-scaffolder/alpha` or `@backstage/plugin-scaffolder-react/alpha` will need to be imported from `@backstage/plugin-scaffolder` and `@backstage/plugin-scaffolder-react` respectively. + + The legacy versions are now available in `/alpha` under `createLegacyFieldExtension` and `LegacyScaffolderPage` if you're running into issues, but be aware that these will be removed in a next mainline release. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 1.5.6 ### Patch Changes diff --git a/plugins/scaffolder-react/alpha-api-report.md b/plugins/scaffolder-react/alpha-api-report.md index a31d9b1d60..3d9361bf18 100644 --- a/plugins/scaffolder-react/alpha-api-report.md +++ b/plugins/scaffolder-react/alpha-api-report.md @@ -33,6 +33,7 @@ import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplateGroupFilter } from '@backstage/plugin-scaffolder-react'; import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; +import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common'; import { UiSchema } from '@rjsf/utils'; // @alpha (undocumented) @@ -189,6 +190,7 @@ export type StepperProps = { components?: { ReviewStepComponent?: ComponentType; ReviewStateComponent?: (props: ReviewStateProps) => JSX.Element; + backButtonText?: ReactNode; createButtonText?: ReactNode; reviewButtonText?: ReactNode; }; @@ -303,6 +305,7 @@ export const useTemplateParameterSchema: (templateRef: string) => { // @alpha export const useTemplateSchema: (manifest: TemplateParameterSchema) => { steps: ParsedTemplateSchema[]; + presentation?: TemplatePresentationV1beta3 | undefined; }; // @alpha (undocumented) diff --git a/plugins/scaffolder-react/api-report.md b/plugins/scaffolder-react/api-report.md index 6acc2f9812..8589078f1e 100644 --- a/plugins/scaffolder-react/api-report.md +++ b/plugins/scaffolder-react/api-report.md @@ -39,6 +39,7 @@ import { StrictRJSFSchema } from '@rjsf/utils'; import { TaskSpec } from '@backstage/plugin-scaffolder-common'; import { TaskStep } from '@backstage/plugin-scaffolder-common'; import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; +import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common'; import { TemplatesType } from '@rjsf/utils'; import { UIOptionsType } from '@rjsf/utils'; import { UiSchema } from '@rjsf/utils'; @@ -482,6 +483,7 @@ export type TemplateGroupFilter = { export type TemplateParameterSchema = { title: string; description?: string; + presentation?: TemplatePresentationV1beta3; steps: Array<{ title: string; description?: string; diff --git a/plugins/scaffolder-react/package.json b/plugins/scaffolder-react/package.json index 5f29f96946..f53c643710 100644 --- a/plugins/scaffolder-react/package.json +++ b/plugins/scaffolder-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder-react", "description": "A frontend library that helps other Backstage plugins interact with the Scaffolder", - "version": "1.5.6", + "version": "1.6.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder-react/src/next/components/Form/Form.tsx b/plugins/scaffolder-react/src/next/components/Form/Form.tsx index 85e75e2c0d..8717c8b55a 100644 --- a/plugins/scaffolder-react/src/next/components/Form/Form.tsx +++ b/plugins/scaffolder-react/src/next/components/Form/Form.tsx @@ -32,27 +32,34 @@ const WrappedForm = withTheme(MuiTheme); export const Form = (props: PropsWithChildren) => { // This is where we unbreak the changes from RJSF, and make it work with our custom fields so we don't pass on this // breaking change to our users. We will look more into a better API for this in scaffolderv2. - const wrappedFields = Object.fromEntries( - Object.entries(props.fields ?? {}).map(([key, Component]) => [ - key, - (wrapperProps: FieldProps) => { - return ( - - ); - }, - ]), + const wrappedFields = React.useMemo( + () => + Object.fromEntries( + Object.entries(props.fields ?? {}).map(([key, Component]) => [ + key, + (wrapperProps: FieldProps) => { + return ( + + ); + }, + ]), + ), + [props.fields], ); - const templates = { - FieldTemplate, - DescriptionFieldTemplate, - ...props.templates, - }; + const templates = React.useMemo( + () => ({ + FieldTemplate, + DescriptionFieldTemplate, + ...props.templates, + }), + [props.templates], + ); return ( diff --git a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx index 7ccbe4ef8d..8326b361e1 100644 --- a/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx +++ b/plugins/scaffolder-react/src/next/components/ScaffolderPageContextMenu/ScaffolderPageContextMenu.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; import IconButton from '@material-ui/core/IconButton'; import ListItemIcon from '@material-ui/core/ListItemIcon'; import ListItemText from '@material-ui/core/ListItemText'; @@ -28,7 +27,7 @@ import List from '@material-ui/icons/List'; import MoreVert from '@material-ui/icons/MoreVert'; import React, { useState } from 'react'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ button: { color: theme.page.fontColor, }, diff --git a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx index 1e43eba770..9a333944b9 100644 --- a/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx +++ b/plugins/scaffolder-react/src/next/components/Stepper/Stepper.tsx @@ -81,6 +81,7 @@ export type StepperProps = { components?: { ReviewStepComponent?: ComponentType; ReviewStateComponent?: (props: ReviewStateProps) => JSX.Element; + backButtonText?: ReactNode; createButtonText?: ReactNode; reviewButtonText?: ReactNode; }; @@ -96,11 +97,12 @@ export const Stepper = (stepperProps: StepperProps) => { const { ReviewStateComponent = ReviewState, ReviewStepComponent, + backButtonText = 'Back', createButtonText = 'Create', reviewButtonText = 'Review', } = components; const analytics = useAnalytics(); - const { steps } = useTemplateSchema(props.manifest); + const { presentation, steps } = useTemplateSchema(props.manifest); const apiHolder = useApiHolder(); const [activeStep, setActiveStep] = useState(0); const [isValidating, setIsValidating] = useState(false); @@ -115,6 +117,11 @@ export const Stepper = (stepperProps: StepperProps) => { ); }, [props.extensions]); + const fields = useMemo( + () => ({ ...FieldOverrides, ...extensions }), + [extensions], + ); + const validators = useMemo(() => { return Object.fromEntries( props.extensions.map(({ name, validation }) => [name, validation]), @@ -173,6 +180,13 @@ export const Stepper = (stepperProps: StepperProps) => { setFormState(current => ({ ...current, ...formData })); }; + const backLabel = + presentation?.buttonLabels?.backButtonText ?? backButtonText; + const createLabel = + presentation?.buttonLabels?.createButtonText ?? createButtonText; + const reviewLabel = + presentation?.buttonLabels?.reviewButtonText ?? reviewButtonText; + return ( <> {isValidating && } @@ -197,7 +211,7 @@ export const Stepper = (stepperProps: StepperProps) => { schema={currentStep.schema} uiSchema={currentStep.uiSchema} onSubmit={handleNext} - fields={{ ...FieldOverrides, ...extensions }} + fields={fields} showErrorList={false} onChange={handleChange} {...(props.formProps ?? {})} @@ -208,7 +222,7 @@ export const Stepper = (stepperProps: StepperProps) => { className={styles.backButton} disabled={activeStep < 1 || isValidating} > - Back + {backLabel} @@ -246,7 +260,7 @@ export const Stepper = (stepperProps: StepperProps) => { color="primary" onClick={handleCreate} > - {createButtonText} + {createLabel} diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx index cecf682596..58f2eb9050 100644 --- a/plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/StepIcon.tsx @@ -13,9 +13,8 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { BackstageTheme } from '@backstage/theme'; +import React from 'react'; import { CircularProgress, makeStyles, StepIconProps } from '@material-ui/core'; import RemoveCircleOutline from '@material-ui/icons/RemoveCircleOutline'; import PanoramaFishEyeIcon from '@material-ui/icons/PanoramaFishEye'; @@ -23,7 +22,7 @@ import classNames from 'classnames'; import CheckCircleOutline from '@material-ui/icons/CheckCircleOutline'; import ErrorOutline from '@material-ui/icons/ErrorOutline'; -const useStepIconStyles = makeStyles((theme: BackstageTheme) => ({ +const useStepIconStyles = makeStyles(theme => ({ root: { color: theme.palette.text.disabled, }, diff --git a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx index 2d8e99bfe7..d7a96e3864 100644 --- a/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx +++ b/plugins/scaffolder-react/src/next/components/TaskSteps/TaskBorder.tsx @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React from 'react'; import { LinearProgress, makeStyles } from '@material-ui/core'; -import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ failed: { backgroundColor: theme.palette.error.main, }, diff --git a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx index 2d5b203cfb..5c373411fa 100644 --- a/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx +++ b/plugins/scaffolder-react/src/next/components/TemplateCard/TemplateCard.test.tsx @@ -89,7 +89,7 @@ describe('TemplateCard', () => { expect(description.querySelector('strong')).toBeInTheDocument(); }); - it('should render no descroption if none is provided through the template', async () => { + it('should render no description if none is provided through the template', async () => { const mockTemplate: TemplateEntityV1beta3 = { apiVersion: 'scaffolder.backstage.io/v1beta3', kind: 'Template', @@ -186,11 +186,12 @@ describe('TemplateCard', () => { }, ); - expect(getByRole('link', { name: 'my-test-user' })).toBeInTheDocument(); - expect(getByRole('link', { name: 'my-test-user' })).toHaveAttribute( - 'href', - '/catalog/group/default/my-test-user', - ); + expect( + getByRole('link', { name: 'group:default/my-test-user' }), + ).toBeInTheDocument(); + expect( + getByRole('link', { name: 'group:default/my-test-user' }), + ).toHaveAttribute('href', '/catalog/group/default/my-test-user'); }); it('should call the onSelected handler when clicking the choose button', async () => { diff --git a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.ts b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.ts index df70df965c..838f8b1c30 100644 --- a/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.ts +++ b/plugins/scaffolder-react/src/next/hooks/useTemplateSchema.ts @@ -14,6 +14,7 @@ * limitations under the License. */ import { featureFlagsApiRef, useApi } from '@backstage/core-plugin-api'; +import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common'; import { JsonObject } from '@backstage/types'; import { UiSchema } from '@rjsf/utils'; import { TemplateParameterSchema } from '@backstage/plugin-scaffolder-react'; @@ -39,7 +40,10 @@ export interface ParsedTemplateSchema { */ export const useTemplateSchema = ( manifest: TemplateParameterSchema, -): { steps: ParsedTemplateSchema[] } => { +): { + steps: ParsedTemplateSchema[]; + presentation?: TemplatePresentationV1beta3; +} => { const featureFlags = useApi(featureFlagsApiRef); const steps = manifest.steps.map(({ title, description, schema }) => ({ title, @@ -76,6 +80,7 @@ export const useTemplateSchema = ( })); return { + presentation: manifest.presentation, steps: returningSteps, }; }; diff --git a/plugins/scaffolder-react/src/types.ts b/plugins/scaffolder-react/src/types.ts index f644fbeb64..c1c26f32bb 100644 --- a/plugins/scaffolder-react/src/types.ts +++ b/plugins/scaffolder-react/src/types.ts @@ -16,6 +16,8 @@ import { JsonObject } from '@backstage/types'; +import { TemplatePresentationV1beta3 } from '@backstage/plugin-scaffolder-common'; + /** * The shape of each entry of parameters which gets rendered * as a separate step in the wizard input @@ -25,6 +27,7 @@ import { JsonObject } from '@backstage/types'; export type TemplateParameterSchema = { title: string; description?: string; + presentation?: TemplatePresentationV1beta3; steps: Array<{ title: string; description?: string; diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 2f681837ea..8ee2a6638c 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,37 @@ # @backstage/plugin-scaffolder +## 1.16.0-next.0 + +### Minor Changes + +- 3fdffbb699: Release design improvements for the `Scaffolder` plugin and support v5 of `@rjsf/*` libraries. + + This change should be non-breaking. If you're seeing typescript issues after migrating please [open an issue](https://github.com/backstage/backstage/issues/new/choose) + + The `next` versions like `createNextFieldExtension` and `NextScaffolderPage` have been promoted to the public interface under `createScaffolderFieldExtension` and `ScaffolderPage`, so any older imports which are no longer found will need updating from `@backstage/plugin-scaffolder/alpha` or `@backstage/plugin-scaffolder-react/alpha` will need to be imported from `@backstage/plugin-scaffolder` and `@backstage/plugin-scaffolder-react` respectively. + + The legacy versions are now available in `/alpha` under `createLegacyFieldExtension` and `LegacyScaffolderPage` if you're running into issues, but be aware that these will be removed in a next mainline release. + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-scaffolder-common@1.4.3-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-permission-react@0.4.17-next.0 + - @backstage/plugin-scaffolder-react@1.6.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + ## 1.15.1 ### Patch Changes diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index d36ce86acc..e5a29ae563 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-scaffolder", "description": "The Backstage plugin that helps you create new things", - "version": "1.15.1", + "version": "1.16.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx index da86e6e4b8..fb0b478556 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.test.tsx @@ -132,6 +132,38 @@ describe('RepoUrlPicker', () => { getByRole('option', { name: 'dev.azure.com' }), ).toBeInTheDocument(); }); + + it('should render properly with title and description', async () => { + const { getByText } = await renderInTestApp( + + +
, + }} + /> + + , + ); + + expect(getByText('test title')).toBeInTheDocument(); + expect(getByText('test description')).toBeInTheDocument(); + }); }); describe('requestUserCredentials', () => { diff --git a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx index 57cebb3d96..7fc71d8338 100644 --- a/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx +++ b/plugins/scaffolder/src/components/fields/RepoUrlPicker/RepoUrlPicker.tsx @@ -31,6 +31,7 @@ import { RepoUrlPickerProps } from './schema'; import { RepoUrlPickerState } from './types'; import useDebounce from 'react-use/lib/useDebounce'; import { useTemplateSecrets } from '@backstage/plugin-scaffolder-react'; +import { Box, Divider, Typography } from '@material-ui/core'; export { RepoUrlPickerSchema } from './schema'; @@ -41,7 +42,7 @@ export { RepoUrlPickerSchema } from './schema'; * @public */ export const RepoUrlPicker = (props: RepoUrlPickerProps) => { - const { uiSchema, onChange, rawErrors, formData } = props; + const { uiSchema, onChange, rawErrors, formData, schema } = props; const [state, setState] = useState( parseRepoPickerUrl(formData), ); @@ -157,9 +158,17 @@ export const RepoUrlPicker = (props: RepoUrlPickerProps) => { const hostType = (state.host && integrationApi.byHost(state.host)?.type) ?? null; - return ( <> + {schema.title && ( + + {schema.title} + + + )} + {schema.description && ( + {schema.description} + )} createStyles({ @@ -127,7 +124,7 @@ const StepTimeTicker = ({ step }: { step: TaskStep }) => { return {time}; }; -const useStepIconStyles = makeStyles((theme: BackstageTheme) => +const useStepIconStyles = makeStyles(theme => createStyles({ root: { color: theme.palette.text.disabled, diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResults.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResults.tsx index ae9374438f..9319f5e836 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResults.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResults.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; import Accordion from '@material-ui/core/Accordion'; import AccordionDetails from '@material-ui/core/AccordionDetails'; import AccordionSummary from '@material-ui/core/AccordionSummary'; @@ -28,7 +27,7 @@ import { useDryRun } from '../DryRunContext'; import { DryRunResultsList } from './DryRunResultsList'; import { DryRunResultsView } from './DryRunResultsView'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ header: { height: 48, minHeight: 0, diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx index df6e8ef5f2..38cc3f1762 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsList.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; import IconButton from '@material-ui/core/IconButton'; import List from '@material-ui/core/List'; import ListItem from '@material-ui/core/ListItem'; @@ -30,7 +29,7 @@ import React from 'react'; import { useDryRun } from '../DryRunContext'; import { downloadBlob } from '../../../lib/download'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ root: { overflowY: 'auto', background: theme.palette.background.default, diff --git a/plugins/search-backend-module-catalog/CHANGELOG.md b/plugins/search-backend-module-catalog/CHANGELOG.md index c65b3a119c..78f60eae4c 100644 --- a/plugins/search-backend-module-catalog/CHANGELOG.md +++ b/plugins/search-backend-module-catalog/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-backend-module-catalog +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + ## 0.1.10 ### Patch Changes diff --git a/plugins/search-backend-module-catalog/package.json b/plugins/search-backend-module-catalog/package.json index 4bcdf8f53b..41face5383 100644 --- a/plugins/search-backend-module-catalog/package.json +++ b/plugins/search-backend-module-catalog/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-catalog", "description": "A module for the search backend that exports catalog modules", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-elasticsearch/CHANGELOG.md b/plugins/search-backend-module-elasticsearch/CHANGELOG.md index 94727e88a2..47bc95fe4b 100644 --- a/plugins/search-backend-module-elasticsearch/CHANGELOG.md +++ b/plugins/search-backend-module-elasticsearch/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-search-backend-module-elasticsearch +## 1.3.10-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration-aws-node@0.1.7 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 1.3.9 ### Patch Changes diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index ef15b13290..838f226387 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-elasticsearch", "description": "A module for the search backend that implements search using ElasticSearch", - "version": "1.3.9", + "version": "1.3.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-explore/CHANGELOG.md b/plugins/search-backend-module-explore/CHANGELOG.md index f4b64f07c8..ddf571771e 100644 --- a/plugins/search-backend-module-explore/CHANGELOG.md +++ b/plugins/search-backend-module-explore/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-search-backend-module-explore +## 0.1.11-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-explore-common@0.0.2 + - @backstage/plugin-search-common@1.2.7 + ## 0.1.10 ### Patch Changes diff --git a/plugins/search-backend-module-explore/package.json b/plugins/search-backend-module-explore/package.json index 41a390d773..9fedd97985 100644 --- a/plugins/search-backend-module-explore/package.json +++ b/plugins/search-backend-module-explore/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-explore", "description": "A module for the search backend that exports explore modules", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-pg/CHANGELOG.md b/plugins/search-backend-module-pg/CHANGELOG.md index 3f8d5adeb7..fb14cb5beb 100644 --- a/plugins/search-backend-module-pg/CHANGELOG.md +++ b/plugins/search-backend-module-pg/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-search-backend-module-pg +## 0.5.16-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 0.5.15 ### Patch Changes diff --git a/plugins/search-backend-module-pg/package.json b/plugins/search-backend-module-pg/package.json index 2bdea9b391..774429b2a6 100644 --- a/plugins/search-backend-module-pg/package.json +++ b/plugins/search-backend-module-pg/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-pg", "description": "A module for the search backend that implements search using PostgreSQL", - "version": "0.5.15", + "version": "0.5.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md new file mode 100644 index 0000000000..482f74f31f --- /dev/null +++ b/plugins/search-backend-module-stack-overflow-collator/CHANGELOG.md @@ -0,0 +1,17 @@ +# @backstage/plugin-search-backend-module-stack-overflow-collator + +## 0.1.0-next.0 + +### Minor Changes + +- 46f0f1700e: Extract a package for the Stack Overflow new backend system plugin. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 diff --git a/plugins/search-backend-module-stack-overflow-collator/package.json b/plugins/search-backend-module-stack-overflow-collator/package.json index 14e547de8e..cc531219fa 100644 --- a/plugins/search-backend-module-stack-overflow-collator/package.json +++ b/plugins/search-backend-module-stack-overflow-collator/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-stack-overflow-collator", "description": "A module for the search backend that exports stack overflow modules", - "version": "0.0.0", + "version": "0.1.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-techdocs/CHANGELOG.md b/plugins/search-backend-module-techdocs/CHANGELOG.md index fa8997a990..0ede11b81c 100644 --- a/plugins/search-backend-module-techdocs/CHANGELOG.md +++ b/plugins/search-backend-module-techdocs/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-search-backend-module-techdocs +## 0.1.11-next.0 + +### Patch Changes + +- c437253b7a: The process of adding or modifying fields in the techdocs search index has been simplified. For more details, see [How to customize fields in the Software Catalog or TechDocs index](https://backstage.io/docs/features/search/how-to-guides.md#how-to-customize-fields-in-the-software-catalog-or-techdocs-index). +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-catalog-node@1.4.8-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + ## 0.1.10 ### Patch Changes diff --git a/plugins/search-backend-module-techdocs/alpha-api-report.md b/plugins/search-backend-module-techdocs/alpha-api-report.md index 997d4a68db..df98623631 100644 --- a/plugins/search-backend-module-techdocs/alpha-api-report.md +++ b/plugins/search-backend-module-techdocs/alpha-api-report.md @@ -4,10 +4,21 @@ ```ts import { BackendFeature } from '@backstage/backend-plugin-api'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { TechDocsCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-techdocs'; // @alpha const _default: () => BackendFeature; export default _default; +// @alpha (undocumented) +export interface TechDocsCollatorEntityTransformerExtensionPoint { + // (undocumented) + setTransformer(transformer: TechDocsCollatorEntityTransformer): void; +} + +// @alpha +export const techdocsCollatorEntityTransformerExtensionPoint: ExtensionPoint; + // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/search-backend-module-techdocs/api-report.md b/plugins/search-backend-module-techdocs/api-report.md index 9f72b1c487..592cf5065a 100644 --- a/plugins/search-backend-module-techdocs/api-report.md +++ b/plugins/search-backend-module-techdocs/api-report.md @@ -8,12 +8,17 @@ import { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { DocumentCollatorFactory } from '@backstage/plugin-search-common'; +import { Entity } from '@backstage/catalog-model'; import { Logger } from 'winston'; import { Permission } from '@backstage/plugin-permission-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { Readable } from 'stream'; +import { TechDocsDocument } from '@backstage/plugin-techdocs-node'; import { TokenManager } from '@backstage/backend-common'; +// @public (undocumented) +export const defaultTechDocsCollatorEntityTransformer: TechDocsCollatorEntityTransformer; + // @public export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { // (undocumented) @@ -29,6 +34,11 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { readonly visibilityPermission: Permission; } +// @public (undocumented) +export type TechDocsCollatorEntityTransformer = ( + entity: Entity, +) => Omit; + // @public export type TechDocsCollatorFactoryOptions = { discovery: PluginEndpointDiscovery; @@ -38,5 +48,6 @@ export type TechDocsCollatorFactoryOptions = { catalogClient?: CatalogApi; parallelismLimit?: number; legacyPathCasing?: boolean; + entityTransformer?: TechDocsCollatorEntityTransformer; }; ``` diff --git a/plugins/search-backend-module-techdocs/package.json b/plugins/search-backend-module-techdocs/package.json index aa888a3d8a..9be4fa5246 100644 --- a/plugins/search-backend-module-techdocs/package.json +++ b/plugins/search-backend-module-techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-module-techdocs", "description": "A module for the search backend that exports techdocs modules", - "version": "0.1.10", + "version": "0.1.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend-module-techdocs/src/alpha.ts b/plugins/search-backend-module-techdocs/src/alpha.ts index 1c4988cc21..950acd4039 100644 --- a/plugins/search-backend-module-techdocs/src/alpha.ts +++ b/plugins/search-backend-module-techdocs/src/alpha.ts @@ -23,11 +23,28 @@ import { loggerToWinstonLogger } from '@backstage/backend-common'; import { coreServices, createBackendModule, + createExtensionPoint, } from '@backstage/backend-plugin-api'; import { readTaskScheduleDefinitionFromConfig } from '@backstage/backend-tasks'; import { catalogServiceRef } from '@backstage/plugin-catalog-node/alpha'; import { DefaultTechDocsCollatorFactory } from '@backstage/plugin-search-backend-module-techdocs'; import { searchIndexRegistryExtensionPoint } from '@backstage/plugin-search-backend-node/alpha'; +import { TechDocsCollatorEntityTransformer } from '@backstage/plugin-search-backend-module-techdocs'; + +/** @alpha */ +export interface TechDocsCollatorEntityTransformerExtensionPoint { + setTransformer(transformer: TechDocsCollatorEntityTransformer): void; +} + +/** + * Extension point used to customize the TechDocs collator entity transformer. + * + * @alpha + */ +export const techdocsCollatorEntityTransformerExtensionPoint = + createExtensionPoint({ + id: 'search.techdocsCollator.transformer', + }); /** * @alpha @@ -37,6 +54,22 @@ export default createBackendModule({ moduleId: 'techDocsCollator', pluginId: 'search', register(env) { + let transformer: TechDocsCollatorEntityTransformer | undefined; + + env.registerExtensionPoint( + techdocsCollatorEntityTransformerExtensionPoint, + { + setTransformer(newTransformer) { + if (transformer) { + throw new Error( + 'TechDocs collator entity transformer may only be set once', + ); + } + transformer = newTransformer; + }, + }, + ); + env.registerInit({ deps: { config: coreServices.rootConfig, @@ -75,6 +108,7 @@ export default createBackendModule({ tokenManager, logger: loggerToWinstonLogger(logger), catalogClient: catalog, + entityTransformer: transformer, }), }); }, diff --git a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts index 319b8fb446..8f56a92096 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.test.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Backstage Authors + * 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. @@ -26,6 +26,8 @@ import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { Readable } from 'stream'; import { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory'; +import { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; +import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer'; const logger = getVoidLogger(); @@ -66,6 +68,7 @@ const expectedEntities: Entity[] = [ annotations: { 'backstage.io/techdocs-ref': './', }, + tags: ['tag1', 'tag2'], }, spec: { type: 'dog', @@ -256,5 +259,42 @@ describe('DefaultTechDocsCollatorFactory', () => { }); }); }); + + it('should transform the entity using the entityTransformer function', async () => { + const entityTransformer: TechDocsCollatorEntityTransformer = ( + entity: Entity, + ) => { + return { + ...defaultTechDocsCollatorEntityTransformer(entity), + tags: entity.metadata.tags, + }; + }; + + factory = DefaultTechDocsCollatorFactory.fromConfig(config, { + ...options, + entityTransformer, + }); + + collator = await factory.getCollator(); + + const pipeline = TestPipeline.fromCollator(collator); + const { documents } = await pipeline.execute(); + const entity = expectedEntities[0]; + documents.forEach((document, idx) => { + expect(document).toMatchObject({ + title: mockSearchDocIndex.docs[idx].title, + location: `/docs/default/component/${entity.metadata.name}/${mockSearchDocIndex.docs[idx].location}`, + text: mockSearchDocIndex.docs[idx].text, + namespace: 'default', + entityTitle: entity!.metadata.title, + componentType: entity!.spec!.type, + lifecycle: entity!.spec!.lifecycle, + owner: '', + kind: entity.kind.toLocaleLowerCase('en-US'), + name: entity.metadata.name, + tags: entity.metadata.tags, + }); + }); + }); }); }); diff --git a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts index 051dc93189..b76fedbdf9 100644 --- a/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts +++ b/plugins/search-backend-module-techdocs/src/collators/DefaultTechDocsCollatorFactory.ts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Backstage Authors + * 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. @@ -39,6 +39,8 @@ import fetch from 'node-fetch'; import pLimit from 'p-limit'; import { Readable } from 'stream'; import { Logger } from 'winston'; +import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer'; +import { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; interface MkSearchIndexDoc { title: string; @@ -59,6 +61,7 @@ export type TechDocsCollatorFactoryOptions = { catalogClient?: CatalogApi; parallelismLimit?: number; legacyPathCasing?: boolean; + entityTransformer?: TechDocsCollatorEntityTransformer; }; type EntityInfo = { @@ -85,6 +88,7 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { private readonly tokenManager: TokenManager; private readonly parallelismLimit: number; private readonly legacyPathCasing: boolean; + private entityTransformer: TechDocsCollatorEntityTransformer; private constructor(options: TechDocsCollatorFactoryOptions) { this.discovery = options.discovery; @@ -97,6 +101,8 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { this.parallelismLimit = options.parallelismLimit ?? 10; this.legacyPathCasing = options.legacyPathCasing ?? false; this.tokenManager = options.tokenManager; + this.entityTransformer = + options.entityTransformer ?? defaultTechDocsCollatorEntityTransformer; } static fromConfig(config: Config, options: TechDocsCollatorFactoryOptions) { @@ -142,17 +148,6 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { 'metadata.annotations.backstage.io/techdocs-ref': CATALOG_FILTER_EXISTS, }, - fields: [ - 'kind', - 'namespace', - 'metadata.annotations', - 'metadata.name', - 'metadata.title', - 'metadata.namespace', - 'spec.type', - 'spec.lifecycle', - 'relations', - ], limit: batchSize, offset: entitiesRetrieved, }, @@ -204,6 +199,7 @@ export class DefaultTechDocsCollatorFactory implements DocumentCollatorFactory { ]); return searchIndex.docs.map((doc: MkSearchIndexDoc) => ({ + ...this.entityTransformer(entity), title: unescape(doc.title), text: unescape(doc.text || ''), location: this.applyArgsToFormat( diff --git a/plugins/search-backend-module-techdocs/src/collators/TechDocsCollatorEntityTransformer.ts b/plugins/search-backend-module-techdocs/src/collators/TechDocsCollatorEntityTransformer.ts new file mode 100644 index 0000000000..142199e3dc --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/collators/TechDocsCollatorEntityTransformer.ts @@ -0,0 +1,23 @@ +/* + * 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 { Entity } from '@backstage/catalog-model'; +import { TechDocsDocument } from '@backstage/plugin-techdocs-node'; + +/** @public */ +export type TechDocsCollatorEntityTransformer = ( + entity: Entity, +) => Omit; diff --git a/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.test.ts b/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.test.ts new file mode 100644 index 0000000000..fe8f61dbe8 --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.test.ts @@ -0,0 +1,54 @@ +/* + * Copyright 2022 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Entity } from '@backstage/catalog-model'; +import { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; + +describe('defaultTechDocsCollatorEntityTransformer', () => { + it('should transform the entity with the correct properties', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + name: 'test-component', + description: 'A test component', + annotations: { + 'backstage.io/techdocs-ref': 'docs', + }, + }, + spec: { + type: 'service', + owner: 'test@example.com', + }, + }; + + const transformedEntity = defaultTechDocsCollatorEntityTransformer(entity); + + expect(transformedEntity).toEqual({ + kind: entity.kind, + namespace: entity.metadata.namespace || 'default', + annotations: entity.metadata.annotations || '', + name: entity.metadata.name || '', + title: entity.metadata.title || '', + text: 'A test component', + componentType: entity.spec?.type?.toString() || 'other', + type: entity.spec?.type?.toString() || 'other', + lifecycle: (entity.spec?.lifecycle as string) || '', + owner: (entity.spec?.owner as string) || '', + path: '', + }); + }); +}); diff --git a/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.ts b/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.ts new file mode 100644 index 0000000000..cc0b16b2fa --- /dev/null +++ b/plugins/search-backend-module-techdocs/src/collators/defaultTechDocsCollatorEntityTransformer.ts @@ -0,0 +1,55 @@ +/* + * 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 { Entity, isGroupEntity, isUserEntity } from '@backstage/catalog-model'; +import { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer'; + +const getDocumentText = (entity: Entity): string => { + const documentTexts: string[] = []; + documentTexts.push(entity.metadata.description || ''); + + if (isUserEntity(entity) || isGroupEntity(entity)) { + if (entity.spec?.profile?.displayName) { + documentTexts.push(entity.spec.profile.displayName); + } + } + + if (isUserEntity(entity)) { + if (entity.spec?.profile?.email) { + documentTexts.push(entity.spec.profile.email); + } + } + + return documentTexts.join(' : '); +}; + +/** @public */ +export const defaultTechDocsCollatorEntityTransformer: TechDocsCollatorEntityTransformer = + (entity: Entity) => { + return { + kind: entity.kind, + namespace: entity.metadata.namespace || 'default', + annotations: entity.metadata.annotations || '', + name: entity.metadata.name || '', + title: entity.metadata.title || '', + text: getDocumentText(entity), + componentType: entity.spec?.type?.toString() || 'other', + type: entity.spec?.type?.toString() || 'other', + lifecycle: (entity.spec?.lifecycle as string) || '', + owner: (entity.spec?.owner as string) || '', + path: '', + }; + }; diff --git a/plugins/search-backend-module-techdocs/src/collators/index.ts b/plugins/search-backend-module-techdocs/src/collators/index.ts index 8c5fd122f1..94be458e33 100644 --- a/plugins/search-backend-module-techdocs/src/collators/index.ts +++ b/plugins/search-backend-module-techdocs/src/collators/index.ts @@ -17,3 +17,7 @@ export { DefaultTechDocsCollatorFactory } from './DefaultTechDocsCollatorFactory'; export type { TechDocsCollatorFactoryOptions } from './DefaultTechDocsCollatorFactory'; + +export { defaultTechDocsCollatorEntityTransformer } from './defaultTechDocsCollatorEntityTransformer'; + +export type { TechDocsCollatorEntityTransformer } from './TechDocsCollatorEntityTransformer'; diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index c2767e6463..b6fc82c798 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-search-backend-node +## 1.2.11-next.0 + +### Patch Changes + +- b168d7e7ea: Fix highlighting for non-string fields on the `Lunr` search engine implementation. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + ## 1.2.10 ### Patch Changes diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index 78d693466d..e0f07c15fb 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend-node", "description": "A library for Backstage backend plugins that want to interact with the search backend plugin", - "version": "1.2.10", + "version": "1.2.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 94f8c5d605..69305a8a7f 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-backend +## 1.4.7-next.0 + +### Patch Changes + +- 6694b369a3: Update the OpenAPI spec with more complete error responses and request bodies using Optic. Also, updates the test cases to use the new `supertest` pass through from `@backstage/backend-openapi-utils`. +- Updated dependencies + - @backstage/plugin-search-backend-node@1.2.11-next.0 + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-permission-node@0.7.18-next.0 + - @backstage/plugin-search-common@1.2.7 + ## 1.4.6 ### Patch Changes diff --git a/plugins/search-backend/optic.yml b/plugins/search-backend/optic.yml new file mode 100644 index 0000000000..8f38d46f13 --- /dev/null +++ b/plugins/search-backend/optic.yml @@ -0,0 +1,15 @@ +ruleset: + - breaking-changes +capture: + src/schema/openapi.yaml: + # 🔧 Runnable example with simple get requests. + # Run with "PORT=3000 optic capture src/schema/openapi.yaml --update interactive" in 'plugins/search-backend' + # You can change the server and the 'requests' section to experiment + server: + # This will not be used by 'backstage-repo-tools schema openapi test', but may be useful for interactive updates. + url: http://localhost:3000 + requests: + # â„šī¸ Requests should be sent to the Optic proxy, the address of which is injected into 'run.command's env as OPTIC_PROXY (or the value of 'run.proxy_variable'). + run: + # 🔧 Specify a command that will generate traffic + command: yarn backstage-cli package test --no-watch src/service/router.test.ts src/service/createRouter.test.ts diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index cd3c16efe2..84da308624 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search-backend", "description": "The Backstage backend plugin that provides your backstage app with search", - "version": "1.4.6", + "version": "1.4.7-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-backend/src/schema/openapi.generated.ts b/plugins/search-backend/src/schema/openapi.generated.ts index 9bf2e08d3e..2586b7a5bf 100644 --- a/plugins/search-backend/src/schema/openapi.generated.ts +++ b/plugins/search-backend/src/schema/openapi.generated.ts @@ -42,8 +42,58 @@ export const spec = { headers: {}, parameters: {}, requestBodies: {}, - responses: {}, + responses: { + ErrorResponse: { + description: 'An error response from the backend.', + content: { + 'application/json; charset=utf-8': { + schema: { + $ref: '#/components/schemas/Error', + }, + }, + }, + }, + }, schemas: { + Error: { + type: 'object', + properties: { + error: { + type: 'object', + properties: { + name: { + type: 'string', + }, + message: { + type: 'string', + }, + }, + required: ['name', 'message'], + }, + request: { + type: 'object', + properties: { + method: { + type: 'string', + }, + url: { + type: 'string', + }, + }, + required: ['method', 'url'], + }, + response: { + type: 'object', + properties: { + statusCode: { + type: 'number', + }, + }, + required: ['statusCode'], + }, + }, + required: ['error', 'request', 'response'], + }, JsonObject: { type: 'object', properties: {}, @@ -132,25 +182,8 @@ export const spec = { }, }, }, - '400': { - description: 'Bad request', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - error: { - type: 'object', - properties: { - message: { - type: 'string', - }, - }, - }, - }, - }, - }, - }, + default: { + $ref: '#/components/responses/ErrorResponse', }, }, security: [ diff --git a/plugins/search-backend/src/schema/openapi.yaml b/plugins/search-backend/src/schema/openapi.yaml index 4684ca58d8..79bb84dfe0 100644 --- a/plugins/search-backend/src/schema/openapi.yaml +++ b/plugins/search-backend/src/schema/openapi.yaml @@ -1,5 +1,4 @@ openapi: 3.0.3 - info: title: '@backstage/plugin-search-backend' version: '1' @@ -8,17 +7,56 @@ info: name: Apache-2.0 url: http://www.apache.org/licenses/LICENSE-2.0.html contact: {} - servers: - url: / - components: examples: {} headers: {} parameters: {} requestBodies: {} - responses: {} + responses: + ErrorResponse: + description: An error response from the backend. + content: + application/json; charset=utf-8: + schema: + $ref: '#/components/schemas/Error' schemas: + Error: + type: object + properties: + error: + type: object + properties: + name: + type: string + message: + type: string + required: + - name + - message + request: + type: object + properties: + method: + type: string + url: + type: string + required: + - method + - url + response: + type: object + properties: + statusCode: + type: number + required: + - statusCode + required: + - error + - request + - response + JsonObject: type: object properties: {} @@ -85,18 +123,8 @@ paths: type: integer required: - results - '400': - description: Bad request - content: - application/json: - schema: - type: object - properties: - error: - type: object - properties: - message: - type: string + default: + $ref: '#/components/responses/ErrorResponse' security: - {} - JWT: [] diff --git a/plugins/search-backend/src/service/router.test.ts b/plugins/search-backend/src/service/router.test.ts index 73523f2b62..401488dcbf 100644 --- a/plugins/search-backend/src/service/router.test.ts +++ b/plugins/search-backend/src/service/router.test.ts @@ -22,6 +22,8 @@ import { SearchEngine } from '@backstage/plugin-search-common'; import express from 'express'; import request from 'supertest'; import { createRouter } from './router'; +import { wrapInOpenApiTestServer } from '@backstage/backend-openapi-utils'; +import { Server } from 'http'; const mockPermissionEvaluator: PermissionEvaluator = { authorize: () => { @@ -33,7 +35,7 @@ const mockPermissionEvaluator: PermissionEvaluator = { }; describe('createRouter', () => { - let app: express.Express; + let app: express.Express | Server; let mockSearchEngine: jest.Mocked; beforeAll(async () => { @@ -65,7 +67,7 @@ describe('createRouter', () => { permissions: mockPermissionEvaluator, logger, }); - app = express().use(router); + app = wrapInOpenApiTestServer(express().use(router)); }); beforeEach(() => { @@ -78,6 +80,7 @@ describe('createRouter', () => { mockSearchEngine.query.mockRejectedValueOnce(error); const response = await request(app).get('/query'); + console.log((response as any).text); expect(response.status).toEqual(500); expect(response.body).toMatchObject( diff --git a/plugins/search-react/CHANGELOG.md b/plugins/search-react/CHANGELOG.md index 91ec2e4421..49e259950c 100644 --- a/plugins/search-react/CHANGELOG.md +++ b/plugins/search-react/CHANGELOG.md @@ -1,5 +1,23 @@ # @backstage/plugin-search-react +## 1.7.2-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- f75caf9f3d: Fixed a rare occurrence where a race in the search bar could throw away user input or cause the clear button not to work. +- 71c97e7d73: The filter options passed to `SearchResultGroupLayout` are now always explicitly rendered as strings by default. +- e7c09c4f4b: Use default extensions boundary and suspense on the alpha declarative `createSearchResultListItem` extension factory. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/frontend-app-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/types@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 1.7.1 ### Patch Changes diff --git a/plugins/search-react/package.json b/plugins/search-react/package.json index c2c181f657..25ed360a89 100644 --- a/plugins/search-react/package.json +++ b/plugins/search-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-react", - "version": "1.7.1", + "version": "1.7.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search-react/src/alpha.tsx b/plugins/search-react/src/alpha.tsx index f937ef99cc..80e464fe4e 100644 --- a/plugins/search-react/src/alpha.tsx +++ b/plugins/search-react/src/alpha.tsx @@ -107,14 +107,14 @@ export function createSearchResultListItemExtension< output: { item: searchResultItemExtensionData, }, - factory({ bind, config, source }) { + factory({ config, source }) { const ExtensionComponent = lazy(() => options .component({ config }) .then(component => ({ default: component })), ) as unknown as SearchResultItemExtensionComponent; - bind({ + return { item: { predicate: options.predicate, component: props => ( @@ -129,7 +129,7 @@ export function createSearchResultListItemExtension< ), }, - }); + }; }, }); } diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx index d9b29584f8..8a3dd84286 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.test.tsx @@ -339,7 +339,6 @@ describe('SearchBar', () => { value = 'new value'; await user.clear(textbox); - await waitFor(() => expect(textbox.value).toBe('')); // make sure new term is captured await user.type(textbox, value); diff --git a/plugins/search-react/src/components/SearchBar/SearchBar.tsx b/plugins/search-react/src/components/SearchBar/SearchBar.tsx index cfd0538694..d101efa669 100644 --- a/plugins/search-react/src/components/SearchBar/SearchBar.tsx +++ b/plugins/search-react/src/components/SearchBar/SearchBar.tsx @@ -30,6 +30,7 @@ import React, { KeyboardEvent, useCallback, useEffect, + useRef, useState, } from 'react'; import useDebounce from 'react-use/lib/useDebounce'; @@ -88,14 +89,28 @@ export const SearchBarBase: ForwardRefExoticComponent = const configApi = useApi(configApiRef); const [value, setValue] = useState(''); + const forwardedValueRef = useRef(''); useEffect(() => { - setValue(prevValue => - prevValue !== defaultValue ? String(defaultValue) : prevValue, - ); - }, [defaultValue]); + setValue(prevValue => { + // We only update the value if our current value is the same as it was + // for the most recent onChange call. Otherwise it means that the users + // has continued typing and we should not replace their input. + if (prevValue === forwardedValueRef.current) { + return String(defaultValue); + } + return prevValue; + }); + }, [defaultValue, forwardedValueRef]); - useDebounce(() => onChange(value), debounceTime, [value]); + useDebounce( + () => { + forwardedValueRef.current = value; + onChange(value); + }, + debounceTime, + [value], + ); const handleChange = useCallback( (e: ChangeEvent) => { @@ -115,7 +130,9 @@ export const SearchBarBase: ForwardRefExoticComponent = ); const handleClear = useCallback(() => { + forwardedValueRef.current = ''; onChange(''); + setValue(''); if (onClear) { onClear(); } diff --git a/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx b/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx index b19af6eef8..6cdfd205fc 100644 --- a/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx +++ b/plugins/search-react/src/components/SearchTracker/SearchTracker.tsx @@ -19,23 +19,22 @@ import { useAnalytics } from '@backstage/core-plugin-api'; import { useSearch } from '../../context'; import usePrevious from 'react-use/lib/usePrevious'; +function useFallingEdge(next: boolean) { + const prev = usePrevious(next); + return prev && !next; +} + /** * Capture search event on term change. */ export const TrackSearch = ({ children }: { children: React.ReactChild }) => { - const useHasChanged = (value: any) => { - const previousVal = usePrevious(value); - return previousVal !== value; - }; - const analytics = useAnalytics(); const { term, result } = useSearch(); const numberOfResults = result.value?.numberOfResults ?? undefined; // Stops the analtyics event from firing before the new search engine response is returned - const hasStartedLoading = useHasChanged(result.loading); - const hasFinishedLoading = hasStartedLoading && !result.loading; + const hasFinishedLoading = useFallingEdge(result.loading); useEffect(() => { if (term && hasFinishedLoading) { diff --git a/plugins/search-react/src/context/SearchContext.test.tsx b/plugins/search-react/src/context/SearchContext.test.tsx index 5aa217553f..f962bc1620 100644 --- a/plugins/search-react/src/context/SearchContext.test.tsx +++ b/plugins/search-react/src/context/SearchContext.test.tsx @@ -405,11 +405,10 @@ describe('SearchContext', () => { await waitFor(() => { expect(result.current).toEqual(expect.objectContaining(initialState)); + expect(result.current.fetchNextPage).toBeUndefined(); + expect(result.current.fetchPreviousPage).toBeDefined(); }); - expect(result.current.fetchNextPage).toBeUndefined(); - expect(result.current.fetchPreviousPage).toBeDefined(); - await act(async () => { result.current.fetchPreviousPage!(); }); diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index ff8d87c347..3251137a28 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,27 @@ # @backstage/plugin-search +## 1.4.2-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- fa11120050: Fixed incorrect plugin ID in `/alpha` export. +- 71c97e7d73: Minor internal code cleanup. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 1.4.1 ### Patch Changes diff --git a/plugins/search/package.json b/plugins/search/package.json index 23abaefa1d..31dd51d8bc 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-search", "description": "The Backstage plugin that provides your backstage app with search", - "version": "1.4.1", + "version": "1.4.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/search/src/components/SearchModal/SearchModal.test.tsx b/plugins/search/src/components/SearchModal/SearchModal.test.tsx index 6fce560800..0d0bfdef5c 100644 --- a/plugins/search/src/components/SearchModal/SearchModal.test.tsx +++ b/plugins/search/src/components/SearchModal/SearchModal.test.tsx @@ -15,7 +15,7 @@ */ import React from 'react'; -import { screen, waitFor } from '@testing-library/react'; +import { screen } from '@testing-library/react'; import { renderInTestApp, TestApiRegistry } from '@backstage/test-utils'; import userEvent from '@testing-library/user-event'; import { configApiRef } from '@backstage/core-plugin-api'; @@ -205,7 +205,6 @@ describe('SearchModal', () => { const input = screen.getByLabelText('Search'); await userEvent.clear(input); - await waitFor(() => expect(input.value).toBe('')); await userEvent.type(input, 'new term{enter}'); expect(navigate).toHaveBeenCalledWith('/search?query=new term'); diff --git a/plugins/search/src/components/SearchType/SearchType.Tabs.tsx b/plugins/search/src/components/SearchType/SearchType.Tabs.tsx index 57a311b00c..5b3990f7bb 100644 --- a/plugins/search/src/components/SearchType/SearchType.Tabs.tsx +++ b/plugins/search/src/components/SearchType/SearchType.Tabs.tsx @@ -16,10 +16,9 @@ import React, { useEffect } from 'react'; import { useSearch } from '@backstage/plugin-search-react'; -import { BackstageTheme } from '@backstage/theme'; import { makeStyles, Tab, Tabs } from '@material-ui/core'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ tabs: { borderBottom: `1px solid ${theme.palette.textVerySubtle}`, }, diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index 77895eb2f1..78027d462a 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-sentry +## 0.5.11-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.5.10 ### Patch Changes diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index 3856270628..f570259201 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-sentry", "description": "A Backstage plugin that integrates towards Sentry", - "version": "0.5.10", + "version": "0.5.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/shortcuts/CHANGELOG.md b/plugins/shortcuts/CHANGELOG.md index 6774c7eaf2..093dd70505 100644 --- a/plugins/shortcuts/CHANGELOG.md +++ b/plugins/shortcuts/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-shortcuts +## 0.3.16-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/types@1.1.1 + ## 0.3.15 ### Patch Changes diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index c38125d561..6bd14faa1e 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-shortcuts", "description": "A Backstage plugin that provides a shortcuts feature to the sidebar", - "version": "0.3.15", + "version": "0.3.16-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-backend/CHANGELOG.md b/plugins/sonarqube-backend/CHANGELOG.md index caf2a677c1..b5e03c7bc8 100644 --- a/plugins/sonarqube-backend/CHANGELOG.md +++ b/plugins/sonarqube-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-sonarqube-backend +## 0.2.9-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.2.8 ### Patch Changes diff --git a/plugins/sonarqube-backend/package.json b/plugins/sonarqube-backend/package.json index 63592380db..50fbbc590f 100644 --- a/plugins/sonarqube-backend/package.json +++ b/plugins/sonarqube-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-backend", - "version": "0.2.8", + "version": "0.2.9-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube-react/CHANGELOG.md b/plugins/sonarqube-react/CHANGELOG.md index af370f6d1a..5bdedc7354 100644 --- a/plugins/sonarqube-react/CHANGELOG.md +++ b/plugins/sonarqube-react/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-sonarqube-react +## 0.1.10-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.1.9 ### Patch Changes diff --git a/plugins/sonarqube-react/package.json b/plugins/sonarqube-react/package.json index c643df3702..bfcd061497 100644 --- a/plugins/sonarqube-react/package.json +++ b/plugins/sonarqube-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube-react", - "version": "0.1.9", + "version": "0.1.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 3ed88667a9..18e618d42f 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-sonarqube +## 0.7.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-sonarqube-react@0.1.10-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.7.6 ### Patch Changes diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index 00d6c494db..eb00a9736a 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,15 +1,14 @@ { "name": "@backstage/plugin-sonarqube", "description": "", - "version": "0.7.6", + "version": "0.7.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "types": "dist/index.d.ts" }, "backstage": { "role": "frontend-plugin" @@ -27,7 +26,7 @@ ], "sideEffects": false, "scripts": { - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "start": "backstage-cli package start", "lint": "backstage-cli package lint", "test": "backstage-cli package test", diff --git a/plugins/sonarqube/src/components/SonarQubeCard/Percentage.tsx b/plugins/sonarqube/src/components/SonarQubeCard/Percentage.tsx index 406817de4b..efc19d4bf5 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/Percentage.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/Percentage.tsx @@ -14,13 +14,12 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; import { makeStyles } from '@material-ui/core/styles'; import { useTheme } from '@material-ui/core'; import { Circle } from 'rc-progress'; import React from 'react'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ root: { height: theme.spacing(3), width: theme.spacing(3), @@ -29,7 +28,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export const Percentage = ({ value }: { value?: string }) => { const classes = useStyles(); - const theme = useTheme(); + const theme = useTheme(); return ( { +const useStyles = makeStyles(theme => { const commonCardRating: CSSProperties = { height: theme.spacing(3), width: theme.spacing(3), diff --git a/plugins/sonarqube/src/components/SonarQubeCard/Value.tsx b/plugins/sonarqube/src/components/SonarQubeCard/Value.tsx index 2f136cc5fe..af1b5059ba 100644 --- a/plugins/sonarqube/src/components/SonarQubeCard/Value.tsx +++ b/plugins/sonarqube/src/components/SonarQubeCard/Value.tsx @@ -14,12 +14,11 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import React from 'react'; -const useStyles = makeStyles((theme: BackstageTheme) => { +const useStyles = makeStyles(theme => { return { value: { fontSize: '1.5rem', diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 4b1ac632d1..c98ccdbeb4 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-splunk-on-call +## 0.4.15-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + ## 0.4.14 ### Patch Changes diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index e07a08254f..3a971fd613 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-splunk-on-call", "description": "A Backstage plugin that integrates towards Splunk On-Call", - "version": "0.4.14", + "version": "0.4.15-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow-backend/CHANGELOG.md b/plugins/stack-overflow-backend/CHANGELOG.md index bb83becff1..33ede5165e 100644 --- a/plugins/stack-overflow-backend/CHANGELOG.md +++ b/plugins/stack-overflow-backend/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-stack-overflow-backend +## 0.2.11-next.0 + +### Patch Changes + +- b168d7e7ea: Deprecate package in favor of the new `@backstage/plugin-search-backend-module-stack-overflow-collator` module. + + The search collator `requestParams` option is optional now, so its default value is `{ order: 'desc', sort: 'activity', site: 'stackoverflow' }` as defined in the `Try It` section on the [official Stack Overflow API documentation](https://api.stackexchange.com/docs/questions). + +- Updated dependencies + - @backstage/plugin-search-backend-module-stack-overflow-collator@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 0.2.10 ### Patch Changes diff --git a/plugins/stack-overflow-backend/package.json b/plugins/stack-overflow-backend/package.json index 77ebd8c69e..b397d39d75 100644 --- a/plugins/stack-overflow-backend/package.json +++ b/plugins/stack-overflow-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stack-overflow-backend", "description": "Deprecated, consider using @backstage/plugin-search-backend-module-stack-overflow-collator instead", - "version": "0.2.10", + "version": "0.2.11-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stack-overflow/CHANGELOG.md b/plugins/stack-overflow/CHANGELOG.md index 7ff52c4364..55a046bfad 100644 --- a/plugins/stack-overflow/CHANGELOG.md +++ b/plugins/stack-overflow/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-stack-overflow +## 0.1.22-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- b168d7e7ea: Migrate package to the new Frontend system, the new module is distributed with a `/alpha` subpath. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/plugin-home-react@0.1.5-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/config@1.1.1 + - @backstage/plugin-search-common@1.2.7 + ## 0.1.21 ### Patch Changes diff --git a/plugins/stack-overflow/package.json b/plugins/stack-overflow/package.json index 431f8a5b80..51a1031e86 100644 --- a/plugins/stack-overflow/package.json +++ b/plugins/stack-overflow/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-stack-overflow", - "version": "0.1.21", + "version": "0.1.22-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/stackstorm/CHANGELOG.md b/plugins/stackstorm/CHANGELOG.md index d831792629..88736b62aa 100644 --- a/plugins/stackstorm/CHANGELOG.md +++ b/plugins/stackstorm/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-stackstorm +## 0.1.8-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + ## 0.1.7 ### Patch Changes diff --git a/plugins/stackstorm/package.json b/plugins/stackstorm/package.json index 76f9958812..bb86be6b13 100644 --- a/plugins/stackstorm/package.json +++ b/plugins/stackstorm/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-stackstorm", "description": "A Backstage plugin that integrates towards StackStorm", - "version": "0.1.7", + "version": "0.1.8-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md index f319b515ec..25a5396f0a 100644 --- a/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md +++ b/plugins/tech-insights-backend-module-jsonfc/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-insights-backend-module-jsonfc +## 0.1.39-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + ## 0.1.38 ### Patch Changes diff --git a/plugins/tech-insights-backend-module-jsonfc/package.json b/plugins/tech-insights-backend-module-jsonfc/package.json index 17fa4702e3..45e7955366 100644 --- a/plugins/tech-insights-backend-module-jsonfc/package.json +++ b/plugins/tech-insights-backend-module-jsonfc/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend-module-jsonfc", - "version": "0.1.38", + "version": "0.1.39-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-backend/CHANGELOG.md b/plugins/tech-insights-backend/CHANGELOG.md index fcd5520d29..dc3ce7355d 100644 --- a/plugins/tech-insights-backend/CHANGELOG.md +++ b/plugins/tech-insights-backend/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-tech-insights-backend +## 0.5.21-next.0 + +### Patch Changes + +- 193ad022bb: Add `factRetrieverId` to the fact retriever's logger metadata. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + - @backstage/plugin-tech-insights-node@0.4.13-next.0 + ## 0.5.20 ### Patch Changes diff --git a/plugins/tech-insights-backend/package.json b/plugins/tech-insights-backend/package.json index 1dda0595f5..ab9fb19b22 100644 --- a/plugins/tech-insights-backend/package.json +++ b/plugins/tech-insights-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-backend", - "version": "0.5.20", + "version": "0.5.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights-node/CHANGELOG.md b/plugins/tech-insights-node/CHANGELOG.md index 37117bc0d8..6e046eea3b 100644 --- a/plugins/tech-insights-node/CHANGELOG.md +++ b/plugins/tech-insights-node/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-tech-insights-node +## 0.4.13-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.4.12 ### Patch Changes diff --git a/plugins/tech-insights-node/package.json b/plugins/tech-insights-node/package.json index 23863a774d..478125b16c 100644 --- a/plugins/tech-insights-node/package.json +++ b/plugins/tech-insights-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights-node", - "version": "0.4.12", + "version": "0.4.13-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights/CHANGELOG.md b/plugins/tech-insights/CHANGELOG.md index 83644ecdab..1a19864fbb 100644 --- a/plugins/tech-insights/CHANGELOG.md +++ b/plugins/tech-insights/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-tech-insights +## 0.3.18-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-tech-insights-common@0.2.12 + ## 0.3.17 ### Patch Changes diff --git a/plugins/tech-insights/package.json b/plugins/tech-insights/package.json index 7735fb3719..ff005c0581 100644 --- a/plugins/tech-insights/package.json +++ b/plugins/tech-insights/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-tech-insights", - "version": "0.3.17", + "version": "0.3.18-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/tech-insights/src/components/ScorecardsInfo/ScorecardInfo.tsx b/plugins/tech-insights/src/components/ScorecardsInfo/ScorecardInfo.tsx index 8002784c9e..cfff074368 100644 --- a/plugins/tech-insights/src/components/ScorecardsInfo/ScorecardInfo.tsx +++ b/plugins/tech-insights/src/components/ScorecardsInfo/ScorecardInfo.tsx @@ -18,11 +18,10 @@ import React from 'react'; import { makeStyles, Grid, Typography } from '@material-ui/core'; import { InfoCard } from '@backstage/core-components'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; -import { BackstageTheme } from '@backstage/theme'; import { Alert } from '@material-ui/lab'; import { ScorecardsList } from '../ScorecardsList'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ subheader: { fontWeight: 'bold', paddingLeft: theme.spacing(0.5), diff --git a/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx b/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx index e8e57b0afe..1a9d04a9de 100644 --- a/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx +++ b/plugins/tech-insights/src/components/ScorecardsList/ScorecardsList.tsx @@ -19,11 +19,10 @@ import { useApi } from '@backstage/core-plugin-api'; import { makeStyles, List, ListItem, ListItemText } from '@material-ui/core'; import { techInsightsApiRef } from '../../api'; import { CheckResult } from '@backstage/plugin-tech-insights-common'; -import { BackstageTheme } from '@backstage/theme'; import { Alert } from '@material-ui/lab'; import { MarkdownContent } from '@backstage/core-components'; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ listItemText: { paddingRight: theme.spacing(0.5), }, diff --git a/plugins/tech-radar/CHANGELOG.md b/plugins/tech-radar/CHANGELOG.md index 276c50be04..b70e0607e1 100644 --- a/plugins/tech-radar/CHANGELOG.md +++ b/plugins/tech-radar/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-tech-radar +## 0.6.10-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + ## 0.6.9 ### Patch Changes diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 82fd7ca147..63a416ae24 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-tech-radar", "description": "A Backstage plugin that lets you display a Tech Radar for your organization", - "version": "0.6.9", + "version": "0.6.10-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-addons-test-utils/CHANGELOG.md b/plugins/techdocs-addons-test-utils/CHANGELOG.md index e215d3d842..7cc7651156 100644 --- a/plugins/techdocs-addons-test-utils/CHANGELOG.md +++ b/plugins/techdocs-addons-test-utils/CHANGELOG.md @@ -1,5 +1,22 @@ # @backstage/plugin-techdocs-addons-test-utils +## 1.0.23-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog@1.15.0-next.0 + - @backstage/plugin-techdocs@1.8.1-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/test-utils@1.4.5-next.0 + - @backstage/theme@0.4.4-next.0 + ## 1.0.22 ### Patch Changes diff --git a/plugins/techdocs-addons-test-utils/package.json b/plugins/techdocs-addons-test-utils/package.json index b65cc1d2b1..874524528d 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.22", + "version": "1.0.23-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index ad2b8f67ed..3d10ddb03c 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,24 @@ # @backstage/plugin-techdocs-backend +## 1.8.1-next.0 + +### Patch Changes + +- c3c5c7e514: Add info about the entity when tech docs fail to build +- Updated dependencies + - @backstage/plugin-search-backend-module-techdocs@0.1.11-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-common@1.0.17 + - @backstage/plugin-permission-common@0.7.9 + - @backstage/plugin-search-common@1.2.7 + - @backstage/plugin-techdocs-node@1.9.1-next.0 + ## 1.8.0 ### Minor Changes diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index b8974e6d4f..9e9a42c388 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-backend/api-report.md @@ -7,6 +7,7 @@ import { CatalogApi } from '@backstage/catalog-client'; import { CatalogClient } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { DefaultTechDocsCollatorFactory as DefaultTechDocsCollatorFactory_2 } from '@backstage/plugin-search-backend-module-techdocs'; +import { DocsBuildStrategy as DocsBuildStrategy_2 } from '@backstage/plugin-techdocs-node'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; import { GeneratorBuilder } from '@backstage/plugin-techdocs-node'; @@ -18,7 +19,7 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { PreparerBuilder } from '@backstage/plugin-techdocs-node'; import { PublisherBase } from '@backstage/plugin-techdocs-node'; import type { TechDocsCollatorFactoryOptions as TechDocsCollatorFactoryOptions_2 } from '@backstage/plugin-search-backend-module-techdocs'; -import { TechDocsDocument } from '@backstage/plugin-techdocs-node'; +import { TechDocsDocument as TechDocsDocument_2 } from '@backstage/plugin-techdocs-node'; import { TokenManager } from '@backstage/backend-common'; import * as winston from 'winston'; @@ -33,7 +34,7 @@ export class DefaultTechDocsCollator { args: Record, ): string; // (undocumented) - execute(): Promise; + execute(): Promise; // (undocumented) static fromConfig( config: Config, @@ -48,11 +49,8 @@ export class DefaultTechDocsCollator { // @public @deprecated (undocumented) export const DefaultTechDocsCollatorFactory: typeof DefaultTechDocsCollatorFactory_2; -// @public -export interface DocsBuildStrategy { - // (undocumented) - shouldBuild(params: ShouldBuildParameters): Promise; -} +// @public @deprecated (undocumented) +export type DocsBuildStrategy = DocsBuildStrategy_2; // @public export type OutOfTheBoxDeploymentOptions = { @@ -64,7 +62,7 @@ export type OutOfTheBoxDeploymentOptions = { database?: Knex; config: Config; cache: PluginCacheManager; - docsBuildStrategy?: DocsBuildStrategy; + docsBuildStrategy?: DocsBuildStrategy_2; buildLogTransport?: winston.transport; catalogClient?: CatalogClient; }; @@ -76,7 +74,7 @@ export type RecommendedDeploymentOptions = { discovery: PluginEndpointDiscovery; config: Config; cache: PluginCacheManager; - docsBuildStrategy?: DocsBuildStrategy; + docsBuildStrategy?: DocsBuildStrategy_2; buildLogTransport?: winston.transport; catalogClient?: CatalogClient; }; @@ -86,7 +84,7 @@ export type RouterOptions = | RecommendedDeploymentOptions | OutOfTheBoxDeploymentOptions; -// @public +// @public @deprecated (undocumented) export type ShouldBuildParameters = { entity: Entity; }; @@ -105,7 +103,8 @@ export type TechDocsCollatorOptions = { legacyPathCasing?: boolean; }; -export { TechDocsDocument }; +// @public @deprecated (undocumented) +export type TechDocsDocument = TechDocsDocument_2; export * from '@backstage/plugin-techdocs-node'; ``` diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index c3e2ee0708..ab1f4ab6f6 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-backend", "description": "The Backstage backend plugin that renders technical documentation for your components", - "version": "1.8.0", + "version": "1.8.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-backend/src/index.ts b/plugins/techdocs-backend/src/index.ts index dc5d87d1b3..6dc68a4d44 100644 --- a/plugins/techdocs-backend/src/index.ts +++ b/plugins/techdocs-backend/src/index.ts @@ -20,13 +20,17 @@ * @packageDocumentation */ +import { Entity } from '@backstage/catalog-model'; +import { + DocsBuildStrategy as _DocsBuildStrategy, + TechDocsDocument as _TechDocsDocument, +} from '@backstage/plugin-techdocs-node'; + export { createRouter } from './service'; export type { RouterOptions, RecommendedDeploymentOptions, OutOfTheBoxDeploymentOptions, - DocsBuildStrategy, - ShouldBuildParameters, } from './service'; export { @@ -39,8 +43,21 @@ export type { } from './search'; /** - * @deprecated Use directly from @backstage/plugin-techdocs-node + * @public + * @deprecated import from `@backstage/plugin-techdocs-node` instead */ -export type { TechDocsDocument } from '@backstage/plugin-techdocs-node'; +export type DocsBuildStrategy = _DocsBuildStrategy; +/** + * @public + * @deprecated use direct type definition instead + */ +export type ShouldBuildParameters = { + entity: Entity; +}; +/** + * @public + * @deprecated import from `@backstage/plugin-techdocs-node` instead + */ +export type TechDocsDocument = _TechDocsDocument; export * from '@backstage/plugin-techdocs-node'; diff --git a/plugins/techdocs-backend/src/plugin.ts b/plugins/techdocs-backend/src/plugin.ts index 745370b67a..f139cf3c3a 100644 --- a/plugins/techdocs-backend/src/plugin.ts +++ b/plugins/techdocs-backend/src/plugin.ts @@ -25,9 +25,11 @@ import { } from '@backstage/backend-plugin-api'; import { + DocsBuildStrategy, Preparers, Generators, Publisher, + techdocsBuildsExtensionPoint, } from '@backstage/plugin-techdocs-node'; import Docker from 'dockerode'; import { createRouter } from '@backstage/plugin-techdocs-backend'; @@ -39,6 +41,16 @@ import { createRouter } from '@backstage/plugin-techdocs-backend'; export const techdocsPlugin = createBackendPlugin({ pluginId: 'techdocs', register(env) { + let docsBuildStrategy: DocsBuildStrategy | undefined; + env.registerExtensionPoint(techdocsBuildsExtensionPoint, { + setBuildStrategy(buildStrategy: DocsBuildStrategy) { + if (docsBuildStrategy) { + throw new Error('DocsBuildStrategy may only be set once'); + } + docsBuildStrategy = buildStrategy; + }, + }); + env.registerInit({ deps: { config: coreServices.rootConfig, @@ -82,6 +94,7 @@ export const techdocsPlugin = createBackendPlugin({ await createRouter({ logger: winstonLogger, cache: cacheManager, + docsBuildStrategy, preparers, generators, publisher, diff --git a/plugins/techdocs-backend/src/service/DocsBuildStrategy.test.ts b/plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.test.ts similarity index 96% rename from plugins/techdocs-backend/src/service/DocsBuildStrategy.test.ts rename to plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.test.ts index 84bd960f8f..7868135b08 100644 --- a/plugins/techdocs-backend/src/service/DocsBuildStrategy.test.ts +++ b/plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { DefaultDocsBuildStrategy } from './DocsBuildStrategy'; +import { DefaultDocsBuildStrategy } from './DefaultDocsBuildStrategy'; import { ConfigReader } from '@backstage/config'; const MockedConfigReader = ConfigReader as jest.MockedClass< diff --git a/plugins/techdocs-backend/src/service/DocsBuildStrategy.ts b/plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.ts similarity index 66% rename from plugins/techdocs-backend/src/service/DocsBuildStrategy.ts rename to plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.ts index 42a16234f9..bc2c586c82 100644 --- a/plugins/techdocs-backend/src/service/DocsBuildStrategy.ts +++ b/plugins/techdocs-backend/src/service/DefaultDocsBuildStrategy.ts @@ -15,26 +15,9 @@ */ import { Entity } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; +import { DocsBuildStrategy } from '@backstage/plugin-techdocs-node'; -/** - * Parameters passed to the shouldBuild method on the DocsBuildStrategy interface - * - * @public - */ -export type ShouldBuildParameters = { - entity: Entity; -}; - -/** - * A strategy for when to build TechDocs locally, and when to skip building TechDocs (allowing for an external build) - * - * @public - */ -export interface DocsBuildStrategy { - shouldBuild(params: ShouldBuildParameters): Promise; -} - -export class DefaultDocsBuildStrategy { +export class DefaultDocsBuildStrategy implements DocsBuildStrategy { private readonly config: Config; private constructor(config: Config) { @@ -45,7 +28,7 @@ export class DefaultDocsBuildStrategy { return new DefaultDocsBuildStrategy(config); } - async shouldBuild(_: ShouldBuildParameters): Promise { + async shouldBuild(_: { entity: Entity }): Promise { return this.config.getString('techdocs.builder') === 'local'; } } diff --git a/plugins/techdocs-backend/src/service/DocsSynchronizer.ts b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts index fad915b068..7859b55e3f 100644 --- a/plugins/techdocs-backend/src/service/DocsSynchronizer.ts +++ b/plugins/techdocs-backend/src/service/DocsSynchronizer.ts @@ -48,7 +48,7 @@ export type DocsSynchronizerSyncOpts = { export class DocsSynchronizer { private readonly publisher: PublisherBase; private readonly logger: winston.Logger; - private readonly buildLogTransport: winston.transport; + private readonly buildLogTransport?: winston.transport; private readonly config: Config; private readonly scmIntegrations: ScmIntegrationRegistry; private readonly cache: TechDocsCache | undefined; @@ -64,7 +64,7 @@ export class DocsSynchronizer { }: { publisher: PublisherBase; logger: winston.Logger; - buildLogTransport: winston.transport; + buildLogTransport?: winston.transport; config: Config; scmIntegrations: ScmIntegrationRegistry; cache: TechDocsCache | undefined; @@ -109,7 +109,9 @@ export class DocsSynchronizer { }); taskLogger.add(new winston.transports.Stream({ stream: logStream })); - taskLogger.add(this.buildLogTransport); + if (this.buildLogTransport) { + taskLogger.add(this.buildLogTransport); + } // check if the last update check was too recent if (!shouldCheckForUpdate(entity.metadata.uid!)) { diff --git a/plugins/techdocs-backend/src/service/index.ts b/plugins/techdocs-backend/src/service/index.ts index 7355a34e32..0065e33a2a 100644 --- a/plugins/techdocs-backend/src/service/index.ts +++ b/plugins/techdocs-backend/src/service/index.ts @@ -20,7 +20,3 @@ export type { RecommendedDeploymentOptions, OutOfTheBoxDeploymentOptions, } from './router'; -export type { - DocsBuildStrategy, - ShouldBuildParameters, -} from './DocsBuildStrategy'; diff --git a/plugins/techdocs-backend/src/service/router.test.ts b/plugins/techdocs-backend/src/service/router.test.ts index 0c049c00f2..224a87cb43 100644 --- a/plugins/techdocs-backend/src/service/router.test.ts +++ b/plugins/techdocs-backend/src/service/router.test.ts @@ -22,6 +22,7 @@ import { } from '@backstage/backend-common'; import { ConfigReader } from '@backstage/config'; import { + DocsBuildStrategy, GeneratorBuilder, PreparerBuilder, PublisherBase, @@ -32,7 +33,6 @@ import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer'; import { CachedEntityLoader } from './CachedEntityLoader'; import { createEventStream, createRouter, RouterOptions } from './router'; import { TechDocsCache } from '../cache'; -import { DocsBuildStrategy } from './DocsBuildStrategy'; jest.mock('@backstage/catalog-client'); jest.mock('@backstage/config'); diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index 855d8b02f4..00aea0b0dd 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -22,6 +22,7 @@ import { stringifyEntityRef } from '@backstage/catalog-model'; import { Config } from '@backstage/config'; import { NotFoundError } from '@backstage/errors'; import { + DocsBuildStrategy, GeneratorBuilder, getLocationForEntity, PreparerBuilder, @@ -34,12 +35,8 @@ import { ScmIntegrations } from '@backstage/integration'; import { DocsSynchronizer, DocsSynchronizerSyncOpts } from './DocsSynchronizer'; import { createCacheMiddleware, TechDocsCache } from '../cache'; import { CachedEntityLoader } from './CachedEntityLoader'; -import { - DefaultDocsBuildStrategy, - DocsBuildStrategy, -} from './DocsBuildStrategy'; +import { DefaultDocsBuildStrategy } from './DefaultDocsBuildStrategy'; import * as winston from 'winston'; -import { PassThrough } from 'stream'; /** * Required dependencies for running TechDocs in the "out-of-the-box" @@ -113,9 +110,7 @@ export async function createRouter( options.catalogClient ?? new CatalogClient({ discoveryApi: discovery }); const docsBuildStrategy = options.docsBuildStrategy ?? DefaultDocsBuildStrategy.fromConfig(config); - const buildLogTransport = - options.buildLogTransport ?? - new winston.transports.Stream({ stream: new PassThrough() }); + const buildLogTransport = options.buildLogTransport; // Entities are cached to optimize the /static/docs request path, which can be called many times // when loading a single techdocs page. diff --git a/plugins/techdocs-module-addons-contrib/CHANGELOG.md b/plugins/techdocs-module-addons-contrib/CHANGELOG.md index 37b6a46b20..e151635876 100644 --- a/plugins/techdocs-module-addons-contrib/CHANGELOG.md +++ b/plugins/techdocs-module-addons-contrib/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-techdocs-module-addons-contrib +## 1.1.2-next.0 + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + ## 1.1.1 ### Patch Changes diff --git a/plugins/techdocs-module-addons-contrib/package.json b/plugins/techdocs-module-addons-contrib/package.json index cf92b0c969..c67a4aa3a8 100644 --- a/plugins/techdocs-module-addons-contrib/package.json +++ b/plugins/techdocs-module-addons-contrib/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-module-addons-contrib", "description": "Plugin module for contributed TechDocs Addons", - "version": "1.1.1", + "version": "1.1.2-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx b/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx index fc865c7826..50cc59b814 100644 --- a/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx +++ b/plugins/techdocs-module-addons-contrib/src/TextSize/TextSize.tsx @@ -33,11 +33,11 @@ import { Slider, IconButton, Typography, + Theme, } from '@material-ui/core'; import AddIcon from '@material-ui/icons/Add'; import RemoveIcon from '@material-ui/icons/Remove'; -import { BackstageTheme } from '@backstage/theme'; import { useShadowRootElements } from '@backstage/plugin-techdocs-react'; const boxShadow = @@ -116,7 +116,7 @@ const marks = [ }, ]; -const useStyles = makeStyles((theme: BackstageTheme) => ({ +const useStyles = makeStyles(theme => ({ container: { color: theme.palette.textSubtle, display: 'flex', @@ -139,7 +139,7 @@ const useStyles = makeStyles((theme: BackstageTheme) => ({ export const TextSizeAddon = () => { const classes = useStyles(); - const theme = useTheme(); + const theme = useTheme(); const [body] = useShadowRootElements(['body']); const [value, setValue] = useState(() => { @@ -182,7 +182,7 @@ export const TextSizeAddon = () => { if (!body) return; const htmlFontSize = ( - theme.typography as BackstageTheme['typography'] & { + theme.typography as Theme['typography'] & { htmlFontSize?: number; } )?.htmlFontSize ?? 16; diff --git a/plugins/techdocs-module-addons-contrib/src/setupTests.ts b/plugins/techdocs-module-addons-contrib/src/setupTests.ts index 326d2ed212..56b9b18321 100644 --- a/plugins/techdocs-module-addons-contrib/src/setupTests.ts +++ b/plugins/techdocs-module-addons-contrib/src/setupTests.ts @@ -14,3 +14,5 @@ * limitations under the License. */ import '@testing-library/jest-dom'; + +Element.prototype.scrollIntoView = jest.fn(); diff --git a/plugins/techdocs-node/CHANGELOG.md b/plugins/techdocs-node/CHANGELOG.md index 2576313037..c10de03d91 100644 --- a/plugins/techdocs-node/CHANGELOG.md +++ b/plugins/techdocs-node/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-techdocs-node +## 1.9.1-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/integration-aws-node@0.1.7 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.7 + ## 1.9.0 ### Minor Changes diff --git a/plugins/techdocs-node/api-report.md b/plugins/techdocs-node/api-report.md index 656db6ae36..c267574642 100644 --- a/plugins/techdocs-node/api-report.md +++ b/plugins/techdocs-node/api-report.md @@ -10,6 +10,7 @@ import { Config } from '@backstage/config'; import { ContainerRunner } from '@backstage/backend-common'; import { Entity } from '@backstage/catalog-model'; import express from 'express'; +import { ExtensionPoint } from '@backstage/backend-plugin-api'; import { IndexableDocument } from '@backstage/plugin-search-common'; import { Logger } from 'winston'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; @@ -24,6 +25,12 @@ export class DirectoryPreparer implements PreparerBase { shouldCleanPreparedDirectory(): boolean; } +// @public +export interface DocsBuildStrategy { + // (undocumented) + shouldBuild(params: { entity: Entity }): Promise; +} + // @public export type ETag = string; @@ -229,6 +236,15 @@ export type RemoteProtocol = 'url' | 'dir'; // @public export type SupportedGeneratorKey = 'techdocs' | string; +// @public +export interface TechdocsBuildsExtensionPoint { + // (undocumented) + setBuildStrategy(buildStrategy: DocsBuildStrategy): void; +} + +// @public +export const techdocsBuildsExtensionPoint: ExtensionPoint; + // @public export interface TechDocsDocument extends IndexableDocument { kind: string; diff --git a/plugins/techdocs-node/package.json b/plugins/techdocs-node/package.json index 712693f6af..956b9f15be 100644 --- a/plugins/techdocs-node/package.json +++ b/plugins/techdocs-node/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-node", "description": "Common node.js functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "1.9.0", + "version": "1.9.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "publishConfig": { @@ -42,11 +42,11 @@ "@aws-sdk/client-s3": "^3.350.0", "@aws-sdk/credential-providers": "^3.350.0", "@aws-sdk/lib-storage": "^3.350.0", - "@aws-sdk/node-http-handler": "^3.350.0", "@aws-sdk/types": "^3.347.0", "@azure/identity": "^3.2.1", "@azure/storage-blob": "^12.5.0", "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/catalog-model": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", @@ -54,6 +54,7 @@ "@backstage/integration-aws-node": "workspace:^", "@backstage/plugin-search-common": "workspace:^", "@google-cloud/storage": "^6.0.0", + "@smithy/node-http-handler": "^2.1.7", "@trendyol-js/openstack-swift-sdk": "^0.0.6", "@types/express": "^4.17.6", "express": "^4.17.1", diff --git a/plugins/techdocs-node/src/extensions.ts b/plugins/techdocs-node/src/extensions.ts new file mode 100644 index 0000000000..c3a048b4e2 --- /dev/null +++ b/plugins/techdocs-node/src/extensions.ts @@ -0,0 +1,36 @@ +/* + * 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 { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { DocsBuildStrategy } from './techdocsTypes'; + +/** + * Extension point type for configuring Techdocs builds. + * + * @public + */ +export interface TechdocsBuildsExtensionPoint { + setBuildStrategy(buildStrategy: DocsBuildStrategy): void; +} + +/** + * Extension point for configuring Techdocs builds. + * + * @public + */ +export const techdocsBuildsExtensionPoint = + createExtensionPoint({ + id: 'techdocs.builds', + }); diff --git a/plugins/techdocs-node/src/index.ts b/plugins/techdocs-node/src/index.ts index a2acf84611..640bd09ce0 100644 --- a/plugins/techdocs-node/src/index.ts +++ b/plugins/techdocs-node/src/index.ts @@ -23,3 +23,7 @@ export * from './stages'; export * from './helpers'; export * from './techdocsTypes'; +export { + techdocsBuildsExtensionPoint, + type TechdocsBuildsExtensionPoint, +} from './extensions'; diff --git a/plugins/techdocs-node/src/stages/publish/awsS3.ts b/plugins/techdocs-node/src/stages/publish/awsS3.ts index 679e9bf7c9..29a5277682 100644 --- a/plugins/techdocs-node/src/stages/publish/awsS3.ts +++ b/plugins/techdocs-node/src/stages/publish/awsS3.ts @@ -32,7 +32,7 @@ import { S3Client, } from '@aws-sdk/client-s3'; import { fromTemporaryCredentials } from '@aws-sdk/credential-providers'; -import { NodeHttpHandler } from '@aws-sdk/node-http-handler'; +import { NodeHttpHandler } from '@smithy/node-http-handler'; import { Upload } from '@aws-sdk/lib-storage'; import { AwsCredentialIdentityProvider } from '@aws-sdk/types'; import { HttpsProxyAgent } from 'hpagent'; diff --git a/plugins/techdocs-node/src/techdocsTypes.ts b/plugins/techdocs-node/src/techdocsTypes.ts index 77436b35d5..6a5ee522e1 100644 --- a/plugins/techdocs-node/src/techdocsTypes.ts +++ b/plugins/techdocs-node/src/techdocsTypes.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { Entity } from '@backstage/catalog-model'; import { IndexableDocument } from '@backstage/plugin-search-common'; /** @@ -46,3 +47,12 @@ export interface TechDocsDocument extends IndexableDocument { */ path: string; } + +/** + * A strategy for when to build TechDocs locally, and when to skip building TechDocs (allowing for an external build) + * + * @public + */ +export interface DocsBuildStrategy { + shouldBuild(params: { entity: Entity }): Promise; +} diff --git a/plugins/techdocs-react/CHANGELOG.md b/plugins/techdocs-react/CHANGELOG.md index 42edbad969..2ed5c503f1 100644 --- a/plugins/techdocs-react/CHANGELOG.md +++ b/plugins/techdocs-react/CHANGELOG.md @@ -1,5 +1,17 @@ # @backstage/plugin-techdocs-react +## 1.1.13-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/version-bridge@1.0.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + ## 1.1.12 ### Patch Changes diff --git a/plugins/techdocs-react/package.json b/plugins/techdocs-react/package.json index 79d2b3cc17..eaae848546 100644 --- a/plugins/techdocs-react/package.json +++ b/plugins/techdocs-react/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs-react", "description": "Shared frontend utilities for TechDocs and Addons", - "version": "1.1.12", + "version": "1.1.13-next.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 4db98ea055..930997aac3 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,32 @@ # @backstage/plugin-techdocs +## 1.8.1-next.0 + +### Patch Changes + +- 4728b3960d: Fixed navigation bug that caused users to not be scrolled to the top of a new page. Fixed navigation bug where using backwards and forwards browser navigation did not scroll users to the correct place on the TechDoc page. +- a3add7a682: Export alpha routes and nav item extension, only available for applications that uses the new Frontend system. +- 71c97e7d73: The `spec.lifecycle' field in entities will now always be rendered as a string. +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- 0bf6ebda88: Added entity page content for the new plugin exported via `/alpha`. +- 67cc85bb14: Switched the conditional `react-dom/client` import to use `import(...)` rather than `require(...)`. +- 38cda52746: Added support for React 18. The new `createRoot` API from `react-dom/client` will now be used if present. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/integration-react@1.1.21-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/plugin-techdocs-react@1.1.13-next.0 + - @backstage/plugin-search-react@1.7.2-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/integration@1.7.1 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-search-common@1.2.7 + ## 1.8.0 ### Minor Changes diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 3df7399521..d3808c961f 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-techdocs", "description": "The Backstage plugin that renders technical documentation for your components", - "version": "1.8.0", + "version": "1.8.1-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/techdocs/src/alpha.tsx b/plugins/techdocs/src/alpha.tsx index b7ccb0b12c..ceb5f47364 100644 --- a/plugins/techdocs/src/alpha.tsx +++ b/plugins/techdocs/src/alpha.tsx @@ -42,6 +42,7 @@ import { rootDocsRouteRef, rootRouteRef, } from './routes'; +import { createEntityContentExtension } from '@backstage/plugin-catalog-react/alpha'; /** @alpha */ const techDocsStorage = createApiExtension({ @@ -141,6 +142,18 @@ const TechDocsReaderPage = createPageExtension({ )), }); +/** + * Component responsible for rendering techdocs on entity pages + * + * @alpha + */ +const TechDocsEntityContent = createEntityContentExtension({ + id: 'techdocs', + defaultPath: 'docs', + defaultTitle: 'TechDocs', + loader: () => import('./Router').then(m => ), +}); + /** @alpha */ const TechDocsNavItem = createNavItemExtension({ id: 'plugin.techdocs.nav.index', @@ -158,6 +171,7 @@ export default createPlugin({ TechDocsNavItem, TechDocsIndexPage, TechDocsReaderPage, + TechDocsEntityContent, TechDocsSearchResultListItemExtension, ], routes: { diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.test.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.test.tsx index 32fbe21714..adef2f64d0 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.test.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.test.tsx @@ -33,6 +33,10 @@ jest.mock('../useReaderState', () => ({ ...jest.requireActual('../useReaderState'), useReaderState: (...args: any[]) => useReaderState(...args), })); +jest.mock('@backstage/plugin-techdocs-react', () => ({ + ...jest.requireActual('@backstage/plugin-techdocs-react'), + useShadowDomStylesLoading: jest.fn().mockReturnValue(false), +})); import { TechDocsReaderPageContent } from './TechDocsReaderPageContent'; @@ -84,6 +88,10 @@ const Wrapper = ({ ); describe('', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + it('should render techdocs page content', async () => { getEntityMetadata.mockResolvedValue(mockEntityMetadata); getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); @@ -145,4 +153,61 @@ describe('', () => { ).toBeInTheDocument(); }); }); + + it('should scroll to hash if hash is present in url', async () => { + jest.spyOn(document, 'querySelector'); + + const mockScrollIntoView = jest.fn(); + const h2 = document.createElement('h2'); + h2.innerText = 'emojis'; + h2.id = 'emojis'; + h2.scrollIntoView = mockScrollIntoView; + const mockTechDocsPage = document.createElement('html'); + mockTechDocsPage.appendChild(h2); + + getEntityMetadata.mockResolvedValue(mockEntityMetadata); + getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); + useTechDocsReaderDom.mockReturnValue(mockTechDocsPage); + useReaderState.mockReturnValue({ state: 'cached' }); + + window.location.hash = '#emojis'; + + const rendered = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect( + rendered.getByTestId('techdocs-native-shadowroot'), + ).toBeInTheDocument(); + expect(mockScrollIntoView).toHaveBeenCalled(); + expect(document.querySelector).not.toHaveBeenCalledWith('header'); + }); + + window.location.hash = ''; + }); + + it('should scroll to header if hash is not present in url', async () => { + jest.spyOn(document, 'querySelector'); + + getEntityMetadata.mockResolvedValue(mockEntityMetadata); + getTechDocsMetadata.mockResolvedValue(mockTechDocsMetadata); + useTechDocsReaderDom.mockReturnValue(document.createElement('html')); + useReaderState.mockReturnValue({ state: 'cached' }); + + const rendered = await renderInTestApp( + + + , + ); + + await waitFor(() => { + expect( + rendered.getByTestId('techdocs-native-shadowroot'), + ).toBeInTheDocument(); + expect(document.querySelector).toHaveBeenCalledWith('header'); + }); + }); }); diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx index 1e5459fc9d..f4c5ad8745 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/TechDocsReaderPageContent.tsx @@ -14,12 +14,14 @@ * limitations under the License. */ -import React, { useCallback } from 'react'; +import React, { useCallback, useEffect } from 'react'; import { makeStyles, Grid } from '@material-ui/core'; import { TechDocsShadowDom, + useShadowDomStylesLoading, + useShadowRootElements, useTechDocsReaderPage, } from '@backstage/plugin-techdocs-react'; import { CompoundEntityRef } from '@backstage/catalog-model'; @@ -78,8 +80,23 @@ export const TechDocsReaderPageContent = withTechDocsReaderProvider( entityRef, setShadowRoot, } = useTechDocsReaderPage(); - const dom = useTechDocsReaderDom(entityRef); + const path = window.location.pathname; + const hash = window.location.hash; + const isStyleLoading = useShadowDomStylesLoading(dom); + const [hashElement] = useShadowRootElements([`[id="${hash.slice(1)}"]`]); + + useEffect(() => { + if (isStyleLoading) return; + + if (hash) { + if (hashElement) { + hashElement.scrollIntoView(); + } + } else { + document?.querySelector('header')?.scrollIntoView(); + } + }, [path, hash, hashElement, isStyleLoading]); const handleAppend = useCallback( (newShadowRoot: ShadowRoot) => { diff --git a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx index cc42c46b4e..29e6945fa7 100644 --- a/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx +++ b/plugins/techdocs/src/reader/components/TechDocsReaderPageContent/dom.tsx @@ -39,7 +39,6 @@ import { removeMkdocsHeader, rewriteDocLinks, simplifyMkdocsFooter, - scrollIntoAnchor, scrollIntoNavigation, transform as transformer, copyToClipboard, @@ -167,7 +166,6 @@ export const useTechDocsReaderDom = ( const postRender = useCallback( async (transformedElement: Element) => transformer(transformedElement, [ - scrollIntoAnchor(), scrollIntoNavigation(), copyToClipboard(theme), addLinkClickListener({ diff --git a/plugins/techdocs/src/reader/transformers/index.ts b/plugins/techdocs/src/reader/transformers/index.ts index ca17261572..7df98f1f26 100644 --- a/plugins/techdocs/src/reader/transformers/index.ts +++ b/plugins/techdocs/src/reader/transformers/index.ts @@ -25,6 +25,5 @@ export * from './copyToClipboard'; export * from './removeMkdocsHeader'; export * from './simplifyMkdocsFooter'; export * from './onCssReady'; -export * from './scrollIntoAnchor'; export * from './scrollIntoNavigation'; export * from './transformer'; diff --git a/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.test.ts b/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.test.ts deleted file mode 100644 index e222c24a4c..0000000000 --- a/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.test.ts +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { scrollIntoAnchor } from '../transformers'; -import { createTestShadowDom } from '../../test-utils'; -import { Transformer } from './transformer'; -import mkdocsIndex from '../../test-utils/fixtures/mkdocs-index'; -import { SHADOW_DOM_STYLE_LOAD_EVENT } from '@backstage/plugin-techdocs-react'; - -describe('scrollIntoAnchor', () => { - const scrollIntoView = jest.fn(); - let querySelectorSpy: jest.SpyInstance; - let addEventListenerSpy: jest.SpyInstance; - let removeEventListenerSpy: jest.SpyInstance; - const applySpies: Transformer = dom => { - querySelectorSpy = jest.spyOn(dom, 'querySelector'); - querySelectorSpy.mockReturnValue({ scrollIntoView }); - addEventListenerSpy = jest.spyOn(dom, 'addEventListener'); - removeEventListenerSpy = jest.spyOn(dom, 'removeEventListener'); - return dom; - }; - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('does nothing if there is no anchor element', async () => { - await createTestShadowDom(mkdocsIndex, { - preTransformers: [], - postTransformers: [applySpies, scrollIntoAnchor()], - }); - expect(querySelectorSpy).not.toHaveBeenCalled(); - expect(addEventListenerSpy).toHaveBeenCalled(); - expect(removeEventListenerSpy).toHaveBeenCalled(); - expect(addEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - expect(removeEventListenerSpy).toHaveBeenCalledTimes(1); - expect(removeEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - // check that the same function is passed to both addEventListener and removeEventListener - expect(addEventListenerSpy.mock.calls[0][1]).toBe( - removeEventListenerSpy.mock.calls[0][1], - ); - }); - - it('scroll to the hash anchor element', async () => { - window.location.hash = '#hash'; - await createTestShadowDom(mkdocsIndex, { - preTransformers: [], - postTransformers: [applySpies, scrollIntoAnchor()], - }); - expect(querySelectorSpy).toHaveBeenCalledWith( - expect.stringMatching('[id="hash"]'), - ); - expect(scrollIntoView).toHaveBeenCalledWith(); - expect(addEventListenerSpy).toHaveBeenCalledTimes(1); - expect(removeEventListenerSpy).toHaveBeenCalledTimes(1); - expect(addEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - expect(removeEventListenerSpy).toHaveBeenCalledTimes(1); - expect(removeEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - // check that the same function is passed to both addEventListener and removeEventListener - expect(addEventListenerSpy.mock.calls[0][1]).toBe( - removeEventListenerSpy.mock.calls[0][1], - ); - window.location.hash = ''; - }); - - it('works for anchor starting with number', async () => { - querySelectorSpy.mockReturnValue({ scrollIntoView }); - window.location.hash = '#1-hash'; - await createTestShadowDom(mkdocsIndex, { - preTransformers: [], - postTransformers: [applySpies, scrollIntoAnchor()], - }); - expect(querySelectorSpy).toHaveBeenCalledWith( - expect.stringMatching('[id="1-hash"]'), - ); - expect(scrollIntoView).toHaveBeenCalledWith(); - expect(addEventListenerSpy).toHaveBeenCalledTimes(1); - expect(addEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - expect(removeEventListenerSpy).toHaveBeenCalledTimes(1); - expect(removeEventListenerSpy).toHaveBeenCalledWith( - SHADOW_DOM_STYLE_LOAD_EVENT, - expect.any(Function), - ); - // check that the same function is passed to both addEventListener and removeEventListener - expect(addEventListenerSpy.mock.calls[0][1]).toBe( - removeEventListenerSpy.mock.calls[0][1], - ); - window.location.hash = ''; - }); -}); diff --git a/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.ts b/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.ts deleted file mode 100644 index 97a18b7241..0000000000 --- a/plugins/techdocs/src/reader/transformers/scrollIntoAnchor.ts +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import type { Transformer } from './transformer'; -import { SHADOW_DOM_STYLE_LOAD_EVENT } from '@backstage/plugin-techdocs-react'; - -export const scrollIntoAnchor = (): Transformer => { - return dom => { - dom.addEventListener( - SHADOW_DOM_STYLE_LOAD_EVENT, - function handleShadowDomStyleLoad() { - if (window.location.hash) { - const hash = window.location.hash.slice(1); - dom?.querySelector(`[id="${hash}"]`)?.scrollIntoView(); - } - dom.removeEventListener( - SHADOW_DOM_STYLE_LOAD_EVENT, - handleShadowDomStyleLoad, - ); - }, - ); - return dom; - }; -}; diff --git a/plugins/techdocs/src/setupTests.ts b/plugins/techdocs/src/setupTests.ts index 963c0f188b..6c7fc2d3e3 100644 --- a/plugins/techdocs/src/setupTests.ts +++ b/plugins/techdocs/src/setupTests.ts @@ -15,3 +15,5 @@ */ import '@testing-library/jest-dom'; + +Element.prototype.scrollIntoView = jest.fn(); diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index 6edbe92aa7..a1ed125e60 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-todo-backend +## 0.3.5-next.0 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-openapi-utils@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/integration@1.7.1 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-client@1.4.5 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/plugin-catalog-node@1.4.8-next.0 + ## 0.3.4 ### Patch Changes diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index d3296b70f0..853e6f20e5 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-todo-backend", "description": "A Backstage backend plugin that lets you browse TODO comments in your source code", - "version": "0.3.4", + "version": "0.3.5-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index b30c9d259e..11d42ec56a 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,19 @@ # @backstage/plugin-todo +## 0.2.30-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.2.28 ### Patch Changes diff --git a/plugins/todo/package.json b/plugins/todo/package.json index ec27528d72..af49ab02cf 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,15 +1,14 @@ { "name": "@backstage/plugin-todo", "description": "A Backstage plugin that lets you browse TODO comments in your source code", - "version": "0.2.28", + "version": "0.2.30-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { "access": "public", "main": "dist/index.esm.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "types": "dist/index.d.ts" }, "backstage": { "role": "frontend-plugin" @@ -22,7 +21,7 @@ }, "sideEffects": false, "scripts": { - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "start": "backstage-cli package start", "lint": "backstage-cli package lint", "test": "backstage-cli package test", diff --git a/plugins/user-settings-backend/CHANGELOG.md b/plugins/user-settings-backend/CHANGELOG.md index 53e8a6f30f..480574d082 100644 --- a/plugins/user-settings-backend/CHANGELOG.md +++ b/plugins/user-settings-backend/CHANGELOG.md @@ -1,5 +1,20 @@ # @backstage/plugin-user-settings-backend +## 0.2.6-next.0 + +### Patch Changes + +- dd0350379b: Added dependency on `@backstage/config` +- 8613ba3928: Switched to using `"exports"` field for `/alpha` subpath export. +- Updated dependencies + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + - @backstage/plugin-auth-node@0.4.1-next.0 + ## 0.2.4 ### Patch Changes diff --git a/plugins/user-settings-backend/alpha-api-report.md b/plugins/user-settings-backend/alpha-api-report.md new file mode 100644 index 0000000000..aa6b246616 --- /dev/null +++ b/plugins/user-settings-backend/alpha-api-report.md @@ -0,0 +1,13 @@ +## API Report File for "@backstage/plugin-user-settings-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'; + +// @alpha +const _default: () => BackendFeature; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/user-settings-backend/api-report.md b/plugins/user-settings-backend/api-report.md index 808bd0f29b..1bc37bbec7 100644 --- a/plugins/user-settings-backend/api-report.md +++ b/plugins/user-settings-backend/api-report.md @@ -3,7 +3,6 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { BackendFeature } from '@backstage/backend-plugin-api'; import express from 'express'; import { IdentityApi } from '@backstage/plugin-auth-node'; import { PluginDatabaseManager } from '@backstage/backend-common'; @@ -19,9 +18,5 @@ export interface RouterOptions { identity: IdentityApi; } -// @alpha -const userSettingsPlugin: () => BackendFeature; -export default userSettingsPlugin; - // (No @packageDocumentation comment for this package) ``` diff --git a/plugins/user-settings-backend/package.json b/plugins/user-settings-backend/package.json index c72f349301..4ea98078dd 100644 --- a/plugins/user-settings-backend/package.json +++ b/plugins/user-settings-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings-backend", "description": "The Backstage backend plugin to manage user settings", - "version": "0.2.4", + "version": "0.2.6-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -9,10 +9,22 @@ "role": "backend-plugin" }, "publishConfig": { - "access": "public", - "main": "dist/index.cjs.js", - "types": "dist/index.d.ts", - "alphaTypes": "dist/index.alpha.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "homepage": "https://backstage.io", "repository": { @@ -22,7 +34,7 @@ }, "scripts": { "start": "backstage-cli package start", - "build": "backstage-cli package build --experimental-type-build", + "build": "backstage-cli package build", "lint": "backstage-cli package lint", "test": "backstage-cli package test", "prepack": "backstage-cli package prepack", diff --git a/plugins/user-settings-backend/src/plugin.ts b/plugins/user-settings-backend/src/alpha.ts similarity index 95% rename from plugins/user-settings-backend/src/plugin.ts rename to plugins/user-settings-backend/src/alpha.ts index e200d22ffe..8404c4ae94 100644 --- a/plugins/user-settings-backend/src/plugin.ts +++ b/plugins/user-settings-backend/src/alpha.ts @@ -25,7 +25,7 @@ import { createRouter } from './service/router'; * * @alpha */ -export const userSettingsPlugin = createBackendPlugin({ +export default createBackendPlugin({ pluginId: 'userSettings', register(env) { env.registerInit({ diff --git a/plugins/user-settings-backend/src/index.ts b/plugins/user-settings-backend/src/index.ts index 14a9704e53..04d8accdaf 100644 --- a/plugins/user-settings-backend/src/index.ts +++ b/plugins/user-settings-backend/src/index.ts @@ -16,4 +16,3 @@ export * from './service'; export * from './database'; -export { userSettingsPlugin as default } from './plugin'; diff --git a/plugins/user-settings/CHANGELOG.md b/plugins/user-settings/CHANGELOG.md index 3353f23a79..6072182b2d 100644 --- a/plugins/user-settings/CHANGELOG.md +++ b/plugins/user-settings/CHANGELOG.md @@ -1,5 +1,21 @@ # @backstage/plugin-user-settings +## 0.7.12-next.0 + +### Patch Changes + +- 68fc9dc60e: Updated alpha exports according to routing changes in `@backstage/frontend-plugin-api`. +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/frontend-plugin-api@0.3.0-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/core-app-api@1.11.1-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + - @backstage/types@1.1.1 + ## 0.7.11 ### Patch Changes diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 264521ee45..873cc9bf2a 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-user-settings", "description": "A Backstage plugin that provides a settings page", - "version": "0.7.11", + "version": "0.7.12-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx index df1fabab0a..1be797980b 100644 --- a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.test.tsx @@ -45,7 +45,7 @@ describe('', () => { }, ); - expect(screen.getByText('user:default/test-ownership')).toBeInTheDocument(); - expect(screen.getByText('foo:bar/foobar')).toBeInTheDocument(); + expect(screen.getByText('test-ownership')).toBeInTheDocument(); + expect(screen.getByText('bar/foobar')).toBeInTheDocument(); }); }); diff --git a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx index a398877ce9..d8d3f677aa 100644 --- a/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsIdentityCard.tsx @@ -33,19 +33,13 @@ const Contents = () => { User Entity:{' '} - ref} - /> + Ownership Entities:{' '} - ref} - /> + diff --git a/plugins/vault-backend/CHANGELOG.md b/plugins/vault-backend/CHANGELOG.md index d623adcf33..9554b05272 100644 --- a/plugins/vault-backend/CHANGELOG.md +++ b/plugins/vault-backend/CHANGELOG.md @@ -1,5 +1,49 @@ # @backstage/plugin-vault-backend +## 0.4.0-next.0 + +### Minor Changes + +- a873a32a1f: Added support for the [new backend system](https://backstage.io/docs/backend-system/). + + In your `packages/backend/src/index.ts` make the following changes: + + ```diff + import { createBackend } from '@backstage/backend-defaults'; + const backend = createBackend(); + // ... other feature additions + + backend.add(import('@backstage/plugin-vault-backend'); + backend.start(); + ``` + + If you use the new backend system, the token renewal task can be defined via configuration file: + + ```diff + vault: + baseUrl: + token: + schedule: + + frequency: ... + + timeout: ... + + # Other schedule options, such as scope or initialDelay + ``` + + If the `schedule` is omitted or set to `false` no token renewal task will be scheduled. + If the value of `schedule` is set to `true` the renew will be scheduled hourly (the default). + In other cases (like in the diff above), the defined schedule will be used. + + **DEPRECATIONS**: The interface `VaultApi` and the type `VaultSecret` are now deprecated. Import them from `@backstage/plugin-vault-node`. + +### Patch Changes + +- Updated dependencies + - @backstage/plugin-vault-node@0.1.0-next.0 + - @backstage/backend-common@0.19.9-next.0 + - @backstage/backend-plugin-api@0.6.7-next.0 + - @backstage/backend-tasks@0.5.12-next.0 + - @backstage/config@1.1.1 + - @backstage/errors@1.2.3 + ## 0.3.11 ### Patch Changes diff --git a/plugins/vault-backend/README.md b/plugins/vault-backend/README.md index 8c70cfd24a..117bcaacbb 100644 --- a/plugins/vault-backend/README.md +++ b/plugins/vault-backend/README.md @@ -68,6 +68,9 @@ To get started, first you need a running instance of Vault. You can follow [this token: secretEngine: 'customSecretEngine' # Optional. By default it uses 'secrets'. Can be overwritten by the annotation of the entity kvVersion: # Optional. The K/V version that your instance is using. The available options are '1' or '2' + schedule: # Optional. If the token renewal is enabled this schedule will be used instead of the hourly one + frequency: { hours: 1 } + timeout: { hours: 1 } ``` 4. Get a `VAULT_TOKEN` with **LIST** permissions, as it's enough for the plugin. You can check [this tutorial](https://learn.hashicorp.com/tutorials/vault/tokens) for more info. @@ -80,6 +83,24 @@ To get started, first you need a running instance of Vault. You can follow [this } ``` +## New Backend System + +The Vault backend plugin has support for the [new backend system](https://backstage.io/docs/backend-system/), here's how you can set that up: + +In your `packages/backend/src/index.ts` make the following changes: + +```diff + import { createBackend } from '@backstage/backend-defaults'; + const backend = createBackend(); + // ... other feature additions ++ backend.add(import('@backstage/plugin-vault-backend'); + backend.start(); +``` + +The token renewal is enabled automatically in the new backend system depending on the `app-config.yaml`. If the `schedule` is not defined there, no +task will be executed. If you want to use the default renewal scheduler (which runs hourly), set `schedule: true`. In case you want a custom schedule +just use a configuration like the one set above. + ## Integration with the Catalog The plugin can be integrated into each Component in the catalog. To allow listing the available secrets a new annotation must be added to the `catalog-info.yaml`: @@ -122,7 +143,7 @@ That will overwrite the default secret engine from the configuration. ## Renew token -In a secure Vault instance, it's usual that the tokens are refreshed after some time. In order to always have a valid token to fetch the secrets, it might be necessary to execute a renew action after some time. By default this is deactivated, but it can be easily activated and configured to be executed periodically (hourly by default, but customizable by the user). In order to do that, modify your `src/plugins/vault.ts` file to look like this one: +In a secure Vault instance, it's usual that the tokens are refreshed after some time. In order to always have a valid token to fetch the secrets, it might be necessary to execute a renew action after some time. By default this is deactivated, but it can be easily activated and configured to be executed periodically (hourly by default, but customizable by the user within the app-config.yaml file). In order to do that, modify your `src/plugins/vault.ts` file to look like this one: ```typescript import { VaultBuilder } from '@backstage/plugin-vault-backend'; @@ -149,6 +170,8 @@ export default async function createPlugin( } ``` +If the `taskRunner` is not set when calling the `enableTokenRenew`, the plugin will automatically check what is set in the `app-config.yaml` file. Refer to [the new backend system setup](#new-backend-system) for more information about it. + ## Features - List the secrets present in a certain path diff --git a/plugins/vault-backend/api-report.md b/plugins/vault-backend/api-report.md index ebf9057b98..4002db5084 100644 --- a/plugins/vault-backend/api-report.md +++ b/plugins/vault-backend/api-report.md @@ -3,9 +3,11 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import express from 'express'; import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import { PluginTaskScheduler } from '@backstage/backend-tasks'; import { TaskRunner } from '@backstage/backend-tasks'; @@ -22,7 +24,7 @@ export interface RouterOptions { scheduler: PluginTaskScheduler; } -// @public +// @public @deprecated export interface VaultApi { getFrontendSecretsUrl(): string; listSecrets( @@ -69,12 +71,16 @@ export interface VaultEnvironment { // (undocumented) config: Config; // (undocumented) - logger: Logger; + logger: LoggerService; // (undocumented) scheduler: PluginTaskScheduler; } // @public +const vaultPlugin: () => BackendFeature; +export default vaultPlugin; + +// @public @deprecated export type VaultSecret = { name: string; path: string; diff --git a/plugins/vault-backend/config.d.ts b/plugins/vault-backend/config.d.ts index a30999b0cb..58369e638d 100644 --- a/plugins/vault-backend/config.d.ts +++ b/plugins/vault-backend/config.d.ts @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; /** Configuration for the Vault plugin */ export interface Config { @@ -44,5 +45,11 @@ export interface Config { * The version of the K/V API. Defaults to `2`. */ kvVersion?: 1 | 2; + + /** + * If set to true, the default schedule (hourly) will be used. If a + * different schedule is set, this will be used instead. + * */ + schedule?: TaskScheduleDefinitionConfig | boolean; }; } diff --git a/plugins/vault-backend/package.json b/plugins/vault-backend/package.json index 883bb5281b..7f9b6a5345 100644 --- a/plugins/vault-backend/package.json +++ b/plugins/vault-backend/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault-backend", "description": "A Backstage backend plugin that integrates towards Vault", - "version": "0.3.11", + "version": "0.4.0-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -34,9 +34,11 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/backend-tasks": "workspace:^", "@backstage/config": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/plugin-vault-node": "workspace:^", "@types/express": "*", "compression": "^1.7.4", "cors": "^2.8.5", diff --git a/plugins/vault-backend/src/index.ts b/plugins/vault-backend/src/index.ts index 4c74fd051f..c5fea00642 100644 --- a/plugins/vault-backend/src/index.ts +++ b/plugins/vault-backend/src/index.ts @@ -17,3 +17,4 @@ export * from './service/router'; export * from './service/VaultBuilder'; export * from './service/vaultApi'; +export { vaultPlugin as default } from './service/plugin'; diff --git a/plugins/vault-backend/src/service/VaultBuilder.ts b/plugins/vault-backend/src/service/VaultBuilder.ts index 9856476c20..471064392f 100644 --- a/plugins/vault-backend/src/service/VaultBuilder.ts +++ b/plugins/vault-backend/src/service/VaultBuilder.ts @@ -16,11 +16,17 @@ import { Config } from '@backstage/config'; import { InputError } from '@backstage/errors'; -import { Logger } from 'winston'; +import { LoggerService } from '@backstage/backend-plugin-api'; import express from 'express'; import Router from 'express-promise-router'; import { VaultApi, VaultClient } from './vaultApi'; -import { TaskRunner, PluginTaskScheduler } from '@backstage/backend-tasks'; +import { + PluginTaskScheduler, + TaskRunner, + TaskScheduleDefinition, + TaskScheduleDefinitionConfig, + readTaskScheduleDefinitionFromConfig, +} from '@backstage/backend-tasks'; import { errorHandler } from '@backstage/backend-common'; /** @@ -28,7 +34,7 @@ import { errorHandler } from '@backstage/backend-common'; * @public */ export interface VaultEnvironment { - logger: Logger; + logger: LoggerService; config: Config; scheduler: PluginTaskScheduler; } @@ -101,18 +107,16 @@ export class VaultBuilder { } /** - * Enables the token renewal for Vault. + * Enables the token renewal for Vault. The schedule if configured in the app-config.yaml file. + * If not set, the renewal is executed hourly * - * @param schedule - The TaskRunner used to schedule the renewal, if not set, renewing hourly * @returns */ public async enableTokenRenew(schedule?: TaskRunner) { const taskRunner = schedule ? schedule - : this.env.scheduler.createScheduledTaskRunner({ - frequency: { hours: 1 }, - timeout: { hours: 1 }, - }); + : this.env.scheduler.createScheduledTaskRunner(this.getConfigSchedule()); + await taskRunner.run({ id: 'refresh-vault-token', fn: async () => { @@ -124,6 +128,24 @@ export class VaultBuilder { return this; } + private getConfigSchedule(): TaskScheduleDefinition { + const schedule = this.env.config.getOptional< + TaskScheduleDefinitionConfig | boolean + >('vault.schedule'); + + const scheduleCfg = + schedule !== undefined && schedule !== false + ? { + frequency: { hours: 1 }, + timeout: { hours: 1 }, + } + : readTaskScheduleDefinitionFromConfig( + this.env.config.getConfig('vault.schedule'), + ); + + return scheduleCfg; + } + /** * Builds the backend routes for Vault. * diff --git a/plugins/vault-backend/src/service/plugin.test.tsx b/plugins/vault-backend/src/service/plugin.test.tsx new file mode 100644 index 0000000000..cc9fdaa0a7 --- /dev/null +++ b/plugins/vault-backend/src/service/plugin.test.tsx @@ -0,0 +1,22 @@ +/* + * Copyright 2021 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { vaultPlugin } from './plugin'; + +describe('vault', () => { + it('should export the vault plugin', () => { + expect(vaultPlugin).toBeDefined(); + }); +}); diff --git a/plugins/vault-backend/src/service/plugin.ts b/plugins/vault-backend/src/service/plugin.ts new file mode 100644 index 0000000000..cf9d2cf87b --- /dev/null +++ b/plugins/vault-backend/src/service/plugin.ts @@ -0,0 +1,74 @@ +/* + * 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 { VaultApi, vaultExtensionPoint } from '@backstage/plugin-vault-node'; +import { VaultBuilder } from './VaultBuilder'; +import { TaskScheduleDefinitionConfig } from '@backstage/backend-tasks'; + +/** + * Vault backend plugin + * + * @public + */ +export const vaultPlugin = createBackendPlugin({ + pluginId: 'vault', + register(env) { + let client: VaultApi | undefined; + + env.registerExtensionPoint(vaultExtensionPoint, { + setClient(vaultClient) { + if (client) { + throw new Error('The vault client has been already set'); + } + client = vaultClient; + }, + }); + + env.registerInit({ + deps: { + logger: coreServices.logger, + config: coreServices.rootConfig, + scheduler: coreServices.scheduler, + httpRouter: coreServices.httpRouter, + }, + async init({ logger, config, scheduler, httpRouter }) { + let builder = VaultBuilder.createBuilder({ + logger, + config, + scheduler, + }); + + if (client) { + builder = builder.setVaultClient(client); + } + + const scheduleCfg = config.getOptional< + boolean | TaskScheduleDefinitionConfig + >('vault.schedule'); + if (scheduleCfg !== undefined && scheduleCfg !== false) { + builder = await builder.enableTokenRenew(); + } + + const { router } = builder.build(); + httpRouter.use(router); + }, + }); + }, +}); diff --git a/plugins/vault-backend/src/service/vaultApi.ts b/plugins/vault-backend/src/service/vaultApi.ts index 7c3ccc1d00..789cdeeda5 100644 --- a/plugins/vault-backend/src/service/vaultApi.ts +++ b/plugins/vault-backend/src/service/vaultApi.ts @@ -33,6 +33,7 @@ export type VaultSecretList = { /** * Object containing the secret name and some links * @public + * @deprecated Use the interface from `@backstage/plugin-vault-node` */ export type VaultSecret = { name: string; @@ -53,6 +54,7 @@ type RenewTokenResponse = { /** * Interface for the Vault API * @public + * @deprecated Use the interface from `@backstage/plugin-vault-node` */ export interface VaultApi { /** diff --git a/plugins/vault-node/.eslintrc.js b/plugins/vault-node/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/vault-node/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/vault-node/CHANGELOG.md b/plugins/vault-node/CHANGELOG.md new file mode 100644 index 0000000000..696347e9fc --- /dev/null +++ b/plugins/vault-node/CHANGELOG.md @@ -0,0 +1,13 @@ +# @backstage/plugin-vault-node + +## 0.1.0-next.0 + +### Minor Changes + +- 7a41bcf2af: Initial version of the `plugin-vault-node`` package. It contains the extension point definitions + for the vault backend, as well as some types that will be deprecated in the backend plugin. + +### Patch Changes + +- Updated dependencies + - @backstage/backend-plugin-api@0.6.7-next.0 diff --git a/plugins/vault-node/README.md b/plugins/vault-node/README.md new file mode 100644 index 0000000000..5f777e792b --- /dev/null +++ b/plugins/vault-node/README.md @@ -0,0 +1,3 @@ +# @backstage/plugin-vault-node + +Houses types and utilities for building the vault modules diff --git a/plugins/vault-node/api-report.md b/plugins/vault-node/api-report.md new file mode 100644 index 0000000000..d0e0a29f8f --- /dev/null +++ b/plugins/vault-node/api-report.md @@ -0,0 +1,38 @@ +## API Report File for "@backstage/plugin-vault-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ExtensionPoint } from '@backstage/backend-plugin-api'; + +// @public +export interface VaultApi { + getFrontendSecretsUrl(): string; + listSecrets( + secretPath: string, + options?: { + secretEngine?: string; + }, + ): Promise; + renewToken?(): Promise; +} + +// @public +export interface VaultExtensionPoint { + // (undocumented) + setClient(vaultClient: VaultApi): void; +} + +// @public +export const vaultExtensionPoint: ExtensionPoint; + +// @public +export type VaultSecret = { + name: string; + path: string; + showUrl: string; + editUrl: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/vault-node/catalog-info.yaml b/plugins/vault-node/catalog-info.yaml new file mode 100644 index 0000000000..6b6c8b84ad --- /dev/null +++ b/plugins/vault-node/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-vault-node + title: '@backstage/plugin-vault-node' + description: Node.js library for the vault plugin +spec: + lifecycle: experimental + type: backstage-node-library + owner: maintainers diff --git a/plugins/vault-node/package.json b/plugins/vault-node/package.json new file mode 100644 index 0000000000..41eb2d8242 --- /dev/null +++ b/plugins/vault-node/package.json @@ -0,0 +1,33 @@ +{ + "name": "@backstage/plugin-vault-node", + "description": "Node.js library for the vault plugin", + "version": "0.1.0-next.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "node-library" + }, + "scripts": { + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^" + }, + "devDependencies": { + "@backstage/cli": "workspace:^" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/vault-node/src/api/index.ts b/plugins/vault-node/src/api/index.ts new file mode 100644 index 0000000000..92cb965d75 --- /dev/null +++ b/plugins/vault-node/src/api/index.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export * from './types'; diff --git a/plugins/vault-node/src/api/types.ts b/plugins/vault-node/src/api/types.ts new file mode 100644 index 0000000000..9969fb001b --- /dev/null +++ b/plugins/vault-node/src/api/types.ts @@ -0,0 +1,55 @@ +/* + * 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. + */ + +/** + * Object containing the secret name and some links + * @public + */ +export type VaultSecret = { + name: string; + path: string; + showUrl: string; + editUrl: string; +}; + +/** + * Interface for the Vault API + * @public + */ +export interface VaultApi { + /** + * Returns the URL to access the Vault UI with the defined config. + */ + getFrontendSecretsUrl(): string; + + /** + * Returns a list of secrets used to show in a table. + * @param secretPath - The path where the secrets are stored in Vault + * @param options - Additional options to be passed to the Vault API, allows to override vault default settings in app config file + */ + listSecrets( + secretPath: string, + options?: { + secretEngine?: string; + }, + ): Promise; + + /** + * Optional, to renew the token used to list the secrets. Throws an + * error if the token renewal went wrong. + */ + renewToken?(): Promise; +} diff --git a/plugins/vault-node/src/extensions.ts b/plugins/vault-node/src/extensions.ts new file mode 100644 index 0000000000..efe4ba6223 --- /dev/null +++ b/plugins/vault-node/src/extensions.ts @@ -0,0 +1,36 @@ +/* + * 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 { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { VaultApi } from './api'; + +/** + * Extension point for vault. + * + * @public + */ +export interface VaultExtensionPoint { + setClient(vaultClient: VaultApi): void; +} + +/** + * Extension point for vault. + * + * @public + */ +export const vaultExtensionPoint = createExtensionPoint({ + id: 'vault.configuration', +}); diff --git a/plugins/vault-node/src/index.ts b/plugins/vault-node/src/index.ts new file mode 100644 index 0000000000..070f2cf64d --- /dev/null +++ b/plugins/vault-node/src/index.ts @@ -0,0 +1,17 @@ +/* + * 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. + */ +export { type VaultExtensionPoint, vaultExtensionPoint } from './extensions'; +export type { VaultApi, VaultSecret } from './api'; diff --git a/plugins/vault-node/src/setupTests.ts b/plugins/vault-node/src/setupTests.ts new file mode 100644 index 0000000000..4b9026cde5 --- /dev/null +++ b/plugins/vault-node/src/setupTests.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +export {}; diff --git a/plugins/vault/CHANGELOG.md b/plugins/vault/CHANGELOG.md index 37ed1eb14e..ce4fff2c41 100644 --- a/plugins/vault/CHANGELOG.md +++ b/plugins/vault/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-vault +## 0.1.21-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/plugin-catalog-react@1.9.0-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/catalog-model@1.4.3 + - @backstage/errors@1.2.3 + ## 0.1.20 ### Patch Changes diff --git a/plugins/vault/package.json b/plugins/vault/package.json index 055d38073a..d1c23fae1c 100644 --- a/plugins/vault/package.json +++ b/plugins/vault/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-vault", "description": "A Backstage plugin that integrates towards Vault", - "version": "0.1.20", + "version": "0.1.21-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/vault/src/api.test.ts b/plugins/vault/src/api.test.ts index eb122a92d1..7ecd09b6f4 100644 --- a/plugins/vault/src/api.test.ts +++ b/plugins/vault/src/api.test.ts @@ -68,8 +68,6 @@ describe('api', () => { }; beforeEach(() => { - jest.resetAllMocks(); - setupHandlers(); api = new VaultClient({ discoveryApi, fetchApi }); diff --git a/plugins/xcmetrics/CHANGELOG.md b/plugins/xcmetrics/CHANGELOG.md index 9682aa6411..b47889abf8 100644 --- a/plugins/xcmetrics/CHANGELOG.md +++ b/plugins/xcmetrics/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-xcmetrics +## 0.2.45-next.0 + +### Patch Changes + +- 6c2b872153: Add official support for React 18. +- Updated dependencies + - @backstage/core-components@0.13.7-next.0 + - @backstage/core-plugin-api@1.8.0-next.0 + - @backstage/theme@0.4.4-next.0 + - @backstage/errors@1.2.3 + ## 0.2.44 ### Patch Changes diff --git a/plugins/xcmetrics/package.json b/plugins/xcmetrics/package.json index 97887eb2ea..7aeb570996 100644 --- a/plugins/xcmetrics/package.json +++ b/plugins/xcmetrics/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-xcmetrics", "description": "A Backstage plugin that shows XCode build metrics for your components", - "version": "0.2.44", + "version": "0.2.45-next.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/xcmetrics/src/components/Accordion/Accordion.tsx b/plugins/xcmetrics/src/components/Accordion/Accordion.tsx index 2e2100a816..5c418de496 100644 --- a/plugins/xcmetrics/src/components/Accordion/Accordion.tsx +++ b/plugins/xcmetrics/src/components/Accordion/Accordion.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { Accordion as MuiAccordion, AccordionSummary as MuiAccordionSummary, @@ -23,9 +24,8 @@ import { } from '@material-ui/core'; import React, { PropsWithChildren } from 'react'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; -import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ heading: { flexBasis: '33.33%', diff --git a/plugins/xcmetrics/src/components/BuildDetails/BuildDetails.tsx b/plugins/xcmetrics/src/components/BuildDetails/BuildDetails.tsx index 949f430ce2..86db3be858 100644 --- a/plugins/xcmetrics/src/components/BuildDetails/BuildDetails.tsx +++ b/plugins/xcmetrics/src/components/BuildDetails/BuildDetails.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import { createStyles, Divider, Grid, makeStyles } from '@material-ui/core'; import React from 'react'; import { BuildResponse, xcmetricsApiRef } from '../../api'; @@ -22,12 +23,11 @@ import useAsync from 'react-use/lib/useAsync'; import { useApi } from '@backstage/core-plugin-api'; import { formatDuration, formatStatus, formatTime } from '../../utils'; import { StatusIcon } from '../StatusIcon'; -import { BackstageTheme } from '@backstage/theme'; import { Accordion } from '../Accordion'; import { BuildTimeline } from '../BuildTimeline'; import { PreformattedText } from '../PreformattedText'; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ divider: { marginTop: theme.spacing(2), diff --git a/plugins/xcmetrics/src/components/BuildList/BuildList.tsx b/plugins/xcmetrics/src/components/BuildList/BuildList.tsx index 20924b181b..85eee6009d 100644 --- a/plugins/xcmetrics/src/components/BuildList/BuildList.tsx +++ b/plugins/xcmetrics/src/components/BuildList/BuildList.tsx @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + import React, { useRef, useState } from 'react'; import { Table } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; @@ -22,9 +23,8 @@ import { BuildListFilter as Filters } from '../BuildListFilter'; import { DateTime } from 'luxon'; import { buildPageColumns } from '../BuildTableColumns'; import { BuildDetails, withRequest } from '../BuildDetails'; -import { BackstageTheme } from '@backstage/theme'; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ detailPanel: { padding: theme.spacing(2), diff --git a/plugins/xcmetrics/src/components/BuildTimeline/BuildTimeline.tsx b/plugins/xcmetrics/src/components/BuildTimeline/BuildTimeline.tsx index 54d84ff6c6..5c0a1b1e67 100644 --- a/plugins/xcmetrics/src/components/BuildTimeline/BuildTimeline.tsx +++ b/plugins/xcmetrics/src/components/BuildTimeline/BuildTimeline.tsx @@ -27,13 +27,12 @@ import { XAxis, YAxis, } from 'recharts'; -import { BackstageTheme } from '@backstage/theme'; import { Target } from '../../api'; import { formatSecondsInterval, formatPercentage } from '../../utils'; const EMPTY_HEIGHT = 100; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ toolTip: { backgroundColor: theme.palette.background.paper, diff --git a/plugins/xcmetrics/src/components/PreformattedText/PreformattedText.tsx b/plugins/xcmetrics/src/components/PreformattedText/PreformattedText.tsx index c2c3f8ce25..2b6a77cc9d 100644 --- a/plugins/xcmetrics/src/components/PreformattedText/PreformattedText.tsx +++ b/plugins/xcmetrics/src/components/PreformattedText/PreformattedText.tsx @@ -26,10 +26,9 @@ import { } from '@material-ui/core'; import CloseIcon from '@material-ui/icons/Close'; import React, { useState } from 'react'; -import { BackstageTheme } from '@backstage/theme'; import { cn } from '../../utils'; -const useStyles = makeStyles((theme: BackstageTheme) => +const useStyles = makeStyles(theme => createStyles({ pre: { whiteSpace: 'pre-line', diff --git a/scripts/verify-links.js b/scripts/verify-links.js index 7664cadad5..b05dd5eead 100755 --- a/scripts/verify-links.js +++ b/scripts/verify-links.js @@ -77,6 +77,10 @@ async function verifyUrl(basePath, absUrl, docPages) { return undefined; } + if (basePath.startsWith('.changeset/')) { + return { url, basePath, problem: 'out-of-changeset' }; + } + let path = ''; if (url.startsWith('/')) { @@ -195,6 +199,10 @@ async function main() { '', )}`, ); + } else if (problem === 'out-of-changeset') { + console.error('Links in changesets must use absolute URLs'); + console.error(` From: ${basePath}`); + console.error(` To: ${url}`); } else if (problem === 'doc-missing') { const suggestion = docPages.get(url) || diff --git a/storybook/yarn.lock b/storybook/yarn.lock index 00ec6908f0..fe2d5b27ff 100644 --- a/storybook/yarn.lock +++ b/storybook/yarn.lock @@ -24,6 +24,16 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.22.13": + version: 7.22.13 + resolution: "@babel/code-frame@npm:7.22.13" + dependencies: + "@babel/highlight": ^7.22.13 + chalk: ^2.4.2 + checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.17.7, @babel/compat-data@npm:^7.18.8": version: 7.18.8 resolution: "@babel/compat-data@npm:7.18.8" @@ -89,6 +99,18 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/generator@npm:7.23.0" + dependencies: + "@babel/types": ^7.23.0 + "@jridgewell/gen-mapping": ^0.3.2 + "@jridgewell/trace-mapping": ^0.3.17 + jsesc: ^2.5.1 + checksum: 8efe24adad34300f1f8ea2add420b28171a646edc70f2a1b3e1683842f23b8b7ffa7e35ef0119294e1901f45bfea5b3dc70abe1f10a1917ccdfb41bed69be5f1 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-annotate-as-pure@npm:7.18.6" @@ -192,6 +214,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-environment-visitor@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-environment-visitor@npm:7.22.20" + checksum: d80ee98ff66f41e233f36ca1921774c37e88a803b2f7dca3db7c057a5fea0473804db9fb6729e5dbfd07f4bed722d60f7852035c2c739382e84c335661590b69 + languageName: node + linkType: hard + "@babel/helper-explode-assignable-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-explode-assignable-expression@npm:7.18.6" @@ -211,6 +240,16 @@ __metadata: languageName: node linkType: hard +"@babel/helper-function-name@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-function-name@npm:7.23.0" + dependencies: + "@babel/template": ^7.22.15 + "@babel/types": ^7.23.0 + checksum: e44542257b2d4634a1f979244eb2a4ad8e6d75eb6761b4cfceb56b562f7db150d134bc538c8e6adca3783e3bc31be949071527aa8e3aab7867d1ad2d84a26e10 + languageName: node + linkType: hard + "@babel/helper-hoist-variables@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-hoist-variables@npm:7.18.6" @@ -220,6 +259,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-hoist-variables@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-hoist-variables@npm:7.22.5" + dependencies: + "@babel/types": ^7.22.5 + checksum: 394ca191b4ac908a76e7c50ab52102669efe3a1c277033e49467913c7ed6f7c64d7eacbeabf3bed39ea1f41731e22993f763b1edce0f74ff8563fd1f380d92cc + languageName: node + linkType: hard + "@babel/helper-member-expression-to-functions@npm:^7.18.9": version: 7.18.9 resolution: "@babel/helper-member-expression-to-functions@npm:7.18.9" @@ -331,6 +379,15 @@ __metadata: languageName: node linkType: hard +"@babel/helper-split-export-declaration@npm:^7.22.6": + version: 7.22.6 + resolution: "@babel/helper-split-export-declaration@npm:7.22.6" + dependencies: + "@babel/types": ^7.22.5 + checksum: e141cace583b19d9195f9c2b8e17a3ae913b7ee9b8120246d0f9ca349ca6f03cb2c001fd5ec57488c544347c0bb584afec66c936511e447fd20a360e591ac921 + languageName: node + linkType: hard + "@babel/helper-string-parser@npm:^7.19.4": version: 7.19.4 resolution: "@babel/helper-string-parser@npm:7.19.4" @@ -338,6 +395,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-string-parser@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/helper-string-parser@npm:7.22.5" + checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 + languageName: node + linkType: hard + "@babel/helper-validator-identifier@npm:^7.18.6, @babel/helper-validator-identifier@npm:^7.19.1": version: 7.19.1 resolution: "@babel/helper-validator-identifier@npm:7.19.1" @@ -345,6 +409,13 @@ __metadata: languageName: node linkType: hard +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc + languageName: node + linkType: hard + "@babel/helper-validator-option@npm:^7.18.6": version: 7.18.6 resolution: "@babel/helper-validator-option@npm:7.18.6" @@ -386,7 +457,18 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.18.10, @babel/parser@npm:^7.18.11": +"@babel/highlight@npm:^7.22.13": + version: 7.22.20 + resolution: "@babel/highlight@npm:7.22.20" + dependencies: + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 + js-tokens: ^4.0.0 + checksum: 84bd034dca309a5e680083cd827a766780ca63cef37308404f17653d32366ea76262bd2364b2d38776232f2d01b649f26721417d507e8b4b6da3e4e739f6d134 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.12.11, @babel/parser@npm:^7.12.7, @babel/parser@npm:^7.18.10": version: 7.21.4 resolution: "@babel/parser@npm:7.21.4" bin: @@ -395,6 +477,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/parser@npm:7.23.0" + bin: + parser: ./bin/babel-parser.js + checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 + languageName: node + linkType: hard + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.18.6": version: 7.18.6 resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.18.6" @@ -1510,21 +1601,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.18.10, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.9": - version: 7.18.11 - resolution: "@babel/traverse@npm:7.18.11" +"@babel/template@npm:^7.22.15": + version: 7.22.15 + resolution: "@babel/template@npm:7.22.15" dependencies: - "@babel/code-frame": ^7.18.6 - "@babel/generator": ^7.18.10 - "@babel/helper-environment-visitor": ^7.18.9 - "@babel/helper-function-name": ^7.18.9 - "@babel/helper-hoist-variables": ^7.18.6 - "@babel/helper-split-export-declaration": ^7.18.6 - "@babel/parser": ^7.18.11 - "@babel/types": ^7.18.10 + "@babel/code-frame": ^7.22.13 + "@babel/parser": ^7.22.15 + "@babel/types": ^7.22.15 + checksum: 1f3e7dcd6c44f5904c184b3f7fe280394b191f2fed819919ffa1e529c259d5b197da8981b6ca491c235aee8dbad4a50b7e31304aa531271cb823a4a24a0dd8fd + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.1.6, @babel/traverse@npm:^7.12.11, @babel/traverse@npm:^7.12.9, @babel/traverse@npm:^7.13.0, @babel/traverse@npm:^7.18.10, @babel/traverse@npm:^7.18.11, @babel/traverse@npm:^7.18.9": + version: 7.23.2 + resolution: "@babel/traverse@npm:7.23.2" + dependencies: + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.23.0 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-function-name": ^7.23.0 + "@babel/helper-hoist-variables": ^7.22.5 + "@babel/helper-split-export-declaration": ^7.22.6 + "@babel/parser": ^7.23.0 + "@babel/types": ^7.23.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 727409464d5cf27f33555010098ce9bb435f0648cc76e674f4fb7513522356655ba62be99c8df330982b391ccf5f0c0c23c7bd7453d4936d47e2181693fed14c + checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d languageName: node linkType: hard @@ -1539,6 +1641,17 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/types@npm:7.23.0" + dependencies: + "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 + to-fast-properties: ^2.0.0 + checksum: 215fe04bd7feef79eeb4d33374b39909ce9cad1611c4135a4f7fdf41fe3280594105af6d7094354751514625ea92d0875aba355f53e86a92600f290e77b0e604 + languageName: node + linkType: hard + "@base2/pretty-print-object@npm:1.0.1": version: 1.0.1 resolution: "@base2/pretty-print-object@npm:1.0.1" @@ -2840,90 +2953,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-arm64@npm:1.3.92" +"@swc/core-darwin-arm64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-arm64@npm:1.3.95" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-x64@npm:1.3.92" +"@swc/core-darwin-x64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-x64@npm:1.3.95" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.92" +"@swc/core-linux-arm-gnueabihf@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.95" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.92" +"@swc/core-linux-arm64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.92" +"@swc/core-linux-arm64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.92" +"@swc/core-linux-x64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-musl@npm:1.3.92" +"@swc/core-linux-x64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-musl@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.92" +"@swc/core-win32-arm64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.95" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.92" +"@swc/core-win32-ia32-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.95" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.92" +"@swc/core-win32-x64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.95" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.92 - resolution: "@swc/core@npm:1.3.92" + version: 1.3.95 + resolution: "@swc/core@npm:1.3.95" dependencies: - "@swc/core-darwin-arm64": 1.3.92 - "@swc/core-darwin-x64": 1.3.92 - "@swc/core-linux-arm-gnueabihf": 1.3.92 - "@swc/core-linux-arm64-gnu": 1.3.92 - "@swc/core-linux-arm64-musl": 1.3.92 - "@swc/core-linux-x64-gnu": 1.3.92 - "@swc/core-linux-x64-musl": 1.3.92 - "@swc/core-win32-arm64-msvc": 1.3.92 - "@swc/core-win32-ia32-msvc": 1.3.92 - "@swc/core-win32-x64-msvc": 1.3.92 + "@swc/core-darwin-arm64": 1.3.95 + "@swc/core-darwin-x64": 1.3.95 + "@swc/core-linux-arm-gnueabihf": 1.3.95 + "@swc/core-linux-arm64-gnu": 1.3.95 + "@swc/core-linux-arm64-musl": 1.3.95 + "@swc/core-linux-x64-gnu": 1.3.95 + "@swc/core-linux-x64-musl": 1.3.95 + "@swc/core-win32-arm64-msvc": 1.3.95 + "@swc/core-win32-ia32-msvc": 1.3.95 + "@swc/core-win32-x64-msvc": 1.3.95 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -2952,7 +3065,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 88c0c62ff790e896180862c341be8bae98baf0a5c5e87f2f04f49e14b8c4fba460d6b352618b4dda066c8ae6bf152cd843eab25837f38128175208b8c0635721 + checksum: 49856ad64fa16151f7f784956c8134060c5ed612c9b3fcb79e33676c77219f61fa3bb6b9e0dd15d78c7bff92cacc0c944d2b4e60849eb93caca917a8544b1c2c languageName: node linkType: hard @@ -4418,7 +4531,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0, chalk@npm:^2.4.1": +"chalk@npm:^2.0.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: diff --git a/yarn.lock b/yarn.lock index 059c18d859..2d71bc767b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,6 +40,18 @@ __metadata: languageName: node linkType: hard +"@apidevtools/json-schema-ref-parser@npm:9.0.9": + version: 9.0.9 + resolution: "@apidevtools/json-schema-ref-parser@npm:9.0.9" + dependencies: + "@jsdevtools/ono": ^7.1.3 + "@types/json-schema": ^7.0.6 + call-me-maybe: ^1.0.1 + js-yaml: ^4.1.0 + checksum: b21f6bdd37d2942c3967ee77569bc74fadd1b922f688daf5ef85057789a2c3a7f4afc473aa2f3a93ec950dabb6ef365f8bd9cf51e4e062a1ee1e59b989f8f9b4 + languageName: node + linkType: hard + "@apidevtools/json-schema-ref-parser@npm:^9.0.6, @apidevtools/json-schema-ref-parser@npm:^9.1.2": version: 9.1.2 resolution: "@apidevtools/json-schema-ref-parser@npm:9.1.2" @@ -570,457 +582,481 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.405.0" +"@aws-sdk/client-cognito-identity@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 7cae774ec605939c0bb629b7dcb3155fe998250550d8e4f5696e546fd9debba7cb749556c54787e223613b586c06cec7138f4a9a7718de1df8a5abb43556d1a8 + checksum: a31d0d1ac82937991f82fe15b632751a1c8792cf4fb9497c84553149bd179014e897be7714675095e8f04ab462bcfe18d9bc46ee47d46d0b92266a536a5b7fd6 languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/client-eks@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/client-eks@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 - "@smithy/util-waiter": ^2.0.5 + "@smithy/util-waiter": ^2.0.12 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 166feb777e7bd4f1e7d78201b0f0402f0c2afb8e0f35e32576bd22eeb21bd3a2dda504d7c10a502dd64ddce79ff4f76df971a26fd5944dfe42770b962a242c59 + checksum: c2b121afa0d4a8551f9638fd3b4ad1cb534435b8e0051243129a34cded19a530313e84358910b63d412428f5c55dc7eec810fd801a5f713ec267811f2736b538 languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/client-organizations@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/client-organizations@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 9abdf6593730942a3f97b77ab00b1f52b78c6acb695b34e59fcffb03d92b5e66dae9b7c4005660a4189851d40b66c7845bce1be81be4329262d9977d01c7dcc9 + checksum: 838b60665b7f7f808cf7b901cdeaab611d285f640ddc69e563e9d0cc0911fe188308138f3f2f7b29762104dbd9d7fbdbb9f533ada2d79d0d023c29f5605319c8 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/client-s3@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/client-s3@npm:3.433.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-bucket-endpoint": 3.405.0 - "@aws-sdk/middleware-expect-continue": 3.398.0 - "@aws-sdk/middleware-flexible-checksums": 3.400.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-location-constraint": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-sdk-s3": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-ssec": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/signature-v4-multi-region": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-bucket-endpoint": 3.433.0 + "@aws-sdk/middleware-expect-continue": 3.433.0 + "@aws-sdk/middleware-flexible-checksums": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-location-constraint": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-sdk-s3": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-ssec": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/signature-v4-multi-region": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 "@aws-sdk/xml-builder": 3.310.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/eventstream-serde-browser": ^2.0.5 - "@smithy/eventstream-serde-config-resolver": ^2.0.5 - "@smithy/eventstream-serde-node": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-blob-browser": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/hash-stream-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/md5-js": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@smithy/config-resolver": ^2.0.16 + "@smithy/eventstream-serde-browser": ^2.0.12 + "@smithy/eventstream-serde-config-resolver": ^2.0.12 + "@smithy/eventstream-serde-node": ^2.0.12 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-blob-browser": ^2.0.12 + "@smithy/hash-node": ^2.0.12 + "@smithy/hash-stream-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/md5-js": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 - "@smithy/util-stream": ^2.0.5 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 + "@smithy/util-stream": ^2.0.17 "@smithy/util-utf8": ^2.0.0 - "@smithy/util-waiter": ^2.0.5 + "@smithy/util-waiter": ^2.0.12 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: e9665537bcd809d9f8b71d45ce4971618017db35d6074a01e89b96d19a5b2e76e9d8e0005f7f9bf898c1556d1d20125dae38bcfc5609ce11f9e418d03e216d76 + checksum: 0974b71e65ab8bef0229cd3c4ae896fd74596385231df1c5a3f884cfafbb1b2b8a4e570a1ef873323cb3f277807855555eeaa8fcd61ba96f7235c4f76bca94e1 languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/client-sqs@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/client-sqs@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-sdk-sqs": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/md5-js": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-sdk-sqs": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/md5-js": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 7618b9471cb02a47f5c7a6e1177f502712a6e532d76b814d1203989bd70a2f5e3c518e9927207fbf861b2703f241beec1f3f03e2717001245f9b456884c5b963 + checksum: fa680ff42ad1d5b3d475882f3ce482348c38cfb41fd13b10fab178cfd7e96ee0e1558f3b2270eafb7379a5cf23d46209e8851fbb502e374bbe71014f9b4f9e92 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/client-sso@npm:3.405.0" +"@aws-sdk/client-sso@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/client-sso@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: ec4fd5cf3d76ef5b8fdd815c58070a924c98fffb7b8228d409d009ce9596851bd54aa7d1f9f3528972f377c596363243a2d3fed5fc62d336751b99bebfd0363f + checksum: c8cc2e0e691394e0b70dfb556f52546646da70d692209402ab44ef5cefb2f5a6cf819bf6fefd4f91d756c5a81d5d2a825f9dee9faadf8eb1a634940b34511bc6 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.405.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/client-sts@npm:3.405.0" +"@aws-sdk/client-sts@npm:3.433.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.433.0 + resolution: "@aws-sdk/client-sts@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-sdk-sts": 3.398.0 - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-sdk-sts": 3.433.0 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/region-config-resolver": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: f835c95a43100963afe3aea2afb0c0a124191fb9b6cd373a1b61f98287f8637efa08d2caeadb05cf5f85843a4397933e05fe8c63a1d39d0b4829341f972c41bc + checksum: 2c87fa62bf0491bef6ece8acb6fa496b329a77b9368dbf6187dc55192207c25570b8d5be5e01d62a8b5fd0776cb4a3c620ada2aa9819cf43d0c3dcde8c8f7427 languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.405.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.433.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.405.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/client-cognito-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 4deb3d0f401b2365f190f45a2cd68cc81db66b4c4f8ff8b3ae34e526611c272c4308b0020faaf16ad8e9f1728222271959c14643d0ae04713b495795a8fae8b6 + checksum: fc7f1810f004f780cd394ac20d1e20a01c7b1de09f84bf9bdfe69f868997b47cbee3e5142a8c672dfb7c7032d2fafbd4338b76bce95c50d521b5dbce95743d39 languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.398.0" +"@aws-sdk/credential-provider-env@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 00c3fe0dbb0f58e6c769e447f5ae960e7d30fa9c6e5ed392ffbbe87356c526920e2ea18759a0a0cc2941109007ca950b43cfbb1764b2a1f076b4385333de89e5 + checksum: bc8d2afb35245d1c4aea85d0a2fb56ab85b7a48ddf92d90fc7351c871e8fb90622d6662e066a0a0cf6f493a94f8aba24061f663450bafeec6a70cd6e6af07e29 languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.405.0" +"@aws-sdk/credential-provider-http@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.433.0" dependencies: - "@aws-sdk/credential-provider-env": 3.398.0 - "@aws-sdk/credential-provider-process": 3.405.0 - "@aws-sdk/credential-provider-sso": 3.405.0 - "@aws-sdk/credential-provider-web-identity": 3.398.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/property-provider": ^2.0.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 3ccbc90394efe98a7767cde36a674fc3ce16063062516c5d6cf293cd99acfcc2f6751ffbebcbf971106e66e96bf1e26bc843803eccdebc0db77297c209b2f9b3 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.433.0" + dependencies: + "@aws-sdk/credential-provider-env": 3.433.0 + "@aws-sdk/credential-provider-process": 3.433.0 + "@aws-sdk/credential-provider-sso": 3.433.0 + "@aws-sdk/credential-provider-web-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 49805d3600db76c42cbaac613bb2aa541bbef923d93d9cfbea746c566c6aead370251032a016f3b615402783a2f9328c3b766022e0b143c207dfd292460dfdab + checksum: ce02cf8e0469f23a0573647a6d36e34b82c823b6f68c95b165762e60da1ea5bf05b0abcd42cbec4b3f67074480fe0670ba19b4fb615bc7d3d202a1161aade50c languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.405.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.405.0" +"@aws-sdk/credential-provider-node@npm:3.433.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.433.0" dependencies: - "@aws-sdk/credential-provider-env": 3.398.0 - "@aws-sdk/credential-provider-ini": 3.405.0 - "@aws-sdk/credential-provider-process": 3.405.0 - "@aws-sdk/credential-provider-sso": 3.405.0 - "@aws-sdk/credential-provider-web-identity": 3.398.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/credential-provider-env": 3.433.0 + "@aws-sdk/credential-provider-ini": 3.433.0 + "@aws-sdk/credential-provider-process": 3.433.0 + "@aws-sdk/credential-provider-sso": 3.433.0 + "@aws-sdk/credential-provider-web-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 3333ea579cc32cf4ad41d9de5e35ead4a4166fb3fc56766c3c342c39c2c51e6d292d04769a38725b742a89c9cb48cbcca150fce27d2606a305e25666c5ac4af5 + checksum: 30abdb886861d0b2874bb66fe0d31a66cd90a64c0aa545fa54ea99926128be0c52544b748ee1ba3fda3391e8d967dbe8bda01afdacb4b32e3776eb79c4ebf41e languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.405.0" +"@aws-sdk/credential-provider-process@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 54a790ececbb84c7eb37c8933158355d2fcf7a0ef8f73bdbaaa665178725532eafe2b775e7f4e17c75c7fe4bddcef92fa596880905553c0caaeb91a845995c6e + checksum: 42c04f294744a7d2b066b6a9e77f785eb391f49335963d25f87fb09d4b2d9a6acf78dfde7e3b4aca1bfca5eb6d799c557d5800846d8c055a27d5a047e023ba35 languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.405.0" +"@aws-sdk/credential-provider-sso@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.433.0" dependencies: - "@aws-sdk/client-sso": 3.405.0 - "@aws-sdk/token-providers": 3.405.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/client-sso": 3.433.0 + "@aws-sdk/token-providers": 3.433.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 3475919da43fbaf095034db07eb116ed9cc8ffe0e27df8ab09d2cce31e32c270444ef633a84179950f278bd5e3dd081635d520b66537baded8597f3382e4370d + checksum: ec4c97402b03722e8c2fc1a6a648d576e04291edc0320908d5f3fb799731451b0aa6618d03edc50eb0e6dfabc529c1a80f723b76095d1bb1874236b643f34739 languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.398.0" +"@aws-sdk/credential-provider-web-identity@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 85245e5818e418d555575b49bae464b088fba51a1011c66c5c3f15fcf43075ce66e60e0f45acfe0446a7e402578aa741a125ec5a396a42be2a841d9472a6dd24 + checksum: a0a76fb939da1f3a221927a8d4707f9f554ab27649cecbe84fb8f99264009c88aa10cf13324013fc0efc62edd450d60fe39525d7b9715b95ef7ae14374ce82d3 languageName: node linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/credential-providers@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/credential-providers@npm:3.433.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.405.0 - "@aws-sdk/client-sso": 3.405.0 - "@aws-sdk/client-sts": 3.405.0 - "@aws-sdk/credential-provider-cognito-identity": 3.405.0 - "@aws-sdk/credential-provider-env": 3.398.0 - "@aws-sdk/credential-provider-ini": 3.405.0 - "@aws-sdk/credential-provider-node": 3.405.0 - "@aws-sdk/credential-provider-process": 3.405.0 - "@aws-sdk/credential-provider-sso": 3.405.0 - "@aws-sdk/credential-provider-web-identity": 3.398.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/client-cognito-identity": 3.433.0 + "@aws-sdk/client-sso": 3.433.0 + "@aws-sdk/client-sts": 3.433.0 + "@aws-sdk/credential-provider-cognito-identity": 3.433.0 + "@aws-sdk/credential-provider-env": 3.433.0 + "@aws-sdk/credential-provider-http": 3.433.0 + "@aws-sdk/credential-provider-ini": 3.433.0 + "@aws-sdk/credential-provider-node": 3.433.0 + "@aws-sdk/credential-provider-process": 3.433.0 + "@aws-sdk/credential-provider-sso": 3.433.0 + "@aws-sdk/credential-provider-web-identity": 3.433.0 + "@aws-sdk/types": 3.433.0 "@smithy/credential-provider-imds": ^2.0.0 "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: d43e453abf49322933836ccd3d6ac68c87066ba672da543c80f2a751240b67fc679a5b5fd5554caeae2182df2cb1eb604c4cc407fbff200bf7d61631c5539135 + checksum: e2996cacdcc46aaa260c3549502f714dd4bcc7a0e2d757ee16258e119e58c4bc646c9ff7f303cfc52bf2a04976d463b51449aba82c7f25fe5a4f15264856be0e languageName: node linkType: hard @@ -1046,33 +1082,34 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.405.0 - resolution: "@aws-sdk/lib-storage@npm:3.405.0" + version: 3.433.0 + resolution: "@aws-sdk/lib-storage@npm:3.433.0" dependencies: "@smithy/abort-controller": ^2.0.1 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/smithy-client": ^2.0.5 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/smithy-client": ^2.1.12 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: ^2.5.0 peerDependencies: "@aws-sdk/client-s3": ^3.0.0 - checksum: 817f54b492a0d4dde6417541579d6c75691d6637de372ab8167305e3a41604e3c9c39266256346d4ab10f180be0dbb6ded698dc4b8ed351e2a30134d02a6e69d + checksum: 5b266063c14636bc43ab529afef073ce84ba6e6a5934a9a166b5ab5fa1dea43511947627477586972544f82be00a0e9aa771cf00364b49536186c077e1c6b37c languageName: node linkType: hard -"@aws-sdk/middleware-bucket-endpoint@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.405.0" +"@aws-sdk/middleware-bucket-endpoint@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@aws-sdk/util-arn-parser": 3.310.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 "@smithy/util-config-provider": ^2.0.0 tslib: ^2.5.0 - checksum: 3e45507ca7d6887e289401171ba26e18a41aa2c165266ae7b9156b27930e839fab48f7cd0a26b68109f7409c768c8ff9719da0dff044a23b0d7110964d407b61 + checksum: ee9e3d93b680b53402d3a0c945dd7778c2c043f9d50d265074ced28f06b715548272e4fb3349c797454b9b95af96e33a379008590ec827b40de127f339892329 languageName: node linkType: hard @@ -1089,115 +1126,116 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-expect-continue@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-expect-continue@npm:3.398.0" +"@aws-sdk/middleware-expect-continue@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 5eaa935f4478b434e4f80dc5c12822e7c6b6de96dd3fb5031e05d6736cd06ad092126b1f9ca2d413f97db33a690232ac5c2ed67d13bae1cb6f3ab1d69b8b235b + checksum: dcf3b671f5db6e2bfaeceee711a47342025188ce86eacaade44694b16af35f63f5d0b3dac8c41553b30652c71347ecb219c072c2881a92148e0420a8e6553ddb languageName: node linkType: hard -"@aws-sdk/middleware-flexible-checksums@npm:3.400.0": - version: 3.400.0 - resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.400.0" +"@aws-sdk/middleware-flexible-checksums@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.433.0" dependencies: "@aws-crypto/crc32": 3.0.0 "@aws-crypto/crc32c": 3.0.0 - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@smithy/is-array-buffer": ^2.0.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: d2fab21ebf98b3a072fd1062c7ddff1b93db31d2356813b670f70690093f05518612009dde217e21493630aa0e6b8cb62a5c9fe29c81d84a66dab7ebbde6179a + checksum: 06e4925aa78645aa0b3c795f042dd12199cda5e13dd1571465d232e4557ab1cddd6cf329696a1269b85cc922a3686acae0fd5968444707b13a21966e0e5aa7fc languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.398.0" +"@aws-sdk/middleware-host-header@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 58a69bca4fa5917cfec27dfe4111fda5c4b3e1c254d8048087898081774e2a2fc695f43031134a9d6ee89f725b32284238a6e3219b3d11f2908409f020c0d717 + checksum: b9a2b1b8c1eceaad9db2c30a38007e131ea4d67b936b1cfa8727cc20ae9a3f95975e24c0d5267c77b05c8c8811bfb8ede83d9f8d4bb8eb9726f03c6e5f21345a languageName: node linkType: hard -"@aws-sdk/middleware-location-constraint@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-location-constraint@npm:3.398.0" +"@aws-sdk/middleware-location-constraint@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 57c562dd12fae1a6cf2263b78981b24429a2d562d66ab6b90ec34892004c8140282357e38b993cda6779bbcf0c2c367e05ff6fb53c4cdc6c505c225bbdd58371 + checksum: c364e34a346c97bce551004aedf439ee8e175600f5b8ac2ef86e58263999b19d418ace1799f5beac6bd45302955823db9dc575c00f699c7177dd07510cebfb9b languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-logger@npm:3.398.0" +"@aws-sdk/middleware-logger@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-logger@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 7da95e1b3550fb1523232fab758b1976cabca29450fbfe24846cea4e42a28d241665e73c567d311104a114cfa37092b8c01eac0a8dc55ddc9723b79ac9747555 + checksum: 4184122eb5e519e4be2f3e70b3b328488ec861e7e9f586e5589fc7395b759e1bf79a5657f96f3dc13d9b0dcf9a0f0040703ac78e0dc736407319ec6d05b01a64 languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.398.0" +"@aws-sdk/middleware-recursion-detection@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: ada30093a7e8b529830c7f7f2880ee28a4010d216dd9ca6d7b2e8bd2beca1a9c3d875a21feb12af4eebaec0ce0bad26f03aecca00ff363f844543f586c6ea74c + checksum: 49ba0e4b87a911aa834ae4aa22d395258d4a6f1441c780f9f1356b4cb6bb023cecfb5d551f285f11c1968ee930804acf251c0e8b5fdcd9a8544e9177f1675812 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-s3@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-sdk-s3@npm:3.398.0" +"@aws-sdk/middleware-sdk-s3@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@aws-sdk/util-arn-parser": 3.310.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/protocol-http": ^3.0.8 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: eeb8a210124ed6b8cb3506766a8210247ed5c5d28fa66d97f77aa4d9f6be17ca91e00d448778c8e4d455598bc4a73b7ede0d79f0a7556be57aba60cb05bb3424 + checksum: 8d9d5d0b0ad552bf34b15ad510f8aa2257e40f1e0a356fe58ce69a1ee1d560bb94573f24e627384c1447d249f6ec17661887a6ff697217caf37f106b5e53d905 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.398.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 "@smithy/util-hex-encoding": ^2.0.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 00b66081fc4f8d632773c7348c83b125f1b8c4aacd658068d0df9f1f7ec32da8cfbed84a2e2abcc2c08141b844330253f200c60f9497d6724e5b4ca9aca89720 + checksum: 40ea48bb29851a0647dfd053b3f1d575cf322e523b31c78235307732d7275e6563b1296682323dede39c83387f76d58ecf413b2fc6152d8d77bd7b2dbd60ad0e languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sts@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-sdk-sts@npm:3.398.0" +"@aws-sdk/middleware-sdk-sts@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-sdk-sts@npm:3.433.0" dependencies: - "@aws-sdk/middleware-signing": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/middleware-signing": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 51b1c79c71e18d118e6e885e94185abefb91cace5597706a7e6fe9cac03b5be75c7219d9b2ee3182fddb9bd270aa842c51c3073700e71e1e51585e90d8b634dc + checksum: 116b8c1bff74828cbbae69e84c380c0643c45a7b66ea57731f68aa618b189af01a43931c0a82b2a20f67bc8dd7cec1228ebd65c87e620b06a9b5b3c0673d77a3 languageName: node linkType: hard @@ -1211,46 +1249,46 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-signing@npm:3.398.0" +"@aws-sdk/middleware-signing@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-signing@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 "@smithy/property-provider": ^2.0.0 - "@smithy/protocol-http": ^2.0.5 + "@smithy/protocol-http": ^3.0.8 "@smithy/signature-v4": ^2.0.0 - "@smithy/types": ^2.2.2 - "@smithy/util-middleware": ^2.0.0 + "@smithy/types": ^2.4.0 + "@smithy/util-middleware": ^2.0.5 tslib: ^2.5.0 - checksum: cba44afa280ae515285ea8b7f6c032089865696a8416bd66b4e6114ac6303ed4c4a938687f6dee20a23c003339bce95110eb854da679b107486e63cc9ead3fe6 + checksum: a55defd93fa78e613df223668807c314d6c30e299859743c7ffac94da0340703ff93eccf3940cb216add60c475f6334ccbddb484e322c88416111e0e3aef19b5 languageName: node linkType: hard -"@aws-sdk/middleware-ssec@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-ssec@npm:3.398.0" +"@aws-sdk/middleware-ssec@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: d00514654fcb19072c36cf4ab4239e4679b2ede322b41f5d979b6924d78b4ad40d5521b926fd422ed689a78af0e0acc7e7cb4bad1b3a5541d0e57118778712a0 + checksum: e1e1c631686f4a89cf02520361533a007d98a3c9faa868d1462bf8b287e77fd86da896dc7610be37cf8cd66fa672280035dc2169fa15ce1a8debb28fff3cb949 languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.398.0" +"@aws-sdk/middleware-user-agent@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 3113e70a1c395b565a6811d6d486941a3f159ecb412a329edf85e652509fd679556136962a303086ad913b219a6531171d1e2d427c24427d9486e66afa485952 + checksum: 91d31db0f9ae660077142c407f1c833f8dffec7cbfd1538cd2af7bfeeb9f6bc7f3563381f975cae3e38d05d3f613fbe0c165625030241e8561d1feab01c3affe languageName: node linkType: hard -"@aws-sdk/node-http-handler@npm:3.370.0, @aws-sdk/node-http-handler@npm:^3.350.0": +"@aws-sdk/node-http-handler@npm:3.370.0": version: 3.370.0 resolution: "@aws-sdk/node-http-handler@npm:3.370.0" dependencies: @@ -1294,21 +1332,29 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/signature-v4-multi-region@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/signature-v4-multi-region@npm:3.398.0" +"@aws-sdk/region-config-resolver@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/protocol-http": ^2.0.5 - "@smithy/signature-v4": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 + "@smithy/util-config-provider": ^2.0.0 + "@smithy/util-middleware": ^2.0.5 tslib: ^2.5.0 - peerDependencies: - "@aws-sdk/signature-v4-crt": ^3.118.0 - peerDependenciesMeta: - "@aws-sdk/signature-v4-crt": - optional: true - checksum: d6acc0fa94eef3f7d6191d210cf348ff50d6df1ac0b382df5cf4a1d19284e3e093f63352db53fa43077d5dc12f20c96e5fb5092322c17705960886477f9db2b2 + checksum: 80a80707c2c991c16e6a52bde426704337b119d89cdedd70af72a7c52d2ee285a6cdcd355e45cb630e6d2dc3a7f57749b3276b9fff851d57c57916ef5ee2616f + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.433.0" + dependencies: + "@aws-sdk/types": 3.433.0 + "@smithy/protocol-http": ^3.0.8 + "@smithy/signature-v4": ^2.0.0 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 7bd0d911d16385dd47792421b2a1a19284f121faf6b3fa3b400d6959d11888dbefab6994d3fb2efca85d4cae257a80142579352e7d2a0ae6f487c98c0369efc3 languageName: node linkType: hard @@ -1328,46 +1374,46 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/token-providers@npm:3.405.0" +"@aws-sdk/token-providers@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/token-providers@npm:3.433.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.398.0 - "@aws-sdk/middleware-logger": 3.398.0 - "@aws-sdk/middleware-recursion-detection": 3.398.0 - "@aws-sdk/middleware-user-agent": 3.398.0 - "@aws-sdk/types": 3.398.0 - "@aws-sdk/util-endpoints": 3.398.0 - "@aws-sdk/util-user-agent-browser": 3.398.0 - "@aws-sdk/util-user-agent-node": 3.405.0 - "@smithy/config-resolver": ^2.0.5 - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/hash-node": ^2.0.5 - "@smithy/invalid-dependency": ^2.0.5 - "@smithy/middleware-content-length": ^2.0.5 - "@smithy/middleware-endpoint": ^2.0.5 - "@smithy/middleware-retry": ^2.0.5 - "@smithy/middleware-serde": ^2.0.5 - "@smithy/middleware-stack": ^2.0.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/node-http-handler": ^2.0.5 + "@aws-sdk/middleware-host-header": 3.433.0 + "@aws-sdk/middleware-logger": 3.433.0 + "@aws-sdk/middleware-recursion-detection": 3.433.0 + "@aws-sdk/middleware-user-agent": 3.433.0 + "@aws-sdk/types": 3.433.0 + "@aws-sdk/util-endpoints": 3.433.0 + "@aws-sdk/util-user-agent-browser": 3.433.0 + "@aws-sdk/util-user-agent-node": 3.433.0 + "@smithy/config-resolver": ^2.0.16 + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/hash-node": ^2.0.12 + "@smithy/invalid-dependency": ^2.0.12 + "@smithy/middleware-content-length": ^2.0.14 + "@smithy/middleware-endpoint": ^2.1.3 + "@smithy/middleware-retry": ^2.0.18 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/node-http-handler": ^2.1.8 "@smithy/property-provider": ^2.0.0 - "@smithy/protocol-http": ^2.0.5 + "@smithy/protocol-http": ^3.0.8 "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/smithy-client": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 "@smithy/util-base64": ^2.0.0 "@smithy/util-body-length-browser": ^2.0.0 "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.6 - "@smithy/util-defaults-mode-node": ^2.0.6 - "@smithy/util-retry": ^2.0.0 + "@smithy/util-defaults-mode-browser": ^2.0.16 + "@smithy/util-defaults-mode-node": ^2.0.21 + "@smithy/util-retry": ^2.0.5 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: e7a41844ade3f9b4c94451ac71eedaaa370f5c07e982f431f96b0609a1ade1a455a7a46e1e568ba481e3462e97de04d5adff34007c516737e56fcefe93b5bb3f + checksum: 1ac166b74249080f856a679c44244e758c333bd3325c4e6ead9e3b9638a531eed79ce561247de364372caf840788922cb633cb4f646cd4d333193544ec15ffc8 languageName: node linkType: hard @@ -1381,13 +1427,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/types@npm:3.398.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": - version: 3.398.0 - resolution: "@aws-sdk/types@npm:3.398.0" +"@aws-sdk/types@npm:3.433.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": + version: 3.433.0 + resolution: "@aws-sdk/types@npm:3.433.0" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: b0c3531336e3dc0a3e2524199026a2366a29a4eee07195bffd6a9d48c778924183ac9894029b1053d0f744121cec3b7d397828f2065acf37eea79d3106af1273 + checksum: f7460897bee2835b06cd957853b17eb4eb4fe1e7f66f6ca97e2fc0c642ff7093011d73cbde64f097cdcc462f1f72c3e0980472c716eefd656c61dac95e7e060d languageName: node linkType: hard @@ -1421,13 +1467,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/util-endpoints@npm:3.398.0" +"@aws-sdk/util-endpoints@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/util-endpoints@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 + "@aws-sdk/types": 3.433.0 tslib: ^2.5.0 - checksum: af7df2c99f067641a369f54baf6def9c0dd4ea433f295d80afaebad9698a73ca7b969005c9a9b38a11611618f11b5ae968de7521ddfc35e5a3fa5f1e153687e7 + checksum: 0a688f1a21fe83ce1aa69e87e2f56190cabfa721bbb3d1e45bc698cbbae0a4a22c523cf1c1ac5c6c4a63a2a957b3df928ef2ac9d583cbd276341b3d36e21c672 languageName: node linkType: hard @@ -1479,32 +1525,32 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.398.0": - version: 3.398.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.398.0" +"@aws-sdk/util-user-agent-browser@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/types": ^2.4.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: 838879f9741e9c43f34bc3e4c29c60a4f43029a06808e4a91a30f943c35636bba93caf74db38f25752c206d3d2157b3ba8ef3e053d9d015e76ec76d15cacc6a8 + checksum: ca762fdf65f0b17832dd6f9d1e48e3c57d54cb79e1ae26fa882a7c13cae2e14b138ec07d4ef766b40c17ec558f1cfd9c1d9ecf9ccb369472abdef79adc1e3189 languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.405.0": - version: 3.405.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.405.0" +"@aws-sdk/util-user-agent-node@npm:3.433.0": + version: 3.433.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.433.0" dependencies: - "@aws-sdk/types": 3.398.0 - "@smithy/node-config-provider": ^2.0.6 - "@smithy/types": ^2.2.2 + "@aws-sdk/types": 3.433.0 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 peerDependencies: aws-crt: ">=1.0.0" peerDependenciesMeta: aws-crt: optional: true - checksum: e4a0fc8d5fc98b4d7fa31b786541c0e04f8977c2e6236bf0581e23bcb91592598281d196f38cab857e33cc8852f37fb17ddad1d39b751c4a0551a7735a3811c9 + checksum: dcf49bbbeec20eeb2f95d0b187b967d0d44ba39c82aa517a81784bae2abc7bf4749c7427285f4cefa4097546fa7dea39490b3fc8d29b34d575446073c0e9f7a1 languageName: node linkType: hard @@ -1800,7 +1846,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.22.10, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.8.3": +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.12.13, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.16.7, @babel/code-frame@npm:^7.18.6, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.8.3": version: 7.22.13 resolution: "@babel/code-frame@npm:7.22.13" dependencies: @@ -1817,30 +1863,30 @@ __metadata: languageName: node linkType: hard -"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.14.0, @babel/core@npm:^7.19.6": - version: 7.22.11 - resolution: "@babel/core@npm:7.22.11" +"@babel/core@npm:^7.11.6, @babel/core@npm:^7.12.3, @babel/core@npm:^7.13.16, @babel/core@npm:^7.14.0, @babel/core@npm:^7.19.6, @babel/core@npm:^7.22.20": + version: 7.23.0 + resolution: "@babel/core@npm:7.23.0" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.22.10 - "@babel/generator": ^7.22.10 - "@babel/helper-compilation-targets": ^7.22.10 - "@babel/helper-module-transforms": ^7.22.9 - "@babel/helpers": ^7.22.11 - "@babel/parser": ^7.22.11 - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.11 - "@babel/types": ^7.22.11 - convert-source-map: ^1.7.0 + "@babel/code-frame": ^7.22.13 + "@babel/generator": ^7.23.0 + "@babel/helper-compilation-targets": ^7.22.15 + "@babel/helper-module-transforms": ^7.23.0 + "@babel/helpers": ^7.23.0 + "@babel/parser": ^7.23.0 + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.23.0 + "@babel/types": ^7.23.0 + convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.3 semver: ^6.3.1 - checksum: f258b2539ea2e5bfe55a708c2f3e1093a1b4744f12becc35abeb896037b66210de9a8ad6296a706046d5dc3a24e564362b73a9b814e5bfe500c8baab60c22d2e + checksum: cebd9b48dbc970a7548522f207f245c69567e5ea17ebb1a4e4de563823cf20a01177fe8d2fe19b6e1461361f92fa169fd0b29f8ee9d44eeec84842be1feee5f2 languageName: node linkType: hard -"@babel/generator@npm:^7.14.0, @babel/generator@npm:^7.18.13, @babel/generator@npm:^7.22.10, @babel/generator@npm:^7.23.0, @babel/generator@npm:^7.7.2": +"@babel/generator@npm:^7.14.0, @babel/generator@npm:^7.18.13, @babel/generator@npm:^7.23.0, @babel/generator@npm:^7.7.2": version: 7.23.0 resolution: "@babel/generator@npm:7.23.0" dependencies: @@ -1870,16 +1916,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.22.10, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6": - version: 7.22.10 - resolution: "@babel/helper-compilation-targets@npm:7.22.10" +"@babel/helper-compilation-targets@npm:^7.18.9, @babel/helper-compilation-targets@npm:^7.22.10, @babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6": + version: 7.22.15 + resolution: "@babel/helper-compilation-targets@npm:7.22.15" dependencies: "@babel/compat-data": ^7.22.9 - "@babel/helper-validator-option": ^7.22.5 + "@babel/helper-validator-option": ^7.22.15 browserslist: ^4.21.9 lru-cache: ^5.1.1 semver: ^6.3.1 - checksum: f6f1896816392bcff671bbe6e277307729aee53befb4a66ea126e2a91eda78d819a70d06fa384c74ef46c1595544b94dca50bef6c78438d9ffd31776dafbd435 + checksum: ce85196769e091ae54dd39e4a80c2a9df1793da8588e335c383d536d54f06baf648d0a08fc873044f226398c4ded15c4ae9120ee18e7dfd7c639a68e3cdc9980 languageName: node linkType: hard @@ -1965,27 +2011,27 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-module-imports@npm:7.22.5" +"@babel/helper-module-imports@npm:^7.0.0, @babel/helper-module-imports@npm:^7.16.0, @babel/helper-module-imports@npm:^7.16.7, @babel/helper-module-imports@npm:^7.22.15, @babel/helper-module-imports@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-module-imports@npm:7.22.15" dependencies: - "@babel/types": ^7.22.5 - checksum: 9ac2b0404fa38b80bdf2653fbeaf8e8a43ccb41bd505f9741d820ed95d3c4e037c62a1bcdcb6c9527d7798d2e595924c4d025daed73283badc180ada2c9c49ad + "@babel/types": ^7.22.15 + checksum: ecd7e457df0a46f889228f943ef9b4a47d485d82e030676767e6a2fdcbdaa63594d8124d4b55fd160b41c201025aec01fc27580352b1c87a37c9c6f33d116702 languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.22.9": - version: 7.22.9 - resolution: "@babel/helper-module-transforms@npm:7.22.9" +"@babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.22.9, @babel/helper-module-transforms@npm:^7.23.0": + version: 7.23.0 + resolution: "@babel/helper-module-transforms@npm:7.23.0" dependencies: - "@babel/helper-environment-visitor": ^7.22.5 - "@babel/helper-module-imports": ^7.22.5 + "@babel/helper-environment-visitor": ^7.22.20 + "@babel/helper-module-imports": ^7.22.15 "@babel/helper-simple-access": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/helper-validator-identifier": ^7.22.5 + "@babel/helper-validator-identifier": ^7.22.20 peerDependencies: "@babel/core": ^7.0.0 - checksum: 2751f77660518cf4ff027514d6f4794f04598c6393be7b04b8e46c6e21606e11c19f3f57ab6129a9c21bacdf8b3ffe3af87bb401d972f34af2d0ffde02ac3001 + checksum: 6e2afffb058cf3f8ce92f5116f710dda4341c81cfcd872f9a0197ea594f7ce0ab3cb940b0590af2fe99e60d2e5448bfba6bca8156ed70a2ed4be2adc8586c891 languageName: node linkType: hard @@ -2072,10 +2118,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-validator-option@npm:7.22.5" - checksum: bbeca8a85ee86990215c0424997438b388b8d642d69b9f86c375a174d3cdeb270efafd1ff128bc7a1d370923d13b6e45829ba8581c027620e83e3a80c5c414b3 +"@babel/helper-validator-option@npm:^7.16.7, @babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.22.5": + version: 7.22.15 + resolution: "@babel/helper-validator-option@npm:7.22.15" + checksum: 68da52b1e10002a543161494c4bc0f4d0398c8fdf361d5f7f4272e95c45d5b32d974896d44f6a0ea7378c9204988879d73613ca683e13bd1304e46d25ff67a8d languageName: node linkType: hard @@ -2090,14 +2136,14 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.22.11": - version: 7.22.11 - resolution: "@babel/helpers@npm:7.22.11" +"@babel/helpers@npm:^7.23.0": + version: 7.23.1 + resolution: "@babel/helpers@npm:7.23.1" dependencies: - "@babel/template": ^7.22.5 - "@babel/traverse": ^7.22.11 - "@babel/types": ^7.22.11 - checksum: 93186544228b5e371486466ec3b86a77cce91beeff24a5670ca8ec46d50328f7700dab82d532351286e9d68624dc51d6d71589b051dd9535e44be077a43ec013 + "@babel/template": ^7.22.15 + "@babel/traverse": ^7.23.0 + "@babel/types": ^7.23.0 + checksum: acfc345102045c24ea2a4d60e00dcf8220e215af3add4520e2167700661338e6a80bd56baf44bb764af05ec6621101c9afc315dc107e18c61fa6da8acbdbb893 languageName: node linkType: hard @@ -2112,7 +2158,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.20.15, @babel/parser@npm:^7.22.11, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.13.16, @babel/parser@npm:^7.14.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.8, @babel/parser@npm:^7.20.15, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.0": version: 7.23.0 resolution: "@babel/parser@npm:7.23.0" bin: @@ -2959,6 +3005,28 @@ __metadata: languageName: node linkType: hard +"@babel/plugin-transform-react-jsx-self@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-react-jsx-self@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 671eebfabd14a0c7d6ae805fff7e289dfdb7ba984bb100ea2ef6dad1d6a665ebbb09199ab2e64fca7bc78bd0fdc80ca897b07996cf215fafc32c67bc564309af + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-source@npm:^7.22.5": + version: 7.22.5 + resolution: "@babel/plugin-transform-react-jsx-source@npm:7.22.5" + dependencies: + "@babel/helper-plugin-utils": ^7.22.5 + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 4ca2bd62ca14f8bbdcda9139f3f799e1c1c1bae504b67c1ca9bca142c53d81926d1a2b811f66a625f20999b2d352131053d886601f1ba3c1e9378c104d884277 + languageName: node + linkType: hard + "@babel/plugin-transform-react-jsx@npm:^7.0.0, @babel/plugin-transform-react-jsx@npm:^7.22.5": version: 7.22.5 resolution: "@babel/plugin-transform-react-jsx@npm:7.22.5" @@ -3305,12 +3373,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.1, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": - version: 7.23.1 - resolution: "@babel/runtime@npm:7.23.1" +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.13.10, @babel/runtime@npm:^7.15.4, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.2.0, @babel/runtime@npm:^7.20.1, @babel/runtime@npm:^7.20.13, @babel/runtime@npm:^7.20.6, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.23.1, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.4.4, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.6.0, @babel/runtime@npm:^7.7.6, @babel/runtime@npm:^7.8.3, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7, @babel/runtime@npm:^7.9.2": + version: 7.23.2 + resolution: "@babel/runtime@npm:7.23.2" dependencies: regenerator-runtime: ^0.14.0 - checksum: 0cd0d43e6e7dc7f9152fda8c8312b08321cda2f56ef53d6c22ebdd773abdc6f5d0a69008de90aa41908d00e2c1facb24715ff121274e689305c858355ff02c70 + checksum: 6c4df4839ec75ca10175f636d6362f91df8a3137f86b38f6cd3a4c90668a0fe8e9281d320958f4fbd43b394988958585a17c3aab2a4ea6bf7316b22916a371fb languageName: node linkType: hard @@ -3325,9 +3393,9 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.22.11, @babel/traverse@npm:^7.4.5, @babel/traverse@npm:^7.7.2": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" +"@babel/traverse@npm:^7.14.0, @babel/traverse@npm:^7.16.8, @babel/traverse@npm:^7.23.0, @babel/traverse@npm:^7.4.5": + version: 7.23.0 + resolution: "@babel/traverse@npm:7.23.0" dependencies: "@babel/code-frame": ^7.22.13 "@babel/generator": ^7.23.0 @@ -3339,11 +3407,11 @@ __metadata: "@babel/types": ^7.23.0 debug: ^4.1.0 globals: ^11.1.0 - checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d + checksum: 0b17fae53269e1af2cd3edba00892bc2975ad5df9eea7b84815dab07dfec2928c451066d51bc65b4be61d8499e77db7e547ce69ef2a7b0eca3f96269cb43a0b0 languageName: node linkType: hard -"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.13, @babel/types@npm:^7.20.0, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.11, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": +"@babel/types@npm:^7.0.0, @babel/types@npm:^7.16.8, @babel/types@npm:^7.18.13, @babel/types@npm:^7.20.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.22.10, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.3.0, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": version: 7.23.0 resolution: "@babel/types@npm:7.23.0" dependencies: @@ -3533,7 +3601,9 @@ __metadata: version: 0.0.0-use.local resolution: "@backstage/backend-openapi-utils@workspace:packages/backend-openapi-utils" dependencies: + "@backstage/backend-plugin-api": "workspace:^" "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" "@types/express": ^4.17.6 "@types/express-serve-static-core": ^4.17.5 @@ -3542,6 +3612,7 @@ __metadata: express-promise-router: ^4.1.0 json-schema-to-ts: ^2.6.2 lodash: ^4.17.21 + openapi-merge: ^1.3.2 openapi3-ts: ^3.1.2 supertest: ^6.1.3 languageName: unknown @@ -3650,7 +3721,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": +"@backstage/catalog-client@^1.4.5, @backstage/catalog-client@workspace:^, @backstage/catalog-client@workspace:packages/catalog-client": version: 0.0.0-use.local resolution: "@backstage/catalog-client@workspace:packages/catalog-client" dependencies: @@ -3662,7 +3733,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/catalog-model@^1.1.5, @backstage/catalog-model@^1.4.2, @backstage/catalog-model@workspace:^, @backstage/catalog-model@workspace:packages/catalog-model": +"@backstage/catalog-model@^1.1.5, @backstage/catalog-model@^1.4.2, @backstage/catalog-model@^1.4.3, @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: @@ -3726,8 +3797,6 @@ __metadata: "@backstage/test-utils": "workspace:^" "@backstage/theme": "workspace:^" "@backstage/types": "workspace:^" - "@esbuild-kit/cjs-loader": ^2.4.1 - "@esbuild-kit/esm-loader": ^2.5.5 "@manypkg/get-packages": ^1.1.3 "@octokit/graphql": ^5.0.0 "@octokit/graphql-schema": ^13.7.0 @@ -3752,6 +3821,7 @@ __metadata: "@swc/jest": ^0.2.22 "@types/cross-spawn": ^6.0.2 "@types/diff": ^5.0.0 + "@types/ejs": ^3.1.3 "@types/express": ^4.17.6 "@types/fs-extra": ^9.0.1 "@types/http-proxy": ^1.17.4 @@ -3837,6 +3907,7 @@ __metadata: tar: ^6.1.12 terser-webpack-plugin: ^5.1.3 ts-node: ^10.0.0 + tsx: ^3.14.0 type-fest: ^2.19.0 util: ^0.12.3 webpack: ^5.70.0 @@ -3847,9 +3918,18 @@ __metadata: yn: ^4.0.0 zod: ^3.21.4 peerDependencies: - "@microsoft/api-extractor": ^7.21.2 + "@vitejs/plugin-react": ^4.0.4 + vite: ^4.4.9 + vite-plugin-html: ^3.2.0 + vite-plugin-node-polyfills: ^0.14.1 peerDependenciesMeta: - "@microsoft/api-extractor": + "@vitejs/plugin-react": + optional: true + vite: + optional: true + vite-plugin-html: + optional: true + vite-plugin-node-polyfills: optional: true bin: backstage-cli: bin/backstage-cli @@ -3903,7 +3983,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/config@^1.0.6, @backstage/config@^1.0.7, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": +"@backstage/config@^1.0.6, @backstage/config@^1.0.7, @backstage/config@^1.1.1, @backstage/config@workspace:^, @backstage/config@workspace:packages/config": version: 0.0.0-use.local resolution: "@backstage/config@workspace:packages/config" dependencies: @@ -3970,7 +4050,112 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@^0.13.5, @backstage/core-components@workspace:^, @backstage/core-components@workspace:packages/core-components": +"@backstage/core-components@npm:^0.12.3": + version: 0.12.5 + resolution: "@backstage/core-components@npm:0.12.5" + dependencies: + "@backstage/config": ^1.0.7 + "@backstage/core-plugin-api": ^1.5.0 + "@backstage/errors": ^1.1.5 + "@backstage/theme": ^0.2.18 + "@backstage/version-bridge": ^1.0.3 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.57 + "@react-hookz/web": ^20.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + history: ^5.0.0 + immer: ^9.0.1 + lodash: ^4.17.21 + pluralize: ^8.0.0 + prop-types: ^15.7.2 + qs: ^6.9.4 + rc-progress: 3.4.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.6 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ~3.18.0 + peerDependencies: + "@types/react": ^16.13.1 || ^17.0.0 + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 8308c1b90247911f6cf22963b530cef169287f508ff29d159d0c83758134dd43bd5acd2113350690a46a02246bc05dca975bcc38325000aefb1c93c80e2f19c7 + languageName: node + linkType: hard + +"@backstage/core-components@npm:^0.13.5, @backstage/core-components@npm:^0.13.6": + version: 0.13.6 + resolution: "@backstage/core-components@npm:0.13.6" + dependencies: + "@backstage/catalog-model": ^1.4.3 + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.7.0 + "@backstage/errors": ^1.2.3 + "@backstage/plugin-catalog-react": ^1.8.5 + "@backstage/theme": ^0.4.3 + "@backstage/version-bridge": ^1.0.6 + "@date-io/core": ^1.3.13 + "@material-table/core": ^3.1.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^20.0.0 + "@types/react": ^16.13.1 || ^17.0.0 + "@types/react-sparklines": ^1.7.0 + "@types/react-text-truncate": ^0.14.0 + ansi-regex: ^6.0.1 + classnames: ^2.2.6 + d3-selection: ^3.0.0 + d3-shape: ^3.0.0 + d3-zoom: ^3.0.0 + dagre: ^0.8.5 + history: ^5.0.0 + immer: ^9.0.1 + linkify-react: 4.1.1 + linkifyjs: 4.1.1 + lodash: ^4.17.21 + pluralize: ^8.0.0 + qs: ^6.9.4 + rc-progress: 3.5.1 + react-helmet: 6.1.0 + react-hook-form: ^7.12.2 + react-idle-timer: 5.6.2 + react-markdown: ^8.0.0 + react-sparklines: ^1.7.0 + react-syntax-highlighter: ^15.4.5 + react-text-truncate: ^0.19.0 + react-use: ^17.3.2 + react-virtualized-auto-sizer: ^1.0.11 + react-window: ^1.8.6 + remark-gfm: ^3.0.1 + zen-observable: ^0.10.0 + zod: ^3.21.4 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 834522177a8df8edda302fdcd9c91fc21210c1623130dd13db07032b9db56028af559c153c44201c9496b767c075bf6e4df6c2eb9ab55092ebd95fc5a399d4ad + 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: @@ -4045,57 +4230,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/core-components@npm:^0.12.3": - version: 0.12.5 - resolution: "@backstage/core-components@npm:0.12.5" +"@backstage/core-plugin-api@npm:^1.3.0, @backstage/core-plugin-api@npm:^1.5.0, @backstage/core-plugin-api@npm:^1.6.0, @backstage/core-plugin-api@npm:^1.7.0": + version: 1.7.0 + resolution: "@backstage/core-plugin-api@npm:1.7.0" dependencies: - "@backstage/config": ^1.0.7 - "@backstage/core-plugin-api": ^1.5.0 - "@backstage/errors": ^1.1.5 - "@backstage/theme": ^0.2.18 - "@backstage/version-bridge": ^1.0.3 - "@material-table/core": ^3.1.0 - "@material-ui/core": ^4.12.2 - "@material-ui/icons": ^4.9.1 - "@material-ui/lab": 4.0.0-alpha.57 - "@react-hookz/web": ^20.0.0 - "@types/react-sparklines": ^1.7.0 - "@types/react-text-truncate": ^0.14.0 - ansi-regex: ^6.0.1 - classnames: ^2.2.6 - d3-selection: ^3.0.0 - d3-shape: ^3.0.0 - d3-zoom: ^3.0.0 - dagre: ^0.8.5 - history: ^5.0.0 - immer: ^9.0.1 - lodash: ^4.17.21 - pluralize: ^8.0.0 - prop-types: ^15.7.2 - qs: ^6.9.4 - rc-progress: 3.4.1 - react-helmet: 6.1.0 - react-hook-form: ^7.12.2 - react-markdown: ^8.0.0 - react-sparklines: ^1.7.0 - react-syntax-highlighter: ^15.4.5 - react-text-truncate: ^0.19.0 - react-use: ^17.3.2 - react-virtualized-auto-sizer: ^1.0.6 - react-window: ^1.8.6 - remark-gfm: ^3.0.1 - zen-observable: ^0.10.0 - zod: ~3.18.0 - peerDependencies: + "@backstage/config": ^1.1.1 + "@backstage/types": ^1.1.1 + "@backstage/version-bridge": ^1.0.6 "@types/react": ^16.13.1 || ^17.0.0 + history: ^5.0.0 + i18next: ^22.4.15 + peerDependencies: react: ^16.13.1 || ^17.0.0 react-dom: ^16.13.1 || ^17.0.0 react-router-dom: 6.0.0-beta.0 || ^6.3.0 - checksum: 8308c1b90247911f6cf22963b530cef169287f508ff29d159d0c83758134dd43bd5acd2113350690a46a02246bc05dca975bcc38325000aefb1c93c80e2f19c7 + checksum: 7b328dac8faf598f4b45c098ba0629f29d1278c204fde11264fe964a82da4e3a72a13551200941b31901d3b0f483bea55deba5264ab55a2c202eae2c6a196ae7 languageName: node linkType: hard -"@backstage/core-plugin-api@^1.3.0, @backstage/core-plugin-api@^1.5.0, @backstage/core-plugin-api@^1.6.0, @backstage/core-plugin-api@workspace:^, @backstage/core-plugin-api@workspace:packages/core-plugin-api": +"@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: @@ -4193,7 +4346,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/errors@^1.1.5, @backstage/errors@workspace:^, @backstage/errors@workspace:packages/errors": +"@backstage/errors@^1.1.5, @backstage/errors@^1.2.3, @backstage/errors@workspace:^, @backstage/errors@workspace:packages/errors": version: 0.0.0-use.local resolution: "@backstage/errors@workspace:packages/errors" dependencies: @@ -4285,7 +4438,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration-react@^1.1.19, @backstage/integration-react@workspace:^, @backstage/integration-react@workspace:packages/integration-react": +"@backstage/integration-react@npm:^1.1.19": + version: 1.1.20 + resolution: "@backstage/integration-react@npm:1.1.20" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.7.0 + "@backstage/integration": ^1.7.1 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@types/react": ^16.13.1 || ^17.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 6d76d58687703ac9fa8d328caf993cf8ae1326254e55cda90701c94418f37d0fb6ee1940050a56d6f47e7ee8468658be2862359b644deb10e686342960435487 + 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: @@ -4309,7 +4480,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": +"@backstage/integration@^1.7.1, @backstage/integration@workspace:^, @backstage/integration@workspace:packages/integration": version: 0.0.0-use.local resolution: "@backstage/integration@workspace:packages/integration" dependencies: @@ -4793,7 +4964,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-auth-backend-module-microsoft-provider@workspace:^, @backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider": +"@backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider" dependencies: @@ -4870,7 +5041,6 @@ __metadata: "@backstage/plugin-auth-backend-module-github-provider": "workspace:^" "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^" "@backstage/plugin-auth-backend-module-google-provider": "workspace:^" - "@backstage/plugin-auth-backend-module-microsoft-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" @@ -5303,6 +5473,35 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-catalog-backend-module-backstage-openapi@workspace:^, @backstage/plugin-catalog-backend-module-backstage-openapi@workspace:plugins/catalog-backend-module-backstage-openapi": + version: 0.0.0-use.local + resolution: "@backstage/plugin-catalog-backend-module-backstage-openapi@workspace:plugins/catalog-backend-module-backstage-openapi" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-openapi-utils": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-tasks": "workspace:^" + "@backstage/catalog-model": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/errors": "workspace:^" + "@backstage/plugin-catalog-node": "workspace:^" + "@backstage/test-utils": "workspace:^" + "@types/express": "*" + "@types/supertest": ^2.0.8 + cross-fetch: ^3.1.5 + express: ^4.17.1 + express-promise-router: ^4.1.0 + lodash: ^4.17.21 + msw: ^1.0.0 + openapi-merge: ^1.3.2 + openapi3-ts: ^3.1.2 + supertest: ^6.2.4 + uuid: ^9.0.0 + yn: ^4.0.0 + languageName: unknown + linkType: soft + "@backstage/plugin-catalog-backend-module-bitbucket-cloud@workspace:plugins/catalog-backend-module-bitbucket-cloud": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-bitbucket-cloud@workspace:plugins/catalog-backend-module-bitbucket-cloud" @@ -5561,7 +5760,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-backend-module-openapi@workspace:plugins/catalog-backend-module-openapi": +"@backstage/plugin-catalog-backend-module-openapi@workspace:^, @backstage/plugin-catalog-backend-module-openapi@workspace:plugins/catalog-backend-module-openapi": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-backend-module-openapi@workspace:plugins/catalog-backend-module-openapi" dependencies: @@ -5690,7 +5889,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-common@^1.0.10, @backstage/plugin-catalog-common@workspace:^, @backstage/plugin-catalog-common@workspace:plugins/catalog-common": +"@backstage/plugin-catalog-common@^1.0.10, @backstage/plugin-catalog-common@^1.0.17, @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: @@ -5826,7 +6025,43 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-catalog-react@^1.2.4, @backstage/plugin-catalog-react@^1.8.4, @backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": +"@backstage/plugin-catalog-react@npm:^1.2.4, @backstage/plugin-catalog-react@npm:^1.8.4, @backstage/plugin-catalog-react@npm:^1.8.5": + version: 1.8.5 + resolution: "@backstage/plugin-catalog-react@npm:1.8.5" + dependencies: + "@backstage/catalog-client": ^1.4.5 + "@backstage/catalog-model": ^1.4.3 + "@backstage/core-components": ^0.13.6 + "@backstage/core-plugin-api": ^1.7.0 + "@backstage/errors": ^1.2.3 + "@backstage/integration": ^1.7.1 + "@backstage/plugin-catalog-common": ^1.0.17 + "@backstage/plugin-permission-common": ^0.7.9 + "@backstage/plugin-permission-react": ^0.4.16 + "@backstage/theme": ^0.4.3 + "@backstage/types": ^1.1.1 + "@backstage/version-bridge": ^1.0.6 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@material-ui/lab": 4.0.0-alpha.61 + "@react-hookz/web": ^23.0.0 + "@types/react": ^16.13.1 || ^17.0.0 + classnames: ^2.2.6 + lodash: ^4.17.21 + material-ui-popup-state: ^1.9.3 + qs: ^6.9.4 + react-use: ^17.2.4 + yaml: ^2.0.0 + zen-observable: ^0.10.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 8badfa1af8d20f93ff162580d62e0ddf91753c91dfc741bbc7e12a6e9c8ebc554797b537ccecd6adac276e404f7d5c20bf64aea44c6b2798bfe5643d251b9b04 + languageName: node + linkType: hard + +"@backstage/plugin-catalog-react@workspace:^, @backstage/plugin-catalog-react@workspace:plugins/catalog-react": version: 0.0.0-use.local resolution: "@backstage/plugin-catalog-react@workspace:plugins/catalog-react" dependencies: @@ -5837,6 +6072,7 @@ __metadata: "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/integration": "workspace:^" "@backstage/plugin-catalog-common": "workspace:^" "@backstage/plugin-permission-common": "workspace:^" @@ -5931,6 +6167,8 @@ __metadata: "@testing-library/react": ^14.0.0 "@testing-library/user-event": ^14.0.0 "@types/react": ^16.13.1 || ^17.0.0 + dataloader: ^2.0.0 + expiry-map: ^2.0.0 history: ^5.0.0 lodash: ^4.17.21 pluralize: ^8.0.0 @@ -6098,6 +6336,7 @@ __metadata: resolution: "@backstage/plugin-code-coverage-backend@workspace:plugins/code-coverage-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/catalog-client": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" @@ -7116,7 +7355,7 @@ __metadata: "@types/codemirror": ^5.0.0 "@types/react": ^16.13.1 || ^17.0.0 cross-fetch: ^3.1.5 - graphiql: ^1.5.12 + graphiql: ^3.0.6 graphql: ^16.0.0 graphql-ws: ^5.4.1 msw: ^1.0.0 @@ -7182,7 +7421,25 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-home-react@^0.1.3, @backstage/plugin-home-react@workspace:^, @backstage/plugin-home-react@workspace:plugins/home-react": +"@backstage/plugin-home-react@npm:^0.1.3": + version: 0.1.4 + resolution: "@backstage/plugin-home-react@npm:0.1.4" + dependencies: + "@backstage/core-components": ^0.13.6 + "@backstage/core-plugin-api": ^1.7.0 + "@material-ui/core": ^4.12.2 + "@material-ui/icons": ^4.9.1 + "@rjsf/utils": 5.13.0 + "@types/react": ^16.13.1 || ^17.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 52c528a8af4678308bf4341d85aa5360cfe8d1d90f024c59ca0d9bf9343c6964f025eef9c53c50560e65816b7dce052c27463d41b9609a2e125fa1ac637ac463 + languageName: node + linkType: hard + +"@backstage/plugin-home-react@workspace:^, @backstage/plugin-home-react@workspace:plugins/home-react": version: 0.0.0-use.local resolution: "@backstage/plugin-home-react@workspace:plugins/home-react" dependencies: @@ -7243,7 +7500,7 @@ __metadata: lodash: ^4.17.21 luxon: ^3.4.3 msw: ^1.0.0 - react-grid-layout: ^1.3.4 + react-grid-layout: 1.3.4 react-resizable: ^3.0.4 react-use: ^17.2.4 zod: ^3.21.4 @@ -8172,7 +8429,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-permission-common@workspace:^, @backstage/plugin-permission-common@workspace:plugins/permission-common": +"@backstage/plugin-permission-common@^0.7.9, @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: @@ -8210,6 +8467,25 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-permission-react@npm:^0.4.16": + version: 0.4.16 + resolution: "@backstage/plugin-permission-react@npm:0.4.16" + dependencies: + "@backstage/config": ^1.1.1 + "@backstage/core-plugin-api": ^1.7.0 + "@backstage/plugin-permission-common": ^0.7.9 + "@types/react": ^16.13.1 || ^17.0.0 + cross-fetch: ^3.1.5 + react-use: ^17.2.4 + swr: ^2.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: dbf85118d34b29c19424f71cfb3918b63bc0243dd479df32227deec992686c006101395aef5a791887658d6d6543e0dbd9df4c5579fcbfd93284c79876924570 + 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" @@ -8487,6 +8763,7 @@ __metadata: "@backstage/integration": "workspace:^" "@backstage/plugin-scaffolder-node": "workspace:^" "@gitbeaker/node": ^35.8.0 + yaml: ^2.0.0 zod: ^3.21.4 languageName: unknown linkType: soft @@ -9539,11 +9816,11 @@ __metadata: "@aws-sdk/client-s3": ^3.350.0 "@aws-sdk/credential-providers": ^3.350.0 "@aws-sdk/lib-storage": ^3.350.0 - "@aws-sdk/node-http-handler": ^3.350.0 "@aws-sdk/types": ^3.347.0 "@azure/identity": ^3.2.1 "@azure/storage-blob": ^12.5.0 "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/catalog-model": "workspace:^" "@backstage/cli": "workspace:^" @@ -9553,6 +9830,7 @@ __metadata: "@backstage/integration-aws-node": "workspace:^" "@backstage/plugin-search-common": "workspace:^" "@google-cloud/storage": ^6.0.0 + "@smithy/node-http-handler": ^2.1.7 "@trendyol-js/openstack-swift-sdk": ^0.0.6 "@types/express": ^4.17.6 "@types/fs-extra": ^9.0.5 @@ -9775,11 +10053,13 @@ __metadata: resolution: "@backstage/plugin-vault-backend@workspace:plugins/vault-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" "@backstage/backend-tasks": "workspace:^" "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/plugin-vault-node": "workspace:^" "@types/compression": ^1.7.2 "@types/express": "*" "@types/supertest": ^2.0.8 @@ -9797,6 +10077,15 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-vault-node@workspace:^, @backstage/plugin-vault-node@workspace:plugins/vault-node": + version: 0.0.0-use.local + resolution: "@backstage/plugin-vault-node@workspace:plugins/vault-node" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/cli": "workspace:^" + languageName: unknown + linkType: soft + "@backstage/plugin-vault@workspace:plugins/vault": version: 0.0.0-use.local resolution: "@backstage/plugin-vault@workspace:plugins/vault" @@ -9891,12 +10180,13 @@ __metadata: "@stoplight/spectral-formatters": ^1.1.0 "@stoplight/spectral-functions": ^1.7.2 "@stoplight/spectral-parsers": ^1.0.2 - "@stoplight/spectral-rulesets": ^1.16.0 + "@stoplight/spectral-rulesets": ^1.18.0 "@stoplight/spectral-runtime": ^1.1.2 "@stoplight/types": ^13.14.0 "@types/is-glob": ^4.0.2 "@types/mock-fs": ^4.13.0 "@types/node": ^18.17.8 + "@types/prettier": ^2.0.0 chalk: ^4.0.0 codeowners-utils: ^1.0.2 commander: ^9.1.0 @@ -9908,12 +10198,14 @@ __metadata: minimatch: ^5.1.1 mock-fs: ^5.2.0 p-limit: ^3.0.2 + portfinder: ^1.0.32 ts-node: ^10.0.0 yaml-diff-patch: ^2.0.0 peerDependencies: "@microsoft/api-extractor-model": "*" "@microsoft/tsdoc": "*" "@microsoft/tsdoc-config": "*" + "@useoptic/optic": ^0.50.7 prettier: ^2.8.1 typescript: "> 3.0.0" peerDependenciesMeta: @@ -9952,7 +10244,35 @@ __metadata: languageName: unknown linkType: soft -"@backstage/theme@^0.4.2, @backstage/theme@workspace:^, @backstage/theme@workspace:packages/theme": +"@backstage/theme@npm:^0.2.16, @backstage/theme@npm:^0.2.18": + version: 0.2.19 + resolution: "@backstage/theme@npm:0.2.19" + dependencies: + "@material-ui/core": ^4.12.2 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + checksum: 93ca110a3f953d6d98b4a5f2163beaa1bd1f95adb66a593e85eb523a7012acf654b6148db54008a6434805a4bd9016d67e5c6a317bc56940ee6800aecbb97d6f + languageName: node + linkType: hard + +"@backstage/theme@npm:^0.4.2, @backstage/theme@npm:^0.4.3": + version: 0.4.3 + resolution: "@backstage/theme@npm:0.4.3" + dependencies: + "@emotion/react": ^11.10.5 + "@emotion/styled": ^11.10.5 + "@mui/material": ^5.12.2 + peerDependencies: + "@material-ui/core": ^4.12.2 + "@types/react": ^16.13.1 || ^17.0.0 + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + checksum: 8ad491f837803959c22a23e2895d0409bfd8ba58c325c6bcf2a87fbedc4d6076568e7ed2f3a715296e3f6f03f8579df6947831fe983579c777d607826a90b142 + 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: @@ -9969,19 +10289,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/theme@npm:^0.2.16, @backstage/theme@npm:^0.2.18": - version: 0.2.19 - resolution: "@backstage/theme@npm:0.2.19" - dependencies: - "@material-ui/core": ^4.12.2 - peerDependencies: - react: ^16.13.1 || ^17.0.0 - react-dom: ^16.13.1 || ^17.0.0 - checksum: 93ca110a3f953d6d98b4a5f2163beaa1bd1f95adb66a593e85eb523a7012acf654b6148db54008a6434805a4bd9016d67e5c6a317bc56940ee6800aecbb97d6f - languageName: node - linkType: hard - -"@backstage/types@^1.0.2, @backstage/types@workspace:^, @backstage/types@workspace:packages/types": +"@backstage/types@^1.0.2, @backstage/types@^1.1.1, @backstage/types@workspace:^, @backstage/types@workspace:packages/types": version: 0.0.0-use.local resolution: "@backstage/types@workspace:packages/types" dependencies: @@ -9992,7 +10300,20 @@ __metadata: languageName: unknown linkType: soft -"@backstage/version-bridge@^1.0.3, @backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": +"@backstage/version-bridge@npm:^1.0.3, @backstage/version-bridge@npm:^1.0.6": + version: 1.0.6 + resolution: "@backstage/version-bridge@npm:1.0.6" + dependencies: + "@types/react": ^16.13.1 || ^17.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 + react-dom: ^16.13.1 || ^17.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 2923a7723b3e9b6f2a6106ad13e6d7d648f727fa4751482f54bdd181ef7e8d0018b009edcbbfa297c938c3b936b6caba8df814652f5e78a38e8a83a81babce59 + languageName: node + linkType: hard + +"@backstage/version-bridge@workspace:^, @backstage/version-bridge@workspace:packages/version-bridge": version: 0.0.0-use.local resolution: "@backstage/version-bridge@workspace:packages/version-bridge" dependencies: @@ -10519,6 +10840,15 @@ __metadata: languageName: node linkType: hard +"@emotion/is-prop-valid@npm:^0.8.2": + version: 0.8.8 + resolution: "@emotion/is-prop-valid@npm:0.8.8" + dependencies: + "@emotion/memoize": 0.7.4 + checksum: bb7ec6d48c572c540e24e47cc94fc2f8dec2d6a342ae97bc9c8b6388d9b8d283862672172a1bb62d335c02662afe6291e10c71e9b8642664a8b43416cdceffac + languageName: node + linkType: hard + "@emotion/is-prop-valid@npm:^1.1.0, @emotion/is-prop-valid@npm:^1.2.1": version: 1.2.1 resolution: "@emotion/is-prop-valid@npm:1.2.1" @@ -10528,6 +10858,13 @@ __metadata: languageName: node linkType: hard +"@emotion/memoize@npm:0.7.4": + version: 0.7.4 + resolution: "@emotion/memoize@npm:0.7.4" + checksum: 4e3920d4ec95995657a37beb43d3f4b7d89fed6caa2b173a4c04d10482d089d5c3ea50bbc96618d918b020f26ed6e9c4026bbd45433566576c1f7b056c3271dc + languageName: node + linkType: hard + "@emotion/memoize@npm:^0.8.1": version: 0.8.1 resolution: "@emotion/memoize@npm:0.8.1" @@ -10640,36 +10977,6 @@ __metadata: languageName: node linkType: hard -"@esbuild-kit/cjs-loader@npm:^2.4.1": - version: 2.4.2 - resolution: "@esbuild-kit/cjs-loader@npm:2.4.2" - dependencies: - "@esbuild-kit/core-utils": ^3.0.0 - get-tsconfig: ^4.4.0 - checksum: e346e339bfc7eff5c52c270fd0ec06a7f2341b624adfb69f84b7d83f119c35070420906f2761a0b4604e0a0ec90e35eaf12544585476c428ed6d6ee3b250c0fe - languageName: node - linkType: hard - -"@esbuild-kit/core-utils@npm:^3.0.0, @esbuild-kit/core-utils@npm:^3.3.2": - version: 3.3.2 - resolution: "@esbuild-kit/core-utils@npm:3.3.2" - dependencies: - esbuild: ~0.18.20 - source-map-support: ^0.5.21 - checksum: 62f3b97457fa4ef39d752bd2ad1c8adac08929b50c411f5259f105cc74896f1fdb3429a540aa423e8eae37f32ef44656ca21ccb9a723cd9955d65a820960ab1f - languageName: node - linkType: hard - -"@esbuild-kit/esm-loader@npm:^2.5.5": - version: 2.6.5 - resolution: "@esbuild-kit/esm-loader@npm:2.6.5" - dependencies: - "@esbuild-kit/core-utils": ^3.3.2 - get-tsconfig: ^4.7.0 - checksum: 88a27203898f14bd69f03244ae2b3bae727e00243d7801292c4da8caba69813b0978fb8245a55b83bc9b907f475ed634f094a1f44cc590c0754993f4a924cc22 - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.16.17": version: 0.16.17 resolution: "@esbuild/android-arm64@npm:0.16.17" @@ -10684,9 +10991,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/android-arm64@npm:0.19.4" +"@esbuild/android-arm64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/android-arm64@npm:0.19.5" conditions: os=android & cpu=arm64 languageName: node linkType: hard @@ -10705,9 +11012,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/android-arm@npm:0.19.4" +"@esbuild/android-arm@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/android-arm@npm:0.19.5" conditions: os=android & cpu=arm languageName: node linkType: hard @@ -10726,9 +11033,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/android-x64@npm:0.19.4" +"@esbuild/android-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/android-x64@npm:0.19.5" conditions: os=android & cpu=x64 languageName: node linkType: hard @@ -10747,9 +11054,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/darwin-arm64@npm:0.19.4" +"@esbuild/darwin-arm64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/darwin-arm64@npm:0.19.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard @@ -10768,9 +11075,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/darwin-x64@npm:0.19.4" +"@esbuild/darwin-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/darwin-x64@npm:0.19.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard @@ -10789,9 +11096,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/freebsd-arm64@npm:0.19.4" +"@esbuild/freebsd-arm64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/freebsd-arm64@npm:0.19.5" conditions: os=freebsd & cpu=arm64 languageName: node linkType: hard @@ -10810,9 +11117,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/freebsd-x64@npm:0.19.4" +"@esbuild/freebsd-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/freebsd-x64@npm:0.19.5" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard @@ -10831,9 +11138,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-arm64@npm:0.19.4" +"@esbuild/linux-arm64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-arm64@npm:0.19.5" conditions: os=linux & cpu=arm64 languageName: node linkType: hard @@ -10852,9 +11159,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-arm@npm:0.19.4" +"@esbuild/linux-arm@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-arm@npm:0.19.5" conditions: os=linux & cpu=arm languageName: node linkType: hard @@ -10873,9 +11180,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-ia32@npm:0.19.4" +"@esbuild/linux-ia32@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-ia32@npm:0.19.5" conditions: os=linux & cpu=ia32 languageName: node linkType: hard @@ -10894,9 +11201,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-loong64@npm:0.19.4" +"@esbuild/linux-loong64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-loong64@npm:0.19.5" conditions: os=linux & cpu=loong64 languageName: node linkType: hard @@ -10915,9 +11222,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-mips64el@npm:0.19.4" +"@esbuild/linux-mips64el@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-mips64el@npm:0.19.5" conditions: os=linux & cpu=mips64el languageName: node linkType: hard @@ -10936,9 +11243,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-ppc64@npm:0.19.4" +"@esbuild/linux-ppc64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-ppc64@npm:0.19.5" conditions: os=linux & cpu=ppc64 languageName: node linkType: hard @@ -10957,9 +11264,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-riscv64@npm:0.19.4" +"@esbuild/linux-riscv64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-riscv64@npm:0.19.5" conditions: os=linux & cpu=riscv64 languageName: node linkType: hard @@ -10978,9 +11285,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-s390x@npm:0.19.4" +"@esbuild/linux-s390x@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-s390x@npm:0.19.5" conditions: os=linux & cpu=s390x languageName: node linkType: hard @@ -10999,9 +11306,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/linux-x64@npm:0.19.4" +"@esbuild/linux-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/linux-x64@npm:0.19.5" conditions: os=linux & cpu=x64 languageName: node linkType: hard @@ -11020,9 +11327,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/netbsd-x64@npm:0.19.4" +"@esbuild/netbsd-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/netbsd-x64@npm:0.19.5" conditions: os=netbsd & cpu=x64 languageName: node linkType: hard @@ -11041,9 +11348,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/openbsd-x64@npm:0.19.4" +"@esbuild/openbsd-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/openbsd-x64@npm:0.19.5" conditions: os=openbsd & cpu=x64 languageName: node linkType: hard @@ -11062,9 +11369,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/sunos-x64@npm:0.19.4" +"@esbuild/sunos-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/sunos-x64@npm:0.19.5" conditions: os=sunos & cpu=x64 languageName: node linkType: hard @@ -11083,9 +11390,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/win32-arm64@npm:0.19.4" +"@esbuild/win32-arm64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/win32-arm64@npm:0.19.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard @@ -11104,9 +11411,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/win32-ia32@npm:0.19.4" +"@esbuild/win32-ia32@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/win32-ia32@npm:0.19.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard @@ -11125,9 +11432,9 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.19.4": - version: 0.19.4 - resolution: "@esbuild/win32-x64@npm:0.19.4" +"@esbuild/win32-x64@npm:0.19.5": + version: 0.19.5 + resolution: "@esbuild/win32-x64@npm:0.19.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard @@ -11224,6 +11531,13 @@ __metadata: languageName: node linkType: hard +"@faker-js/faker@npm:5.5.3": + version: 5.5.3 + resolution: "@faker-js/faker@npm:5.5.3" + checksum: d248a042e47ac00613d2d7cc29d4504cc5e5d843162454eede8c35f31c74b19a8fd7cecc0d5ea9e3fbbfc812abc51143c3699e51049fd64300e23e6588e76d39 + languageName: node + linkType: hard + "@fastify/busboy@npm:^2.0.0": version: 2.0.0 resolution: "@fastify/busboy@npm:2.0.0" @@ -11250,7 +11564,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.0.2": +"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.0.2": version: 2.0.2 resolution: "@floating-ui/react-dom@npm:2.0.2" dependencies: @@ -11426,6 +11740,33 @@ __metadata: languageName: node linkType: hard +"@graphiql/react@npm:^0.19.4": + version: 0.19.4 + resolution: "@graphiql/react@npm:0.19.4" + dependencies: + "@graphiql/toolkit": ^0.9.1 + "@headlessui/react": ^1.7.15 + "@radix-ui/react-dialog": ^1.0.4 + "@radix-ui/react-dropdown-menu": ^2.0.5 + "@radix-ui/react-tooltip": ^1.0.6 + "@radix-ui/react-visually-hidden": ^1.0.3 + "@types/codemirror": ^5.60.8 + clsx: ^1.2.1 + codemirror: ^5.65.3 + codemirror-graphql: ^2.0.10 + copy-to-clipboard: ^3.2.0 + framer-motion: ^6.5.1 + graphql-language-service: ^5.2.0 + markdown-it: ^12.2.0 + set-value: ^4.1.0 + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: 08eb53fc0449d5814ba92faff8e03ba77e97ecd4d3d473d115d1219e52b59d9a1cc1b0f34c41315b3a0185fb862646c1affd2094e1f5f524328a95c8e4e8b591 + languageName: node + linkType: hard + "@graphiql/toolkit@npm:^0.6.1": version: 0.6.1 resolution: "@graphiql/toolkit@npm:0.6.1" @@ -11439,6 +11780,22 @@ __metadata: languageName: node linkType: hard +"@graphiql/toolkit@npm:^0.9.1": + version: 0.9.1 + resolution: "@graphiql/toolkit@npm:0.9.1" + dependencies: + "@n1ru4l/push-pull-async-iterable-iterator": ^3.1.0 + meros: ^1.1.4 + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + graphql-ws: ">= 4.5.0" + peerDependenciesMeta: + graphql-ws: + optional: true + checksum: 5328426051b7f9a9ffbd569c950d1a103ce0e2ee7b5d7a57f3d899488ad43d1a5101e8aeced7416e106c7687d67bb7981aa7e87dea5b0f17b77569aa738bf3b5 + languageName: node + linkType: hard + "@graphql-codegen/cli@npm:^3.0.0": version: 3.3.1 resolution: "@graphql-codegen/cli@npm:3.3.1" @@ -11888,6 +12245,18 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/merge@npm:8.3.1": + version: 8.3.1 + resolution: "@graphql-tools/merge@npm:8.3.1" + dependencies: + "@graphql-tools/utils": 8.9.0 + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 16af6be2249f4f500a4c2f5d3db2e0efd56ad69b5e10499649c6fc979c257af12e131112304a16699654b54daab37a80737e0538478bc45a0053b9bc859a7ac1 + languageName: node + linkType: hard + "@graphql-tools/merge@npm:8.3.14": version: 8.3.14 resolution: "@graphql-tools/merge@npm:8.3.14" @@ -12031,6 +12400,20 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/schema@npm:^8.5.0": + version: 8.5.1 + resolution: "@graphql-tools/schema@npm:8.5.1" + dependencies: + "@graphql-tools/merge": 8.3.1 + "@graphql-tools/utils": 8.9.0 + tslib: ^2.4.0 + value-or-promise: 1.0.11 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: 91363cd4371e347af40ef66f7d903b5d4f5998bfaec9214768e6a795136ef6372f9f225e05e18daacd929e23695811f15e791c6cbe082bf5b5d03b16b1f874f8 + languageName: node + linkType: hard + "@graphql-tools/schema@npm:^9.0.0": version: 9.0.19 resolution: "@graphql-tools/schema@npm:9.0.19" @@ -12148,6 +12531,17 @@ __metadata: languageName: node linkType: hard +"@graphql-tools/utils@npm:^8.8.0": + version: 8.13.1 + resolution: "@graphql-tools/utils@npm:8.13.1" + dependencies: + tslib: ^2.4.0 + peerDependencies: + graphql: ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0 + checksum: ff04fdeb29e9ac596ea53386cd5b23cd741bb14c1997c6b0ba3c34ca165bd82b528a355e8c8e2ba726eb39e833ba9cbb0851ba0addb8c6d367089a1145bf9a49 + languageName: node + linkType: hard + "@graphql-tools/wrap@npm:9.2.23": version: 9.2.23 resolution: "@graphql-tools/wrap@npm:9.2.23" @@ -12221,6 +12615,58 @@ __metadata: languageName: node linkType: hard +"@headlessui/react@npm:^1.7.15": + version: 1.7.17 + resolution: "@headlessui/react@npm:1.7.17" + dependencies: + client-only: ^0.0.1 + peerDependencies: + react: ^16 || ^17 || ^18 + react-dom: ^16 || ^17 || ^18 + checksum: 0cdb67747e7f606f78214dac0b48573247779e70534b4471515c094b74addda173dc6a9847d33aea9c6e6bc151016c034125328953077e32aa7947ebabed91f7 + languageName: node + linkType: hard + +"@httptoolkit/httpolyglot@npm:^2.0.1, @httptoolkit/httpolyglot@npm:^2.1.1": + version: 2.1.1 + resolution: "@httptoolkit/httpolyglot@npm:2.1.1" + dependencies: + "@types/node": ^16.7.10 + checksum: 138ccd61355de334c509e2fc4ac9ade9e1aa6aa770ed2271e0bd1d883ed815eb742d0a4de37837edd03a9a243c05d6da32c5febe970f4518c46e2d76e6ff10d5 + languageName: node + linkType: hard + +"@httptoolkit/subscriptions-transport-ws@npm:^0.11.2": + version: 0.11.2 + resolution: "@httptoolkit/subscriptions-transport-ws@npm:0.11.2" + dependencies: + backo2: ^1.0.2 + eventemitter3: ^3.1.0 + iterall: ^1.2.1 + symbol-observable: ^1.0.4 + ws: ^8.8.0 + peerDependencies: + graphql: ^15.7.2 || ^16.0.0 + checksum: a2d99b4d8e46b46fd5d4fac3456fa685dba7d876908e632c73af014fdcc92ae1f77f8c542e8b63ae747a164e9d2e4be95c5046665f9e7b5622f02dc6d7d04549 + languageName: node + linkType: hard + +"@httptoolkit/websocket-stream@npm:^6.0.1": + version: 6.0.1 + resolution: "@httptoolkit/websocket-stream@npm:6.0.1" + dependencies: + "@types/ws": "*" + duplexify: ^3.5.1 + inherits: ^2.0.1 + isomorphic-ws: ^4.0.1 + readable-stream: ^2.3.3 + safe-buffer: ^5.1.2 + ws: "*" + xtend: ^4.0.0 + checksum: e70059c24499abab695e7bc269aefc1a751d161296975a4af932577497c4ecd66b7745dc0c63608e06989442db996d76e563bce08156563bac7bc3411ad9bcee + languageName: node + linkType: hard + "@humanwhocodes/config-array@npm:^0.11.11": version: 0.11.11 resolution: "@humanwhocodes/config-array@npm:0.11.11" @@ -12389,50 +12835,50 @@ __metadata: languageName: node linkType: hard -"@jest/console@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/console@npm:29.4.3" +"@jest/console@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/console@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 - jest-message-util: ^29.4.3 - jest-util: ^29.4.3 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 - checksum: 8d9b163febe735153b523db527742309f4d598eda22f17f04e030060329bd3da4de7420fc1f7812f7a16f08273654a7de094c4b4e8b81a99dbfc17cfb1629008 + checksum: 0e3624e32c5a8e7361e889db70b170876401b7d70f509a2538c31d5cd50deb0c1ae4b92dc63fe18a0902e0a48c590c21d53787a0df41a52b34fa7cab96c384d6 languageName: node linkType: hard -"@jest/core@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/core@npm:29.4.3" +"@jest/core@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/core@npm:29.7.0" dependencies: - "@jest/console": ^29.4.3 - "@jest/reporters": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/transform": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/console": ^29.7.0 + "@jest/reporters": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 ci-info: ^3.2.0 exit: ^0.1.2 graceful-fs: ^4.2.9 - jest-changed-files: ^29.4.3 - jest-config: ^29.4.3 - jest-haste-map: ^29.4.3 - jest-message-util: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.4.3 - jest-resolve-dependencies: ^29.4.3 - jest-runner: ^29.4.3 - jest-runtime: ^29.4.3 - jest-snapshot: ^29.4.3 - jest-util: ^29.4.3 - jest-validate: ^29.4.3 - jest-watcher: ^29.4.3 + jest-changed-files: ^29.7.0 + jest-config: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-resolve-dependencies: ^29.7.0 + jest-runner: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 + jest-watcher: ^29.7.0 micromatch: ^4.0.4 - pretty-format: ^29.4.3 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-ansi: ^6.0.0 peerDependencies: @@ -12440,7 +12886,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 4aa10644d66f44f051d5dd9cdcedce27acc71216dbcc5e7adebdea458e27aefe27c78f457d7efd49f58b968c35f42de5a521590876e2013593e675120b9e6ab1 + checksum: af759c9781cfc914553320446ce4e47775ae42779e73621c438feb1e4231a5d4862f84b1d8565926f2d1aab29b3ec3dcfdc84db28608bdf5f29867124ebcfc0d languageName: node linkType: hard @@ -12453,15 +12899,15 @@ __metadata: languageName: node linkType: hard -"@jest/environment@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/environment@npm:29.4.3" +"@jest/environment@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/environment@npm:29.7.0" dependencies: - "@jest/fake-timers": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.4.3 - checksum: 7c1b0cc4e84b90f8a3bbeca9bbf088882c88aee70a81b3b8e24265dcb1cbc302cd1eee3319089cf65bfd39adbaea344903c712afea106cb8da6c86088d99c5fb + jest-mock: ^29.7.0 + checksum: 6fb398143b2543d4b9b8d1c6dbce83fa5247f84f550330604be744e24c2bd2178bb893657d62d1b97cf2f24baf85c450223f8237cccb71192c36a38ea2272934 languageName: node linkType: hard @@ -12474,61 +12920,61 @@ __metadata: languageName: node linkType: hard -"@jest/expect-utils@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/expect-utils@npm:29.4.3" +"@jest/expect-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect-utils@npm:29.7.0" dependencies: - jest-get-type: ^29.4.3 - checksum: 2bbed39ff2fb59f5acac465a1ce7303e3b4b62b479e4f386261986c9827f7f799ea912761e22629c5daf10addf8513f16733c14a29c2647bb66d4ee625e9ff92 + jest-get-type: ^29.6.3 + checksum: 75eb177f3d00b6331bcaa057e07c0ccb0733a1d0a1943e1d8db346779039cb7f103789f16e502f888a3096fb58c2300c38d1f3748b36a7fa762eb6f6d1b160ed languageName: node linkType: hard -"@jest/expect@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/expect@npm:29.4.3" +"@jest/expect@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/expect@npm:29.7.0" dependencies: - expect: ^29.4.3 - jest-snapshot: ^29.4.3 - checksum: 08d0d40077ec99a7491fe59d05821dbd31126cfba70875855d8a063698b7126b5f6c309c50811caacc6ae2f727c6e44f51bdcf1d6c1ea832b4f020045ef22d45 + expect: ^29.7.0 + jest-snapshot: ^29.7.0 + checksum: a01cb85fd9401bab3370618f4b9013b90c93536562222d920e702a0b575d239d74cecfe98010aaec7ad464f67cf534a353d92d181646a4b792acaa7e912ae55e languageName: node linkType: hard -"@jest/fake-timers@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/fake-timers@npm:29.4.3" +"@jest/fake-timers@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/fake-timers@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@sinonjs/fake-timers": ^10.0.2 "@types/node": "*" - jest-message-util: ^29.4.3 - jest-mock: ^29.4.3 - jest-util: ^29.4.3 - checksum: adaceb9143c395cccf3d7baa0e49b7042c3092a554e8283146df19926247e34c21b5bde5688bb90e9e87b4a02e4587926c5d858ee0a38d397a63175d0a127874 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: caf2bbd11f71c9241b458d1b5a66cbe95debc5a15d96442444b5d5c7ba774f523c76627c6931cca5e10e76f0d08761f6f1f01a608898f4751a0eee54fc3d8d00 languageName: node linkType: hard -"@jest/globals@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/globals@npm:29.4.3" +"@jest/globals@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/globals@npm:29.7.0" dependencies: - "@jest/environment": ^29.4.3 - "@jest/expect": ^29.4.3 - "@jest/types": ^29.4.3 - jest-mock: ^29.4.3 - checksum: ea76b546ceb4aa5ce2bb3726df12f989b23150b51c9f7664790caa81b943012a657cf3a8525498af1c3518cdb387f54b816cfba1b0ddd22c7b20f03b1d7290b4 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/types": ^29.6.3 + jest-mock: ^29.7.0 + checksum: 97dbb9459135693ad3a422e65ca1c250f03d82b2a77f6207e7fa0edd2c9d2015fbe4346f3dc9ebff1678b9d8da74754d4d440b7837497f8927059c0642a22123 languageName: node linkType: hard -"@jest/reporters@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/reporters@npm:29.4.3" +"@jest/reporters@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/reporters@npm:29.7.0" dependencies: "@bcoe/v8-coverage": ^0.2.3 - "@jest/console": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/transform": ^29.4.3 - "@jest/types": ^29.4.3 - "@jridgewell/trace-mapping": ^0.3.15 + "@jest/console": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 + "@jridgewell/trace-mapping": ^0.3.18 "@types/node": "*" chalk: ^4.0.0 collect-v8-coverage: ^1.0.0 @@ -12536,13 +12982,13 @@ __metadata: glob: ^7.1.3 graceful-fs: ^4.2.9 istanbul-lib-coverage: ^3.0.0 - istanbul-lib-instrument: ^5.1.0 + istanbul-lib-instrument: ^6.0.0 istanbul-lib-report: ^3.0.0 istanbul-lib-source-maps: ^4.0.0 istanbul-reports: ^3.1.3 - jest-message-util: ^29.4.3 - jest-util: ^29.4.3 - jest-worker: ^29.4.3 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 slash: ^3.0.0 string-length: ^4.0.1 strip-ansi: ^6.0.0 @@ -12552,7 +12998,7 @@ __metadata: peerDependenciesMeta: node-notifier: optional: true - checksum: 7aa2e429c915bd96c3334962addd69d2bbf52065725757ddde26b293f8c4420a1e8c65363cc3e1e5ec89100a5273ccd3771bec58325a2cc0d97afdc81995073a + checksum: 7eadabd62cc344f629024b8a268ecc8367dba756152b761bdcb7b7e570a3864fc51b2a9810cd310d85e0a0173ef002ba4528d5ea0329fbf66ee2a3ada9c40455 languageName: node linkType: hard @@ -12565,7 +13011,7 @@ __metadata: languageName: node linkType: hard -"@jest/schemas@npm:^29.4.3, @jest/schemas@npm:^29.6.3": +"@jest/schemas@npm:^29.6.3": version: 29.6.3 resolution: "@jest/schemas@npm:29.6.3" dependencies: @@ -12574,61 +13020,61 @@ __metadata: languageName: node linkType: hard -"@jest/source-map@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/source-map@npm:29.4.3" +"@jest/source-map@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/source-map@npm:29.6.3" dependencies: - "@jridgewell/trace-mapping": ^0.3.15 + "@jridgewell/trace-mapping": ^0.3.18 callsites: ^3.0.0 graceful-fs: ^4.2.9 - checksum: 2301d225145f8123540c0be073f35a80fd26a2f5e59550fd68525d8cea580fb896d12bf65106591ffb7366a8a19790076dbebc70e0f5e6ceb51f81827ed1f89c + checksum: bcc5a8697d471396c0003b0bfa09722c3cd879ad697eb9c431e6164e2ea7008238a01a07193dfe3cbb48b1d258eb7251f6efcea36f64e1ebc464ea3c03ae2deb languageName: node linkType: hard -"@jest/test-result@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/test-result@npm:29.4.3" +"@jest/test-result@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-result@npm:29.7.0" dependencies: - "@jest/console": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/console": ^29.7.0 + "@jest/types": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 collect-v8-coverage: ^1.0.0 - checksum: 164f102b96619ec283c2c39e208b8048e4674f75bf3c3a4f2e95048ae0f9226105add684b25f10d286d91c221625f877e2c1cfc3da46c42d7e1804da239318cb + checksum: 67b6317d526e335212e5da0e768e3b8ab8a53df110361b80761353ad23b6aea4432b7c5665bdeb87658ea373b90fb1afe02ed3611ef6c858c7fba377505057fa languageName: node linkType: hard -"@jest/test-sequencer@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/test-sequencer@npm:29.4.3" +"@jest/test-sequencer@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/test-sequencer@npm:29.7.0" dependencies: - "@jest/test-result": ^29.4.3 + "@jest/test-result": ^29.7.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.4.3 + jest-haste-map: ^29.7.0 slash: ^3.0.0 - checksum: 145e1fa9379e5be3587bde6d585b8aee5cf4442b06926928a87e9aec7de5be91b581711d627c6ca13144d244fe05e5d248c13b366b51bedc404f9dcfbfd79e9e + checksum: 73f43599017946be85c0b6357993b038f875b796e2f0950487a82f4ebcb115fa12131932dd9904026b4ad8be131fe6e28bd8d0aa93b1563705185f9804bff8bd languageName: node linkType: hard -"@jest/transform@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/transform@npm:29.4.3" +"@jest/transform@npm:^29.7.0": + version: 29.7.0 + resolution: "@jest/transform@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/types": ^29.4.3 - "@jridgewell/trace-mapping": ^0.3.15 + "@jest/types": ^29.6.3 + "@jridgewell/trace-mapping": ^0.3.18 babel-plugin-istanbul: ^6.1.1 chalk: ^4.0.0 convert-source-map: ^2.0.0 fast-json-stable-stringify: ^2.1.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-util: ^29.4.3 + jest-haste-map: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 micromatch: ^4.0.4 pirates: ^4.0.4 slash: ^3.0.0 write-file-atomic: ^4.0.2 - checksum: 082d74e04044213aa7baa8de29f8383e5010034f867969c8602a2447a4ef2f484cfaf2491eba3179ce42f369f7a0af419cbd087910f7e5caf7aa5d1fe03f2ff9 + checksum: 0f8ac9f413903b3cb6d240102db848f2a354f63971ab885833799a9964999dd51c388162106a807f810071f864302cdd8e3f0c241c29ce02d85a36f18f3f40ab languageName: node linkType: hard @@ -12659,17 +13105,17 @@ __metadata: languageName: node linkType: hard -"@jest/types@npm:^29.4.3": - version: 29.4.3 - resolution: "@jest/types@npm:29.4.3" +"@jest/types@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/types@npm:29.6.3" dependencies: - "@jest/schemas": ^29.4.3 + "@jest/schemas": ^29.6.3 "@types/istanbul-lib-coverage": ^2.0.0 "@types/istanbul-reports": ^3.0.0 "@types/node": "*" "@types/yargs": ^17.0.8 chalk: ^4.0.0 - checksum: 1756f4149d360f98567f56f434144f7af23ed49a2c42889261a314df6b6654c2de70af618fb2ee0ee39cadaf10835b885845557184509503646c9cb9dcc02bac + checksum: a0bcf15dbb0eca6bdd8ce61a3fb055349d40268622a7670a3b2eb3c3dbafe9eb26af59938366d520b86907b9505b0f9b29b85cec11579a9e580694b87cd90fcc languageName: node linkType: hard @@ -12691,10 +13137,10 @@ __metadata: languageName: node linkType: hard -"@jridgewell/resolve-uri@npm:3.1.0, @jridgewell/resolve-uri@npm:^3.0.3": - version: 3.1.0 - resolution: "@jridgewell/resolve-uri@npm:3.1.0" - checksum: b5ceaaf9a110fcb2780d1d8f8d4a0bfd216702f31c988d8042e5f8fbe353c55d9b0f55a1733afdc64806f8e79c485d2464680ac48a0d9fcadb9548ee6b81d267 +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.1 + resolution: "@jridgewell/resolve-uri@npm:3.1.1" + checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 languageName: node linkType: hard @@ -12715,10 +13161,10 @@ __metadata: languageName: node linkType: hard -"@jridgewell/sourcemap-codec@npm:1.4.14, @jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.13, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 languageName: node linkType: hard @@ -12732,13 +13178,13 @@ __metadata: languageName: node linkType: hard -"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.15, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.9": - version: 0.3.17 - resolution: "@jridgewell/trace-mapping@npm:0.3.17" +"@jridgewell/trace-mapping@npm:^0.3.12, @jridgewell/trace-mapping@npm:^0.3.17, @jridgewell/trace-mapping@npm:^0.3.18, @jridgewell/trace-mapping@npm:^0.3.9": + version: 0.3.20 + resolution: "@jridgewell/trace-mapping@npm:0.3.20" dependencies: - "@jridgewell/resolve-uri": 3.1.0 - "@jridgewell/sourcemap-codec": 1.4.14 - checksum: 9d703b859cff5cd83b7308fd457a431387db5db96bd781a63bf48e183418dd9d3d44e76b9e4ae13237f6abeeb25d739ec9215c1d5bfdd08f66f750a50074a339 + "@jridgewell/resolve-uri": ^3.1.0 + "@jridgewell/sourcemap-codec": ^1.4.14 + checksum: cd1a7353135f385909468ff0cf20bdd37e59f2ee49a13a966dedf921943e222082c583ade2b579ff6cd0d8faafcb5461f253e1bf2a9f48fec439211fdbe788f5 languageName: node linkType: hard @@ -13357,6 +13803,71 @@ __metadata: languageName: node linkType: hard +"@motionone/animation@npm:^10.12.0": + version: 10.16.3 + resolution: "@motionone/animation@npm:10.16.3" + dependencies: + "@motionone/easing": ^10.16.3 + "@motionone/types": ^10.16.3 + "@motionone/utils": ^10.16.3 + tslib: ^2.3.1 + checksum: 797cacea335e6f892af27579eff51450dcf18c5bbc5c0ca44a000929b21857f4afb974ffb411c4935bfbd01ef2ddb3ef542ba3313ae66e1e5392b5d314df6ad3 + languageName: node + linkType: hard + +"@motionone/dom@npm:10.12.0": + version: 10.12.0 + resolution: "@motionone/dom@npm:10.12.0" + dependencies: + "@motionone/animation": ^10.12.0 + "@motionone/generators": ^10.12.0 + "@motionone/types": ^10.12.0 + "@motionone/utils": ^10.12.0 + hey-listen: ^1.0.8 + tslib: ^2.3.1 + checksum: 123356f28e44362c4f081aae3df22e576f46bfcb07e01257b2ac64a115668448f29b8de67e4b6e692c5407cffb78ffe7cf9fa1bc064007482bab5dd23a69d380 + languageName: node + linkType: hard + +"@motionone/easing@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/easing@npm:10.16.3" + dependencies: + "@motionone/utils": ^10.16.3 + tslib: ^2.3.1 + checksum: 03e2460cdd35ee4967a86ce28ffbaaaca589263f659f652801cf6bd667baba9b3d5ce6d134df6b64413b60b34dd21d7c38b0cd8a4c3e1ed789789cdb971905b2 + languageName: node + linkType: hard + +"@motionone/generators@npm:^10.12.0": + version: 10.16.4 + resolution: "@motionone/generators@npm:10.16.4" + dependencies: + "@motionone/types": ^10.16.3 + "@motionone/utils": ^10.16.3 + tslib: ^2.3.1 + checksum: 185091c5cfbe67c38e84bf3920d1b5862e5d7eb624136494a7e4779b2f9d06855ebe3e633d95dcc5a1735d92d59d1ae28a0724c2f9d8bddd60fc9bc3603fab48 + languageName: node + linkType: hard + +"@motionone/types@npm:^10.12.0, @motionone/types@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/types@npm:10.16.3" + checksum: ff38982f5aff2c0abbc3051c843d186d6f954c971e97dd6fced97a4ef50ee04f6e49607541ebb80e14dd143cf63553c388392110e270d04eca23f6b529f7f321 + languageName: node + linkType: hard + +"@motionone/utils@npm:^10.12.0, @motionone/utils@npm:^10.16.3": + version: 10.16.3 + resolution: "@motionone/utils@npm:10.16.3" + dependencies: + "@motionone/types": ^10.16.3 + hey-listen: ^1.0.8 + tslib: ^2.3.1 + checksum: d06025911c54c2217c98026cd38d4d681268a2b9b2830ac7342820881ba6be09721dd03626f52547749ead0543d5e2f2a69c9270ffdeaabc0949f7afb3233817 + languageName: node + linkType: hard + "@mswjs/cookies@npm:^0.2.2": version: 0.2.2 resolution: "@mswjs/cookies@npm:0.2.2" @@ -14199,7 +14710,7 @@ __metadata: languageName: node linkType: hard -"@octokit/rest@npm:^19.0.3": +"@octokit/rest@npm:^19.0.0, @octokit/rest@npm:^19.0.3": version: 19.0.13 resolution: "@octokit/rest@npm:19.0.13" dependencies: @@ -14597,6 +15108,564 @@ __metadata: languageName: node linkType: hard +"@radix-ui/primitive@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/primitive@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: 2b93e161d3fdabe9a64919def7fa3ceaecf2848341e9211520c401181c9eaebb8451c630b066fad2256e5c639c95edc41de0ba59c40eff37e799918d019822d1 + languageName: node + linkType: hard + +"@radix-ui/react-arrow@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-arrow@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 8cca086f0dbb33360e3c0142adf72f99fc96352d7086d6c2356dbb2ea5944cfb720a87d526fc48087741c602cd8162ca02b0af5e6fdf5f56d20fddb44db8b4c3 + languageName: node + linkType: hard + +"@radix-ui/react-collection@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-collection@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-slot": 1.0.2 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: acfbc9b0b2c553d343c22f02c9f098bc5cfa99e6e48df91c0d671855013f8b877ade9c657b7420a7aa523b5aceadea32a60dd72c23b1291f415684fb45d00cff + languageName: node + linkType: hard + +"@radix-ui/react-compose-refs@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-compose-refs@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2b9a613b6db5bff8865588b6bf4065f73021b3d16c0a90b2d4c23deceeb63612f1f15de188227ebdc5f88222cab031be617a9dd025874c0487b303be3e5cc2a8 + languageName: node + linkType: hard + +"@radix-ui/react-context@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-context@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 60e9b81d364f40c91a6213ec953f7c64fcd9d75721205a494a5815b3e5ae0719193429b62ee6c7002cd6aaf70f8c0e2f08bdbaba9ffcc233044d32b56d2127d1 + languageName: node + linkType: hard + +"@radix-ui/react-dialog@npm:^1.0.4": + version: 1.0.5 + resolution: "@radix-ui/react-dialog@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.5 + "@radix-ui/react-focus-guards": 1.0.1 + "@radix-ui/react-focus-scope": 1.0.4 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-portal": 1.0.4 + "@radix-ui/react-presence": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-use-controllable-state": 1.0.1 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 3d11ca31afb794a6dd286005ab7894cb0ce7bc2de5481de98900470b11d495256401306763de030f5e35aa545ff90d34632ffd54a1b29bf55afba813be4bb84a + languageName: node + linkType: hard + +"@radix-ui/react-direction@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-direction@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 5336a8b0d4f1cde585d5c2b4448af7b3d948bb63a1aadb37c77771b0e5902dc6266e409cf35fd0edaca7f33e26424be19e64fb8f9d7f7be2d6f1714ea2764210 + languageName: node + linkType: hard + +"@radix-ui/react-dismissable-layer@npm:1.0.5": + version: 1.0.5 + resolution: "@radix-ui/react-dismissable-layer@npm:1.0.5" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-escape-keydown": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: e73cf4bd3763f4d55b1bea7486a9700384d7d94dc00b1d5a75e222b2f1e4f32bc667a206ca4ed3baaaf7424dce7a239afd0ba59a6f0d89c3462c4e6e8d029a04 + languageName: node + linkType: hard + +"@radix-ui/react-dropdown-menu@npm:^2.0.5": + version: 2.0.6 + resolution: "@radix-ui/react-dropdown-menu@npm:2.0.6" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-menu": 2.0.6 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 1433e04234c29ae688b1d50b4a5ad0fd67e2627a5ea2e5f60fec6e4307e673ef35a703672eae0d61d96156c59084bbb19de9f9b9936b3fc351917dfe41dcf403 + languageName: node + linkType: hard + +"@radix-ui/react-focus-guards@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-focus-guards@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 1f8ca8f83b884b3612788d0742f3f054e327856d90a39841a47897dbed95e114ee512362ae314177de226d05310047cabbf66b686ae86ad1b65b6b295be24ef7 + languageName: node + linkType: hard + +"@radix-ui/react-focus-scope@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-focus-scope@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 3481db1a641513a572734f0bcb0e47fefeba7bccd6ec8dde19f520719c783ef0b05a55ef0d5292078ed051cc5eda46b698d5d768da02e26e836022f46b376fd1 + languageName: node + linkType: hard + +"@radix-ui/react-id@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-id@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 446a453d799cc790dd2a1583ff8328da88271bff64530b5a17c102fa7fb35eece3cf8985359d416f65e330cd81aa7b8fe984ea125fc4f4eaf4b3801d698e49fe + languageName: node + linkType: hard + +"@radix-ui/react-menu@npm:2.0.6": + version: 2.0.6 + resolution: "@radix-ui/react-menu@npm:2.0.6" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-collection": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.5 + "@radix-ui/react-focus-guards": 1.0.1 + "@radix-ui/react-focus-scope": 1.0.4 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-popper": 1.1.3 + "@radix-ui/react-portal": 1.0.4 + "@radix-ui/react-presence": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-roving-focus": 1.0.4 + "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-use-callback-ref": 1.0.1 + aria-hidden: ^1.1.1 + react-remove-scroll: 2.5.5 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: a43fb560dbb5a4ddc43ea4e2434a9f517bbbcbf8b12e1e74c1e36666ad321aef7e39f91770140c106fe6f34e237102be8a02f3bc5588e6c06a709e20580c5e82 + languageName: node + linkType: hard + +"@radix-ui/react-popper@npm:1.1.3": + version: 1.1.3 + resolution: "@radix-ui/react-popper@npm:1.1.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@floating-ui/react-dom": ^2.0.0 + "@radix-ui/react-arrow": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.0.1 + "@radix-ui/react-use-rect": 1.0.1 + "@radix-ui/react-use-size": 1.0.1 + "@radix-ui/rect": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: b18a15958623f9222b6ed3e24b9fbcc2ba67b8df5a5272412f261de1592b3f05002af1c8b94c065830c3c74267ce00cf6c1d70d4d507ec92ba639501f98aa348 + languageName: node + linkType: hard + +"@radix-ui/react-portal@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-portal@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: c4cf35e2f26a89703189d0eef3ceeeb706ae0832e98e558730a5e929ca7c72c7cb510413a24eca94c7732f8d659a1e81942bec7b90540cb73ce9e4885d040b64 + languageName: node + linkType: hard + +"@radix-ui/react-presence@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-presence@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-use-layout-effect": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: ed2ff9faf9e4257a4065034d3771459e5a91c2d840b2fcec94661761704dbcb65bcdd927d28177a2a129b3dab5664eb90a9b88309afe0257a9f8ba99338c0d95 + languageName: node + linkType: hard + +"@radix-ui/react-primitive@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-primitive@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-slot": 1.0.2 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 9402bc22923c8e5c479051974a721c301535c36521c0237b83e5fa213d013174e77f3ad7905e6d60ef07e14f88ec7f4ea69891dc7a2b39047f8d3640e8f8d713 + languageName: node + linkType: hard + +"@radix-ui/react-roving-focus@npm:1.0.4": + version: 1.0.4 + resolution: "@radix-ui/react-roving-focus@npm:1.0.4" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-collection": 1.0.3 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-direction": 1.0.1 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-use-callback-ref": 1.0.1 + "@radix-ui/react-use-controllable-state": 1.0.1 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 69b1c82c2d9db3ba71549a848f2704200dab1b2cd22d050c1e081a78b9a567dbfdc7fd0403ee010c19b79652de69924d8ca2076cd031d6552901e4213493ffc7 + languageName: node + linkType: hard + +"@radix-ui/react-slot@npm:1.0.2": + version: 1.0.2 + resolution: "@radix-ui/react-slot@npm:1.0.2" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-compose-refs": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: edf5edf435ff594bea7e198bf16d46caf81b6fb559493acad4fa8c308218896136acb16f9b7238c788fd13e94a904f2fd0b6d834e530e4cae94522cdb8f77ce9 + languageName: node + linkType: hard + +"@radix-ui/react-tooltip@npm:^1.0.6": + version: 1.0.7 + resolution: "@radix-ui/react-tooltip@npm:1.0.7" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/primitive": 1.0.1 + "@radix-ui/react-compose-refs": 1.0.1 + "@radix-ui/react-context": 1.0.1 + "@radix-ui/react-dismissable-layer": 1.0.5 + "@radix-ui/react-id": 1.0.1 + "@radix-ui/react-popper": 1.1.3 + "@radix-ui/react-portal": 1.0.4 + "@radix-ui/react-presence": 1.0.1 + "@radix-ui/react-primitive": 1.0.3 + "@radix-ui/react-slot": 1.0.2 + "@radix-ui/react-use-controllable-state": 1.0.1 + "@radix-ui/react-visually-hidden": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 894d448c69a3e4d7626759f9f6c7997018fe8ef9cde098393bd83e10743d493dfd284eef041e46accc45486d5a5cd5f76d97f56afbdace7aed6e0cb14007bf15 + languageName: node + linkType: hard + +"@radix-ui/react-use-callback-ref@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-callback-ref@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: b9fd39911c3644bbda14a84e4fca080682bef84212b8d8931fcaa2d2814465de242c4cfd8d7afb3020646bead9c5e539d478cea0a7031bee8a8a3bb164f3bc4c + languageName: node + linkType: hard + +"@radix-ui/react-use-controllable-state@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-controllable-state@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: dee2be1937d293c3a492cb6d279fc11495a8f19dc595cdbfe24b434e917302f9ac91db24e8cc5af9a065f3f209c3423115b5442e65a5be9fd1e9091338972be9 + languageName: node + linkType: hard + +"@radix-ui/react-use-escape-keydown@npm:1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-use-escape-keydown@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-callback-ref": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: c6ed0d9ce780f67f924980eb305af1f6cce2a8acbaf043a58abe0aa3cc551d9aa76ccee14531df89bbee302ead7ecc7fce330886f82d4672c5eda52f357ef9b8 + languageName: node + linkType: hard + +"@radix-ui/react-use-layout-effect@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-layout-effect@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: bed9c7e8de243a5ec3b93bb6a5860950b0dba359b6680c84d57c7a655e123dec9b5891c5dfe81ab970652e7779fe2ad102a23177c7896dde95f7340817d47ae5 + languageName: node + linkType: hard + +"@radix-ui/react-use-rect@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-rect@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/rect": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 433f07e61e04eb222349825bb05f3591fca131313a1d03709565d6226d8660bd1d0423635553f95ee4fcc25c8f2050972d848808d753c388e2a9ae191ebf17f3 + languageName: node + linkType: hard + +"@radix-ui/react-use-size@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/react-use-size@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-use-layout-effect": 1.0.1 + peerDependencies: + "@types/react": "*" + react: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 6cc150ad1e9fa85019c225c5a5d50a0af6cdc4653dad0c21b4b40cd2121f36ee076db326c43e6bc91a69766ccff5a84e917d27970176b592577deea3c85a3e26 + languageName: node + linkType: hard + +"@radix-ui/react-visually-hidden@npm:1.0.3, @radix-ui/react-visually-hidden@npm:^1.0.3": + version: 1.0.3 + resolution: "@radix-ui/react-visually-hidden@npm:1.0.3" + dependencies: + "@babel/runtime": ^7.13.10 + "@radix-ui/react-primitive": 1.0.3 + peerDependencies: + "@types/react": "*" + "@types/react-dom": "*" + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 2e9d0c8253f97e7d6ffb2e52a5cfd40ba719f813b39c3e2e42c496d54408abd09ef66b5aec4af9b8ab0553215e32452a5d0934597a49c51dd90dc39181ed0d57 + languageName: node + linkType: hard + +"@radix-ui/rect@npm:1.0.1": + version: 1.0.1 + resolution: "@radix-ui/rect@npm:1.0.1" + dependencies: + "@babel/runtime": ^7.13.10 + checksum: aeec13b234a946052512d05239067d2d63422f9ec70bf2fe7acfd6b9196693fc33fbaf43c2667c167f777d90a095c6604eb487e0bce79e230b6df0f6cacd6a55 + languageName: node + linkType: hard + "@react-hookz/deep-equal@npm:^1.0.4": version: 1.0.4 resolution: "@react-hookz/deep-equal@npm:1.0.4" @@ -14841,6 +15910,22 @@ __metadata: languageName: node linkType: hard +"@rollup/plugin-inject@npm:^5.0.3": + version: 5.0.3 + resolution: "@rollup/plugin-inject@npm:5.0.3" + dependencies: + "@rollup/pluginutils": ^5.0.1 + estree-walker: ^2.0.2 + magic-string: ^0.27.0 + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + checksum: d8458b11af3447710ce200fe2886faff07bb054e1269a4f06f5f3c1a1b83019b6ce7761badfa116ca96fbb9c49f16b94ad02d1a72c2fb64dc68cb7dd81331cb7 + languageName: node + linkType: hard + "@rollup/plugin-json@npm:^5.0.0": version: 5.0.2 resolution: "@rollup/plugin-json@npm:5.0.2" @@ -14900,7 +15985,7 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.1": +"@rollup/pluginutils@npm:^4.1.1, @rollup/pluginutils@npm:^4.2.0, @rollup/pluginutils@npm:^4.2.1": version: 4.2.1 resolution: "@rollup/pluginutils@npm:4.2.1" dependencies: @@ -14911,8 +15996,8 @@ __metadata: linkType: hard "@rollup/pluginutils@npm:^5.0.1": - version: 5.0.2 - resolution: "@rollup/pluginutils@npm:5.0.2" + version: 5.0.4 + resolution: "@rollup/pluginutils@npm:5.0.4" dependencies: "@types/estree": ^1.0.0 estree-walker: ^2.0.2 @@ -14922,7 +16007,7 @@ __metadata: peerDependenciesMeta: rollup: optional: true - checksum: edea15e543bebc7dcac3b0ac8bc7b8e8e6dbd46e2864dbe5dd28072de1fbd5b0e10d545a610c0edaa178e8a7ac432e2a2a52e547ece1308471412caba47db8ce + checksum: 893d5805ac4121fc704926963a0ae4e79e9e2bc8d736c3b28499ab69a404cce5119ca3a4e0c3d3a81d62f1beb3966f35285c36935d94b061794f26e94fed4cd1 languageName: node linkType: hard @@ -14994,6 +16079,72 @@ __metadata: languageName: node linkType: hard +"@segment/loosely-validate-event@npm:^2.0.0": + version: 2.0.0 + resolution: "@segment/loosely-validate-event@npm:2.0.0" + dependencies: + component-type: ^1.2.1 + join-component: ^1.1.0 + checksum: 8c4aacc903fb717619b69ca7eecf8d4a7b928661b0e835c9cd98f1b858a85ce62c348369ad9a52cb2df8df02578c0525a73fce4c69a42ac414d9554cc6be7117 + languageName: node + linkType: hard + +"@sentry-internal/tracing@npm:7.70.0": + version: 7.70.0 + resolution: "@sentry-internal/tracing@npm:7.70.0" + dependencies: + "@sentry/core": 7.70.0 + "@sentry/types": 7.70.0 + "@sentry/utils": 7.70.0 + tslib: ^2.4.1 || ^1.9.3 + checksum: 51fe662ae5b4e26a9698515dbd47427714966c4e3f5df6f19d3d26c21150e17260e4b8c53e814a7e9274e8ca7b7994a4fac2838a71233cadcd4a30f394f9227e + languageName: node + linkType: hard + +"@sentry/core@npm:7.70.0": + version: 7.70.0 + resolution: "@sentry/core@npm:7.70.0" + dependencies: + "@sentry/types": 7.70.0 + "@sentry/utils": 7.70.0 + tslib: ^2.4.1 || ^1.9.3 + checksum: 550ff55f8232fbbe8263b05deca997b6fe98ae19fe445f39d90915cc515bdcc8d6c2ec09df6976fcf750547adc39aefb5714ebfd5f039b32e94c5c5320f4cfab + languageName: node + linkType: hard + +"@sentry/node@npm:7.70.0": + version: 7.70.0 + resolution: "@sentry/node@npm:7.70.0" + dependencies: + "@sentry-internal/tracing": 7.70.0 + "@sentry/core": 7.70.0 + "@sentry/types": 7.70.0 + "@sentry/utils": 7.70.0 + cookie: ^0.5.0 + https-proxy-agent: ^5.0.0 + lru_map: ^0.3.3 + tslib: ^2.4.1 || ^1.9.3 + checksum: 4d60023f0ddfca92ee15e42b5a207d998d683570103655282ce6e1a07a3bccbb86675810778f5500229cf21daef34e1f0fd48e0dbe3665d3df19d7b97c2788e5 + languageName: node + linkType: hard + +"@sentry/types@npm:7.70.0": + version: 7.70.0 + resolution: "@sentry/types@npm:7.70.0" + checksum: 0a38f47ccf0d995d8d5ab0353c332bd2573f044d993d746ec9ecee5e185da773ffdc13db3f29d525b4377ed3ce9235eaa0dc728c039739eb23050d0411667222 + languageName: node + linkType: hard + +"@sentry/utils@npm:7.70.0": + version: 7.70.0 + resolution: "@sentry/utils@npm:7.70.0" + dependencies: + "@sentry/types": 7.70.0 + tslib: ^2.4.1 || ^1.9.3 + checksum: a1590f5e752667910638262bb8e85f2e2c26e722df4087e79c39b11d1d81dbefaea8f426826a50b0832c079e77092ff7c2335d48e63a2d11c32b1c0b59b10eee + languageName: node + linkType: hard + "@short.io/opensearch-mock@npm:^0.3.1": version: 0.3.1 resolution: "@short.io/opensearch-mock@npm:0.3.1" @@ -15019,6 +16170,20 @@ __metadata: languageName: node linkType: hard +"@sinclair/typebox@npm:^0.31.0": + version: 0.31.18 + resolution: "@sinclair/typebox@npm:0.31.18" + checksum: 06bc64232394cc11a00ec8214696fcecab4e8af0c04da81fb21c69ccd69b398be55d42dedb5f007b8f49abdb6eb6045520785e362975a9af82563ce11032a91b + languageName: node + linkType: hard + +"@sindresorhus/is@npm:^0.14.0": + version: 0.14.0 + resolution: "@sindresorhus/is@npm:0.14.0" + checksum: 971e0441dd44ba3909b467219a5e242da0fc584048db5324cfb8048148fa8dcc9d44d71e3948972c4f6121d24e5da402ef191420d1266a95f713bb6d6e59c98a + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.0.0": version: 4.0.0 resolution: "@sindresorhus/is@npm:4.0.0" @@ -15089,13 +16254,13 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/abort-controller@npm:2.0.5" +"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/abort-controller@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 2e328942b9f42c1186943ed300acd1bd0dbbf0b69ec4826a124fb1e649bcaef556c24d13023439dac0ce93cf37a46e3a277fe8a683fcaee95537d192a9f5d7da + checksum: 187bbe7819271de99c8218d0df08d7b56131a7563e1822ef3142ecdad258201c9cc792e222d59145f6f59f6260e3c4ae2ef09b76370daa393797fad1b3d56551 languageName: node linkType: hard @@ -15118,141 +16283,142 @@ __metadata: languageName: node linkType: hard -"@smithy/config-resolver@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/config-resolver@npm:2.0.5" +"@smithy/config-resolver@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/config-resolver@npm:2.0.16" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/types": ^2.4.0 "@smithy/util-config-provider": ^2.0.0 - "@smithy/util-middleware": ^2.0.0 + "@smithy/util-middleware": ^2.0.5 tslib: ^2.5.0 - checksum: 324bc188246462ec7b29f128a92f9135620a37d1bf7c8071c3f51fc8249e8baafe8d8722e7547327c9fea8ad292c42383d211daa24dc8b627baacbeae62c3d1b + checksum: d92948bc42e59c451ff0cf5cf803b6cb13c664dd920d43c0f5a647193c93aa3634fa88391e85dad1c159f535432bfdd7653de8450599b4170e4adced2c8c9850 languageName: node linkType: hard -"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.0.7": - version: 2.0.7 - resolution: "@smithy/credential-provider-imds@npm:2.0.7" +"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/credential-provider-imds@npm:2.0.18" dependencies: - "@smithy/node-config-provider": ^2.0.7 - "@smithy/property-provider": ^2.0.6 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/property-provider": ^2.0.13 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 tslib: ^2.5.0 - checksum: e4caa4d2f34b9788345fb13ed7d363bc70491e2ac505f2c20d308a3e228a6705666e540ebe07fb81b1369c8893dc4ca64e0997f55221b411335070a83dd48729 + checksum: 12e4a436429b140a2d85e34842d9deb42d7507fe3d3b26070f45f484bf8ecba9ac4fe3f9deb87252f3f6e5ae31d19c9e61147079c69716c2f4bcd0aa4d2c73b8 languageName: node linkType: hard -"@smithy/eventstream-codec@npm:^2.0.1, @smithy/eventstream-codec@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/eventstream-codec@npm:2.0.5" +"@smithy/eventstream-codec@npm:^2.0.1, @smithy/eventstream-codec@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-codec@npm:2.0.12" dependencies: "@aws-crypto/crc32": 3.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 "@smithy/util-hex-encoding": ^2.0.0 tslib: ^2.5.0 - checksum: 472c0b1652e2afcf9dd9a8f122255d8b3b46019ead3c12ce8ceb3242d2dca525c5dcf6fd2fe57e03621283c8762b44d553a10eb0ce35eecaf2f4366c45bbf683 + checksum: 38e457645512d06e9b74bdb8b33df8b712e96b97e59b7cd51c9d31686ba71b7f4e094615dedcca7a1790fdb7e52f3e0791af7d7b66ca46e0556544827a311d5b languageName: node linkType: hard -"@smithy/eventstream-serde-browser@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/eventstream-serde-browser@npm:2.0.5" +"@smithy/eventstream-serde-browser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-browser@npm:2.0.12" dependencies: - "@smithy/eventstream-serde-universal": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/eventstream-serde-universal": ^2.0.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 5fb5f31b20d8b8e614159ed4fa7ad619d303f220bcf2f9bb13ccf07e91ccd187a898cf9c09c97fe4b48d49ba702d1e3d5ad833591afd0051253638204cfafa4b + checksum: 685d9d874e019d62cacac4d98c19ffbd8496c68efa0968f43f93cbcf3bcaa0db2c5ae060d0550c50bd24a6b1a15ea2b94ce7fed121733bb060dd536b7e618ff6 languageName: node linkType: hard -"@smithy/eventstream-serde-config-resolver@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.5" +"@smithy/eventstream-serde-config-resolver@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-config-resolver@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: e1914a9b422c2cbcff1e8897e59ad0f8ca81116c08180f0316ba4adbfc72115ccbff5453488e8cba15431f980adc0a09695fb437b67df8e11b2ebbb0b70036c5 + checksum: 1fbed5f1b1c5fb8830d9940e2d8d56e1c33dd3ce5e5a79f259f0dacaa8ec6dfa4203163b63e707769e4153d1d17680cbf195690b596a44da6f43a62f66bad1aa languageName: node linkType: hard -"@smithy/eventstream-serde-node@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/eventstream-serde-node@npm:2.0.5" +"@smithy/eventstream-serde-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-node@npm:2.0.12" dependencies: - "@smithy/eventstream-serde-universal": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/eventstream-serde-universal": ^2.0.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: a13a432a13418d7d5f766b1dce312c004691b6cd1991d9bae74e65a2b5bcb4f4d8af285ad6c91edbbc8b9b50d4a1405432e0db38e52401c134113c00ea74c5b4 + checksum: 541f57903daa13d78b09b23ac74a6643e8260b4c9afe9375344ccc347c62fdc1fc0c162f763f733b7bd46f8ceb240890cfc89f786bd49efd57cf43d74c9b3f6b languageName: node linkType: hard -"@smithy/eventstream-serde-universal@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/eventstream-serde-universal@npm:2.0.5" +"@smithy/eventstream-serde-universal@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/eventstream-serde-universal@npm:2.0.12" dependencies: - "@smithy/eventstream-codec": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/eventstream-codec": ^2.0.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 94a49055f025a9aac5e115038bdebdc8c88086e58f25d32c3e57f2d788c880e113eccec4633b485b83e8b1e67d42d3870acedbf27365200fd436ebc13948f9e9 + checksum: fea8ad03da25f92b0f3a0b20398a410bbf264aad6318b2cea9c8740cd86b1b130f3b52a07fb2b25e82b19eb44d60ec3770b17667a6842d404548e200a085ead9 languageName: node linkType: hard -"@smithy/fetch-http-handler@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/fetch-http-handler@npm:2.0.5" +"@smithy/fetch-http-handler@npm:^2.2.4": + version: 2.2.4 + resolution: "@smithy/fetch-http-handler@npm:2.2.4" dependencies: - "@smithy/protocol-http": ^2.0.5 - "@smithy/querystring-builder": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/protocol-http": ^3.0.8 + "@smithy/querystring-builder": ^2.0.12 + "@smithy/types": ^2.4.0 "@smithy/util-base64": ^2.0.0 tslib: ^2.5.0 - checksum: dae54728eca4e47b0e9604dd87cc503887f62d796b2f92efbbcdfcb4b7432454fa866898416ddf7fd02e7a7d06eb2ba0f1709e418cb1ce763764797f53188b46 + checksum: 37b9dfdd35ff4a997de07f3aacdaf4acb3881b3586b3c2bbf27f163066a241d54ce471fe100353e2bea3f3cd71ec8ef57a0a1f78f897e11c9166f75b06902cfc languageName: node linkType: hard -"@smithy/hash-blob-browser@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/hash-blob-browser@npm:2.0.5" +"@smithy/hash-blob-browser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-blob-browser@npm:2.0.12" dependencies: "@smithy/chunked-blob-reader": ^2.0.0 "@smithy/chunked-blob-reader-native": ^2.0.0 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: b66102c035d669169ef28c2ddc7115b0bb2620159eb0768782d832cc85324222e9463945477dc8b61daa5036251e76a0dd05ddda13e2464373cc31a0b05f94f1 + checksum: 212dd0200020c13c98efaea4544d81acf286ecebf6b8751b7205797da7b0282b17df1e85385525a479c7d3a1f7fd17100f8083974fb33e220e084f310b86f578 languageName: node linkType: hard -"@smithy/hash-node@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/hash-node@npm:2.0.5" +"@smithy/hash-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-node@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 "@smithy/util-buffer-from": ^2.0.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: bfc0441464b0f37905feb715bf86fb0ded8be3d31e6b28a1e05bfd48b45fcb929f93be3b25d59a89bb0156cbeb9ba5a245ca807fc42f43b26dd77ed18a3794fa + checksum: e2b36a60c812fb716091ea06d205113cdee9ba4dfdd608bb1723e635f9bd53c4f8a9bd038f2c6fb369a91beee3189123925e2543ee373b81a77d62e71170523c languageName: node linkType: hard -"@smithy/hash-stream-node@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/hash-stream-node@npm:2.0.5" +"@smithy/hash-stream-node@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/hash-stream-node@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: c1b1afa1595bf87101a906d28b7ce3fccfd9d32314305bf143ee126cce2977aa36fb57a12f6830f53008aadfe0161e5b4ad7a193ba00af652eaff27a50a3632b + checksum: 83b395ad6e529a23f82ca006597b08e5e83cf35e92b6813624cb8735632f7271e13249ffc687d6c21dbabccec92fc73fcf747e7dd7096d6d913a33d1e6842c7d languageName: node linkType: hard -"@smithy/invalid-dependency@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/invalid-dependency@npm:2.0.5" +"@smithy/invalid-dependency@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/invalid-dependency@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: b24444cf315fb52078c6d4faaddccc15db7c8068b14bf3d4742ac6addd6719e94ac17b85cc6012714cbca047eb186ac54d632fd3ea5725a523d2fd7e903f0e81 + checksum: 3b8a218ad67d3eca06d1646f21e52bf7704449fec714a0c113ab5db100605b05b37b12facd00b92df1203d5bec66ff4ed5e763691ac7c098b85854f194eefb58 languageName: node linkType: hard @@ -15265,155 +16431,161 @@ __metadata: languageName: node linkType: hard -"@smithy/md5-js@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/md5-js@npm:2.0.5" +"@smithy/md5-js@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/md5-js@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: 4b253d6762750f751431d881f4d4fb6e0a0d0bc63744a637cff7cc51d242a3d84b906c3fed2500324f13236c16946f18a88b51f35b5ef8edd4ad1ffaf69fdac0 + checksum: c6b90d31d89ff386d13b8ecad7aeb2d63fd6b534f0954745b34690fdb4b2520f228769c4ef2967a476a2cd5d6de0151be2998714c5ba1fde2253976012b18fba languageName: node linkType: hard -"@smithy/middleware-content-length@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/middleware-content-length@npm:2.0.5" +"@smithy/middleware-content-length@npm:^2.0.14": + version: 2.0.14 + resolution: "@smithy/middleware-content-length@npm:2.0.14" dependencies: - "@smithy/protocol-http": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/protocol-http": ^3.0.8 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 1dfbac11f63a38a73bb33883c4575d0f74982000ad173a4b723d505734abad3cd20962635aa63231bd0ac1bcdebe75a4f95520c9779d542e6937b70a3dbc84c6 + checksum: ff289f3c7ec4dbf53297e5968196444a387ddd3e67cb8426e40cadc096e7a5127e30315520761aa53a98daecfde0e6ecc195a722d4b31b7662f63b3286474224 languageName: node linkType: hard -"@smithy/middleware-endpoint@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/middleware-endpoint@npm:2.0.5" +"@smithy/middleware-endpoint@npm:^2.1.3": + version: 2.1.3 + resolution: "@smithy/middleware-endpoint@npm:2.1.3" dependencies: - "@smithy/middleware-serde": ^2.0.5 - "@smithy/types": ^2.2.2 - "@smithy/url-parser": ^2.0.5 - "@smithy/util-middleware": ^2.0.0 + "@smithy/middleware-serde": ^2.0.12 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/shared-ini-file-loader": ^2.2.2 + "@smithy/types": ^2.4.0 + "@smithy/url-parser": ^2.0.12 + "@smithy/util-middleware": ^2.0.5 tslib: ^2.5.0 - checksum: 258ad9e51dc39aa0b6f16af14bdc80871e3fa0c9649e1ad83d3e8c7e14b4139f8dcba4fbe0953010fde08ba9af3b27611d70627368d94672232b3b667ac398ac + checksum: 62dfcb031bccb575a33f04ca8d684634eb03585530b28ffe759242dc13fef7e11755673d3d7d1be15a90f933f579614bc78d83dad0747e3bf344c60cb2212d92 languageName: node linkType: hard -"@smithy/middleware-retry@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/middleware-retry@npm:2.0.5" +"@smithy/middleware-retry@npm:^2.0.18": + version: 2.0.18 + resolution: "@smithy/middleware-retry@npm:2.0.18" dependencies: - "@smithy/protocol-http": ^2.0.5 - "@smithy/service-error-classification": ^2.0.0 - "@smithy/types": ^2.2.2 - "@smithy/util-middleware": ^2.0.0 - "@smithy/util-retry": ^2.0.0 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/protocol-http": ^3.0.8 + "@smithy/service-error-classification": ^2.0.5 + "@smithy/types": ^2.4.0 + "@smithy/util-middleware": ^2.0.5 + "@smithy/util-retry": ^2.0.5 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 486d3a1d1e706abbb4853c1ee4db313578c19b2529a7c9c2f9e2afd4789f25d247f74895f2c745a49eed6f4c873bd00c2ad3da0f1d170da09c4e90798ca2c3b7 + checksum: 7372232d35fbff0f770e4ec608940c81a776040971556e3a328980ebcceb9f9469eb09e5d6014811c42759c77653ded4cbbccc21b7c26f3405c7299062a523b3 languageName: node linkType: hard -"@smithy/middleware-serde@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/middleware-serde@npm:2.0.5" +"@smithy/middleware-serde@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/middleware-serde@npm:2.0.12" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 51c2ce5158106757da351fce512ea709c1cf7a2724f5e4430bf18c4e70571a806ebc7f09e38b3281fc81d5446da106450ee84c4508607cff83078c58be61f8dd + checksum: 5e8b04511c017bcadbf1a6efc6c71588586cabaa130df10562a74159d128e56965581799e80a0645557bab03df8bea187b21cb1fd536e17cf73148e5b678925f languageName: node linkType: hard -"@smithy/middleware-stack@npm:^2.0.0": - version: 2.0.0 - resolution: "@smithy/middleware-stack@npm:2.0.0" - dependencies: - tslib: ^2.5.0 - checksum: dd23dff4da44964e936c5ae465de9416bb8dd67da2ae72ffe450156ad52e82475836ed5c18d82cef7edeca421b33d363889549e34482008eeb9ca0bb97f061f2 - languageName: node - linkType: hard - -"@smithy/node-config-provider@npm:^2.0.6, @smithy/node-config-provider@npm:^2.0.7": - version: 2.0.7 - resolution: "@smithy/node-config-provider@npm:2.0.7" - dependencies: - "@smithy/property-provider": ^2.0.6 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.2.2 - tslib: ^2.5.0 - checksum: 5bb2e41ada99a4a94a0b087b2e5d2a5e4e1e5d4ed8cb7bf84258ff662a8f649d70870ec40587d4ee92a93dbc1fa904189d71d691c559252e3dc8ddf20990910b - languageName: node - linkType: hard - -"@smithy/node-http-handler@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/node-http-handler@npm:2.0.5" - dependencies: - "@smithy/abort-controller": ^2.0.5 - "@smithy/protocol-http": ^2.0.5 - "@smithy/querystring-builder": ^2.0.5 - "@smithy/types": ^2.2.2 - tslib: ^2.5.0 - checksum: fb2c6faab1d0107e7610c4ed723114c58de4b396e4e76a13df7831ef4d22ae188138af8becd749b95acec94a44e3d673b2ca9fee5217928ca3bea52c6e5319b0 - languageName: node - linkType: hard - -"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.0.6": +"@smithy/middleware-stack@npm:^2.0.6": version: 2.0.6 - resolution: "@smithy/property-provider@npm:2.0.6" + resolution: "@smithy/middleware-stack@npm:2.0.6" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 7292d89603c3e98c8d771bb7a9f5fac992608927808d7e06b68213514bf2b894ba5360f2a2b0ccd1f0a1b6aa3c31689637b5fce3eebcaa7aec1dd980a6a1e624 + checksum: 3626b71364b83d091751cd6ad7f7bc655a1746f970c63ea3205c2bc171a596a734394d556fcf66f1458b8151fe54cab5bf774ee66b4d40c3dd9d9e7d9114f905 languageName: node linkType: hard -"@smithy/protocol-http@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/protocol-http@npm:2.0.5" +"@smithy/node-config-provider@npm:^2.1.3": + version: 2.1.3 + resolution: "@smithy/node-config-provider@npm:2.1.3" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/property-provider": ^2.0.13 + "@smithy/shared-ini-file-loader": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 0c9dbc7c90a3908626260156ec26e8fc83eb257c002a01c4f840efc19065a9397df8ebaf9ed7f521baea5b2792e3476143ae8bd7e7f21aaa454bcb894c3d1658 + checksum: 22e188fbc099616e50661afb0decb88ba67b396a1fbed122ad2a857a2a9e4a80d34a68d793cca6cb9e34a299ca1cde2bf3b9ab2b97b733bae838852acec080c5 languageName: node linkType: hard -"@smithy/querystring-builder@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/querystring-builder@npm:2.0.5" +"@smithy/node-http-handler@npm:^2.1.7, @smithy/node-http-handler@npm:^2.1.8": + version: 2.1.8 + resolution: "@smithy/node-http-handler@npm:2.1.8" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/abort-controller": ^2.0.12 + "@smithy/protocol-http": ^3.0.8 + "@smithy/querystring-builder": ^2.0.12 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 17e51b8c0b2dc7dcf7e32bc2cbd836220f86355b4d630f0b94fad4ed79dfa737b4ecbb7c72752b59e6849ca342c4a3ade89846e0276d986a72d25ed280ce3a8c + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.0.13": + version: 2.0.13 + resolution: "@smithy/property-provider@npm:2.0.13" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 62443ec94d4dafaa0c2f285957264b3b548fd5a164ebd1ef02e4286c55d3e07e4d22d695fc2857ad0b1e406d01bf27271e9d7c3c05465638da0226ae4305d3d7 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^3.0.8": + version: 3.0.8 + resolution: "@smithy/protocol-http@npm:3.0.8" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: deb4f7d863bcc67724555b3a1ffb8e605a3df63cde9f40234813f072184bb68f5c33388c1934f56576b08a877bb8c9c0bfb849deb0526b55a9410678040fa019 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/querystring-builder@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 "@smithy/util-uri-escape": ^2.0.0 tslib: ^2.5.0 - checksum: 4a684189ac9bf50dba2c648a6a36299181bcdf3e688dc5e5bce94a5befd30e6662b6e84e5a30e8876dc2ff293eeb74f4fdf17446e8886228a62f1cbcf5c0e807 + checksum: d7d0608ac14d8ccd2b418743fc91be9c77b75a302a7552f666a81454fa1764e2162fb2c2f7655cf24045ae44416252362111b9612ea9759dbc1f27f75a71aa42 languageName: node linkType: hard -"@smithy/querystring-parser@npm:^2.0.5": +"@smithy/querystring-parser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/querystring-parser@npm:2.0.12" + dependencies: + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 889dad387fda7db289d0360cbc38901d2c726d164c56915c76ee125bb8059f8a86e28442841000112c3b8a5a3c7701da391f961350969ea5242c6cdf55f296cf + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^2.0.5": version: 2.0.5 - resolution: "@smithy/querystring-parser@npm:2.0.5" + resolution: "@smithy/service-error-classification@npm:2.0.5" dependencies: - "@smithy/types": ^2.2.2 - tslib: ^2.5.0 - checksum: 71d5fb2379529c6fe411945d05887b169113df0c29258981cb541487c0c5cd9be8dc31efee34e357e6532733b000a77f374e665c0ef73aeb92c3134f10ecf607 + "@smithy/types": ^2.4.0 + checksum: cd4b9fcc5cd940035ca4f3e832f8480d75eb81c90501bdb5c9295c5fd26487ca2e2f3d3efa9a322faeaedf10d6d8324327cd3341fc05d38f8605006ad836abaa languageName: node linkType: hard -"@smithy/service-error-classification@npm:^2.0.0": - version: 2.0.0 - resolution: "@smithy/service-error-classification@npm:2.0.0" - checksum: f6f9b869dca8e74871c428e1277dd3700f67b0ca61de69fc838ac55187bbc602193a7d1073113ea6916892babf3f8fe4938b182fc902ce0a415928799a7e3f9a - languageName: node - linkType: hard - -"@smithy/shared-ini-file-loader@npm:^2.0.6": - version: 2.0.6 - resolution: "@smithy/shared-ini-file-loader@npm:2.0.6" +"@smithy/shared-ini-file-loader@npm:^2.0.6, @smithy/shared-ini-file-loader@npm:^2.2.2": + version: 2.2.2 + resolution: "@smithy/shared-ini-file-loader@npm:2.2.2" dependencies: - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 33569b7e1f236b03c9253ba9d1a07e87d3fb186c33da7ce3f1d3f641a50f1a484e8624b6667d0044188e4a5eb5d594ab9f197dc2d31a8958dc2dd5e6cf74af1d + checksum: 851b1ed096609a3c860aebdf7110629783e4824a246d96b10a262426bb90aa4eb2e0370ff489dec48c1dcbd812d95bd3208d785f34c22c2f20249a36bf5ea762 languageName: node linkType: hard @@ -15433,15 +16605,15 @@ __metadata: languageName: node linkType: hard -"@smithy/smithy-client@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/smithy-client@npm:2.0.5" +"@smithy/smithy-client@npm:^2.1.12": + version: 2.1.12 + resolution: "@smithy/smithy-client@npm:2.1.12" dependencies: - "@smithy/middleware-stack": ^2.0.0 - "@smithy/types": ^2.2.2 - "@smithy/util-stream": ^2.0.5 + "@smithy/middleware-stack": ^2.0.6 + "@smithy/types": ^2.4.0 + "@smithy/util-stream": ^2.0.17 tslib: ^2.5.0 - checksum: 8dabc85dae6f7a47cc743e412cbf4d95f93bdee163d9b9dfb0c1f4361f0c3c2d627a1e1dcfec506a2052775b8d3da5977ba94d820c3a3abc48c6d2344185d849 + checksum: 9e2944a9c753511777468ec40a3295e5351d08349258a57b70dfc9a96e882efed6075eb7fd3c0494fa07279bdefdfad2e5aecf7930685c656131a97d56aae209 languageName: node linkType: hard @@ -15454,23 +16626,23 @@ __metadata: languageName: node linkType: hard -"@smithy/types@npm:^2.0.2, @smithy/types@npm:^2.2.2": - version: 2.2.2 - resolution: "@smithy/types@npm:2.2.2" +"@smithy/types@npm:^2.0.2, @smithy/types@npm:^2.4.0": + version: 2.4.0 + resolution: "@smithy/types@npm:2.4.0" dependencies: tslib: ^2.5.0 - checksum: 2799a14620da60efb2a0aba1bf9adc553a5446dc447b9ee1d7a95410233a70dff2b5e563fecf84388137dabbe662c6bf3a2247ca20a1f266c1256f82e0f25fcf + checksum: 936690f8ba9323c05a1046102f83d7ed76c5c2f2405ca22e8bfed8d66a5ba12d74a187c10d93b085d6822b98edaec7b6309a4401f036099bf239a0bf5cdcf00d languageName: node linkType: hard -"@smithy/url-parser@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/url-parser@npm:2.0.5" +"@smithy/url-parser@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/url-parser@npm:2.0.12" dependencies: - "@smithy/querystring-parser": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/querystring-parser": ^2.0.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 5360d96d21492a7014bc952f530bf30a50008a809dc0cb219d336bbd23be29ff15437033bddebe600ff763e3d1efab2fe8cacc6d7545c26c86377e23e6a404d8 + checksum: 40324cee758137342573e9f7bf685bc7c3f8284ff2f15d3c68a244dacf26f62cd92b234f220ddfc2963038ef766dd73c3f70642c592a49bd10432c5432fb1ab6 languageName: node linkType: hard @@ -15521,29 +16693,31 @@ __metadata: languageName: node linkType: hard -"@smithy/util-defaults-mode-browser@npm:^2.0.6": - version: 2.0.6 - resolution: "@smithy/util-defaults-mode-browser@npm:2.0.6" +"@smithy/util-defaults-mode-browser@npm:^2.0.16": + version: 2.0.16 + resolution: "@smithy/util-defaults-mode-browser@npm:2.0.16" dependencies: - "@smithy/property-provider": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/property-provider": ^2.0.13 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: 017bb8b0deaa38509164478e2d4b865a0c80098013a7e445a633f97a8e5211b79741dca3c2e0990992d0cabd2f3187ec8bba5a9992ad65e5f2898f5cb302c76f + checksum: 8dae0256e89c13ab7bcd791fe336124adc17d95401ceb7152784a809ed9ba09a639573c1ce2bf32b12964f7181aeb2cdfc283d820301f2b3a82ef4906fe83280 languageName: node linkType: hard -"@smithy/util-defaults-mode-node@npm:^2.0.6": - version: 2.0.7 - resolution: "@smithy/util-defaults-mode-node@npm:2.0.7" +"@smithy/util-defaults-mode-node@npm:^2.0.21": + version: 2.0.21 + resolution: "@smithy/util-defaults-mode-node@npm:2.0.21" dependencies: - "@smithy/config-resolver": ^2.0.5 - "@smithy/credential-provider-imds": ^2.0.7 - "@smithy/node-config-provider": ^2.0.7 - "@smithy/property-provider": ^2.0.6 - "@smithy/types": ^2.2.2 + "@smithy/config-resolver": ^2.0.16 + "@smithy/credential-provider-imds": ^2.0.18 + "@smithy/node-config-provider": ^2.1.3 + "@smithy/property-provider": ^2.0.13 + "@smithy/smithy-client": ^2.1.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: 87392a683dbf9d73b5068f280f416efcb70a7f71c0b02bd9f4519530c7f48112ce8549378459c764ae23f0520c065c4e801411fb1875fd8d142a4af95a5bcbba + checksum: ce2643ad99181b91b4eb00f2b2b34d12ff006ac1770333ae62541cfc7b98b873e233933d483d7bb0a443a8155debd94731a1df0f4cc572e6cc5ddbf97416e2d7 languageName: node linkType: hard @@ -15556,38 +16730,40 @@ __metadata: languageName: node linkType: hard -"@smithy/util-middleware@npm:^2.0.0": - version: 2.0.0 - resolution: "@smithy/util-middleware@npm:2.0.0" - dependencies: - tslib: ^2.5.0 - checksum: 10401734a10e0c48ed684f20b7a34c40ed85f2e906e61adb6295963d035f2a93b524e80149a252a259a4bca3626773bf89c5eaa2423fd565358c6b4eb9b6d4e0 - languageName: node - linkType: hard - -"@smithy/util-retry@npm:^2.0.0": - version: 2.0.0 - resolution: "@smithy/util-retry@npm:2.0.0" - dependencies: - "@smithy/service-error-classification": ^2.0.0 - tslib: ^2.5.0 - checksum: d5bfe5e81f41dffce6ba5aaf784f08247602d00f883c10c0de9e34a7f04f5369d7ac43901dd75eecc3864882b7390ad40885d07b3dcb35a366411b639482e673 - languageName: node - linkType: hard - -"@smithy/util-stream@npm:^2.0.5": +"@smithy/util-middleware@npm:^2.0.0, @smithy/util-middleware@npm:^2.0.5": version: 2.0.5 - resolution: "@smithy/util-stream@npm:2.0.5" + resolution: "@smithy/util-middleware@npm:2.0.5" dependencies: - "@smithy/fetch-http-handler": ^2.0.5 - "@smithy/node-http-handler": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: 9d001723e7472c0d78619320235f66d1de42f16e13d1189697f8e447d05643047ab97965525b147eaafbb0e169563ecb5b806da2d02bd4ce0b652b72df4d9131 + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^2.0.5": + version: 2.0.5 + resolution: "@smithy/util-retry@npm:2.0.5" + dependencies: + "@smithy/service-error-classification": ^2.0.5 + "@smithy/types": ^2.4.0 + tslib: ^2.5.0 + checksum: e7169b458a9c194104e16014b2829deddb9ee4175fd17bd933d0ab9ec9df065cf23816b605eafb6604da1111e3280c5fea4da98dd8ec5f5f3e1c30e166119808 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^2.0.17": + version: 2.0.17 + resolution: "@smithy/util-stream@npm:2.0.17" + dependencies: + "@smithy/fetch-http-handler": ^2.2.4 + "@smithy/node-http-handler": ^2.1.8 + "@smithy/types": ^2.4.0 "@smithy/util-base64": ^2.0.0 "@smithy/util-buffer-from": ^2.0.0 "@smithy/util-hex-encoding": ^2.0.0 "@smithy/util-utf8": ^2.0.0 tslib: ^2.5.0 - checksum: a78312fcbd50b55eb995959fccea8b7957dd352a1fff1abea2906aa5629305b68a9f3025bcd1edd60e5a2d5dd9e1dab00959f5971ded0d450fb463cf9865aedd + checksum: acd68f7b092fdf3560f5d88f3f81d1bfab4c634f8b7acd8eca1993c8ce789d9652d23048c9e891a42dd12dd71e7a9756b9879ae95fccd1cd92f7ad8204c97d68 languageName: node linkType: hard @@ -15610,14 +16786,14 @@ __metadata: languageName: node linkType: hard -"@smithy/util-waiter@npm:^2.0.5": - version: 2.0.5 - resolution: "@smithy/util-waiter@npm:2.0.5" +"@smithy/util-waiter@npm:^2.0.12": + version: 2.0.12 + resolution: "@smithy/util-waiter@npm:2.0.12" dependencies: - "@smithy/abort-controller": ^2.0.5 - "@smithy/types": ^2.2.2 + "@smithy/abort-controller": ^2.0.12 + "@smithy/types": ^2.4.0 tslib: ^2.5.0 - checksum: b36069643d7641f019f8eb28e006eb301e82f5d93412f79b87ae473d661830349e60d65c18219e252d276cd014954f05e989eb6098100efb782219167a88f1f0 + checksum: af35c36a58585472aae9e06ea000a113110f22bed179687213336a014b002deb867cb094f9cb01bc43856235df05517baf08009b3b929a48b48f964c426c1ffc languageName: node linkType: hard @@ -15844,7 +17020,7 @@ __metadata: languageName: node linkType: hard -"@stoplight/spectral-rulesets@npm:^1.16.0": +"@stoplight/spectral-rulesets@npm:^1.14.1, @stoplight/spectral-rulesets@npm:^1.18.0": version: 1.18.0 resolution: "@stoplight/spectral-rulesets@npm:1.18.0" dependencies: @@ -16504,90 +17680,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-arm64@npm:1.3.92" +"@swc/core-darwin-arm64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-arm64@npm:1.3.95" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-darwin-x64@npm:1.3.92" +"@swc/core-darwin-x64@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-darwin-x64@npm:1.3.95" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.92" +"@swc/core-linux-arm-gnueabihf@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.3.95" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-gnu@npm:1.3.92" +"@swc/core-linux-arm64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-gnu@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-arm64-musl@npm:1.3.92" +"@swc/core-linux-arm64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-arm64-musl@npm:1.3.95" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-gnu@npm:1.3.92" +"@swc/core-linux-x64-gnu@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-gnu@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-linux-x64-musl@npm:1.3.92" +"@swc/core-linux-x64-musl@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-linux-x64-musl@npm:1.3.95" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-arm64-msvc@npm:1.3.92" +"@swc/core-win32-arm64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-arm64-msvc@npm:1.3.95" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-ia32-msvc@npm:1.3.92" +"@swc/core-win32-ia32-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-ia32-msvc@npm:1.3.95" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.3.92": - version: 1.3.92 - resolution: "@swc/core-win32-x64-msvc@npm:1.3.92" +"@swc/core-win32-x64-msvc@npm:1.3.95": + version: 1.3.95 + resolution: "@swc/core-win32-x64-msvc@npm:1.3.95" conditions: os=win32 & cpu=x64 languageName: node linkType: hard "@swc/core@npm:^1.3.46": - version: 1.3.92 - resolution: "@swc/core@npm:1.3.92" + version: 1.3.95 + resolution: "@swc/core@npm:1.3.95" dependencies: - "@swc/core-darwin-arm64": 1.3.92 - "@swc/core-darwin-x64": 1.3.92 - "@swc/core-linux-arm-gnueabihf": 1.3.92 - "@swc/core-linux-arm64-gnu": 1.3.92 - "@swc/core-linux-arm64-musl": 1.3.92 - "@swc/core-linux-x64-gnu": 1.3.92 - "@swc/core-linux-x64-musl": 1.3.92 - "@swc/core-win32-arm64-msvc": 1.3.92 - "@swc/core-win32-ia32-msvc": 1.3.92 - "@swc/core-win32-x64-msvc": 1.3.92 + "@swc/core-darwin-arm64": 1.3.95 + "@swc/core-darwin-x64": 1.3.95 + "@swc/core-linux-arm-gnueabihf": 1.3.95 + "@swc/core-linux-arm64-gnu": 1.3.95 + "@swc/core-linux-arm64-musl": 1.3.95 + "@swc/core-linux-x64-gnu": 1.3.95 + "@swc/core-linux-x64-musl": 1.3.95 + "@swc/core-win32-arm64-msvc": 1.3.95 + "@swc/core-win32-ia32-msvc": 1.3.95 + "@swc/core-win32-x64-msvc": 1.3.95 "@swc/counter": ^0.1.1 "@swc/types": ^0.1.5 peerDependencies: @@ -16616,7 +17792,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 88c0c62ff790e896180862c341be8bae98baf0a5c5e87f2f04f49e14b8c4fba460d6b352618b4dda066c8ae6bf152cd843eab25837f38128175208b8c0635721 + checksum: 49856ad64fa16151f7f784956c8134060c5ed612c9b3fcb79e33676c77219f61fa3bb6b9e0dd15d78c7bff92cacc0c944d2b4e60849eb93caca917a8544b1c2c languageName: node linkType: hard @@ -16655,6 +17831,15 @@ __metadata: languageName: node linkType: hard +"@szmarczak/http-timer@npm:^1.1.2": + version: 1.1.2 + resolution: "@szmarczak/http-timer@npm:1.1.2" + dependencies: + defer-to-connect: ^1.0.1 + checksum: 4d9158061c5f397c57b4988cde33a163244e4f02df16364f103971957a32886beb104d6180902cbe8b38cb940e234d9f98a4e486200deca621923f62f50a06fe + languageName: node + linkType: hard + "@szmarczak/http-timer@npm:^4.0.5": version: 4.0.5 resolution: "@szmarczak/http-timer@npm:4.0.5" @@ -16836,6 +18021,13 @@ __metadata: languageName: node linkType: hard +"@tootallnate/quickjs-emscripten@npm:^0.23.0": + version: 0.23.0 + resolution: "@tootallnate/quickjs-emscripten@npm:0.23.0" + checksum: c350a2947ffb80b22e14ff35099fd582d1340d65723384a0fd0515e905e2534459ad2f301a43279a37308a27c99273c932e64649abd57d0bb3ca8c557150eccc + languageName: node + linkType: hard + "@trendyol-js/openstack-swift-sdk@npm:^0.0.6": version: 0.0.6 resolution: "@trendyol-js/openstack-swift-sdk@npm:0.0.6" @@ -16905,11 +18097,11 @@ __metadata: linkType: hard "@types/archiver@npm:^5.1.0, @types/archiver@npm:^5.3.1": - version: 5.3.3 - resolution: "@types/archiver@npm:5.3.3" + version: 5.3.4 + resolution: "@types/archiver@npm:5.3.4" dependencies: "@types/readdir-glob": "*" - checksum: e660465ac9a622570ddbad86a306c7234c3272cb28eafe9c3ad7229f06e499dfadae9f0cb0caf41443318eea559b75ef662ea276ae64f19695e72018dae37ee7 + checksum: 4ef27b99091ada9b8f13017d5b9e6d42a439e35a7858b30e040c408e081d98d8db6307b0762500288b5da38cab9823c4756b6abae1fdd2658d42bfb09eb7c5fb languageName: node linkType: hard @@ -16935,24 +18127,24 @@ __metadata: linkType: hard "@types/aws4@npm:^1.5.1": - version: 1.11.4 - resolution: "@types/aws4@npm:1.11.4" + version: 1.11.5 + resolution: "@types/aws4@npm:1.11.5" dependencies: "@types/node": "*" - checksum: 8b2576843488b31fdb080369eaff6af539ded60feab4eeeb429481ec0c209c0a7a8d7011747eb71e1b2bc86a455b105dd91fe86add5c2aa1584b6f4821fbf0e7 + checksum: ffc1e7d4fb36c8da6e074ad946c78358baa9744fca729cccfa91349f7565b5ba3d6f30e14ea37d4926217b0b0991e57e2b328dc080465cf12b15f047c3a2b6bb languageName: node linkType: hard -"@types/babel__core@npm:^7.1.14": - version: 7.1.18 - resolution: "@types/babel__core@npm:7.1.18" +"@types/babel__core@npm:^7.1.14, @types/babel__core@npm:^7.20.2": + version: 7.20.2 + resolution: "@types/babel__core@npm:7.20.2" dependencies: - "@babel/parser": ^7.1.0 - "@babel/types": ^7.0.0 + "@babel/parser": ^7.20.7 + "@babel/types": ^7.20.7 "@types/babel__generator": "*" "@types/babel__template": "*" "@types/babel__traverse": "*" - checksum: 2e5b5d7c84f347d3789575486e58b0df5c91613abc3d27e716274aba3048518e07e1f068250ba829e2ed58532ccc88da595ce95ba2688e7bbcd7c25a3c6627ed + checksum: 564fbaa8ff1305d50807ada0ec227c3e7528bebb2f8fe6b2ed88db0735a31511a74ad18729679c43eeed8025ed29d408f53059289719e95ab1352ed559a100bd languageName: node linkType: hard @@ -16985,11 +18177,11 @@ __metadata: linkType: hard "@types/base64-stream@npm:^1.0.2": - version: 1.0.3 - resolution: "@types/base64-stream@npm:1.0.3" + version: 1.0.4 + resolution: "@types/base64-stream@npm:1.0.4" dependencies: "@types/node": "*" - checksum: 4c0328ad7540d57e0f7ddf0135941adc0d1902e6b0f5d6f93537b417d82ae8f996389e14269c67e7c29f9eb6fc717f2f47ffa3f7e35f9a8096e84c3ee7b9853f + checksum: 498dc3a71f9e6a598761616749697621c1b70f5ae79d24bb7f1b88a924c9865d82ac910eff5a41912194c4834e93e0b7da075418ebb61356c0d3dc4806e8007e languageName: node linkType: hard @@ -17007,12 +18199,12 @@ __metadata: linkType: hard "@types/body-parser@npm:*, @types/body-parser@npm:^1.19.0": - version: 1.19.3 - resolution: "@types/body-parser@npm:1.19.3" + version: 1.19.4 + resolution: "@types/body-parser@npm:1.19.4" dependencies: "@types/connect": "*" "@types/node": "*" - checksum: 932fa71437c275023799123680ef26ffd90efd37f51a1abe405e6ae6e5b4ad9511b7a3a8f5a12877ed1444a02b6286c0a137a98e914b3c61932390c83643cc2c + checksum: 10accc30773319bd49af7d12d2cd5faf9a0293ea4764345297f26ba6ef31d5caa7609da7619584d6c61279e09b89d3ab13d28c5cb644807c5d9c722ae1454778 languageName: node linkType: hard @@ -17060,12 +18252,21 @@ __metadata: languageName: node linkType: hard -"@types/codemirror@npm:^5.0.0": - version: 5.60.10 - resolution: "@types/codemirror@npm:5.60.10" +"@types/codemirror@npm:^0.0.90": + version: 0.0.90 + resolution: "@types/codemirror@npm:0.0.90" dependencies: "@types/tern": "*" - checksum: c5977db03939f2a208f0ec7958be70b4fb205dd3f3122b2175ff28287a5424da95f9030b2838c61d37e6278ec53795861dec12439967c1e1da885b2b2a65b299 + checksum: f4594b9bc95306bbbe24d967e0749e28fe7b1e461c41621429b8c8bc295bda1704d99c1d7d5496efd987ee80d24f055155ddd742fa0c975cd69f279ccdaa0af9 + languageName: node + linkType: hard + +"@types/codemirror@npm:^5.0.0, @types/codemirror@npm:^5.60.8": + version: 5.60.12 + resolution: "@types/codemirror@npm:5.60.12" + dependencies: + "@types/tern": "*" + checksum: dff22f32ea42ccd3f9bfcf408631f94a11ffb4614ff4fa8cc55adf7da6e7ba96650533b8dd27d037242747bdaa85141e93520f84409db7bc394862a174a10e1e languageName: node linkType: hard @@ -17086,18 +18287,18 @@ __metadata: linkType: hard "@types/color@npm:^3.0.1": - version: 3.0.4 - resolution: "@types/color@npm:3.0.4" + version: 3.0.5 + resolution: "@types/color@npm:3.0.5" dependencies: "@types/color-convert": "*" - checksum: 46935626ddeb8ae8877488b1f8f2f71b17a529c113673301a1dd7a64d917cb7a748545ac34ed30c188d66a2cd09c33abc24c87bd5b3a80d520cf1f35aaa2e476 + checksum: 82cce7cb132b5c0898dd69b92a4663e34ad8f19db6a009bef7ef79d4840ddb4d5f7e793e2e64e3b4a60da92dc2ae33d1aa498981d0fcf0562917c9550603a99d languageName: node linkType: hard "@types/command-exists@npm:^1.2.0": - version: 1.2.1 - resolution: "@types/command-exists@npm:1.2.1" - checksum: 51e5d13cb06b9a5780e85e0612b924eda01d00919a75b6645fbdc7344c610ac19e8e122e2da9db5e91473cf88a3d71cfd93a294f1c31f2beabb835b6ab7e85d1 + version: 1.2.2 + resolution: "@types/command-exists@npm:1.2.2" + checksum: 75ce30dc03c5a9b26a8c0fcbe903d5189d7470aef713c5bf73d6d284be0f5cd4d8b388f30aedf81eb5b9f9b788ccc9ed775a13e21c25837017bb2d086b1bb06e languageName: node linkType: hard @@ -17111,20 +18312,20 @@ __metadata: linkType: hard "@types/compression@npm:^1.7.0, @types/compression@npm:^1.7.2": - version: 1.7.3 - resolution: "@types/compression@npm:1.7.3" + version: 1.7.4 + resolution: "@types/compression@npm:1.7.4" dependencies: "@types/express": "*" - checksum: 9a65519e8cc894246466fc8828a82045d0d3db97a3d7367381855c8fd10080d752ac10c2274de4668985225c72f3124bf5aeee75656b992837a21c64f8eb1789 + checksum: 0994e68158ae991d1839f950c553141cf95606a2660bcedf11d68eeb7609caf21b03f256aed1ea4243e89cf8adf358d0a2920513316ea2ec6a2d3e2eacfa02c7 languageName: node linkType: hard "@types/concat-stream@npm:^2.0.0": - version: 2.0.0 - resolution: "@types/concat-stream@npm:2.0.0" + version: 2.0.1 + resolution: "@types/concat-stream@npm:2.0.1" dependencies: "@types/node": "*" - checksum: d82ace5cb92f9fc91660ae1a101fa0a6b6159da59b0351c28627b24c317670267bc527f24ef4fa2c08d00404b49882ca66bf5c75d47d2b5f48d2fd85f9c2ea4d + checksum: 5cbcaf6b0499a58674b0ada4c6c55a31a6907e515b141d75e2e96790cbda0bb32416d4f513fefc17dc08bd400bbb4687081d9046896a64569335ca39a9ad9fff languageName: node linkType: hard @@ -17148,11 +18349,11 @@ __metadata: linkType: hard "@types/cookie-parser@npm:^1.4.2": - version: 1.4.4 - resolution: "@types/cookie-parser@npm:1.4.4" + version: 1.4.5 + resolution: "@types/cookie-parser@npm:1.4.5" dependencies: "@types/express": "*" - checksum: 5c81ac4b7d90a567e0c7a904ecbc09c82c43e30c6b5b507aee5147bc06bc8c6418e6719d63fcf68e6a83c71870485ab3fc579a30891b56a0be05d64753e3f74a + checksum: 45855721706d6a57bb0441db11fb59db407414ea83a0000a0df80d19230447cc3bcc43c436397bfb8998ec78db222265fa8083456b00c5b5054a0c03d06f0086 languageName: node linkType: hard @@ -17171,9 +18372,9 @@ __metadata: linkType: hard "@types/core-js@npm:^2.5.4": - version: 2.5.6 - resolution: "@types/core-js@npm:2.5.6" - checksum: 05cfeea4c918c88afcff5949e05a66c9c6cccd67f3de80732609d2546da0fdbd4350d6ecb415d4301f877e5ae1073c8afc0d500233f2b8826dd66d14c6e0c854 + version: 2.5.7 + resolution: "@types/core-js@npm:2.5.7" + checksum: a5ddeaa7103772eaee156d0182779f11e98983cbd76f1b1784dc919b9d59fa4575c608c8e7a31bfe08035e1890d6686b82ee9484a1def17402a1b8983e22ffaf languageName: node linkType: hard @@ -17197,11 +18398,11 @@ __metadata: linkType: hard "@types/cross-spawn@npm:^6.0.2": - version: 6.0.3 - resolution: "@types/cross-spawn@npm:6.0.3" + version: 6.0.4 + resolution: "@types/cross-spawn@npm:6.0.4" dependencies: "@types/node": "*" - checksum: 06d50fa1e1370ef60b9c9085b76adec7d7bc20728fbb02b3c2061d4d922312acf1ba56a7c94d88c27a22fc6241ab6b970c936f3294038a9c97a719fbc8eb8a76 + checksum: 45624a955c064dda167f8aae5560918307110e3a1221989b5957c52842208c2ee29e099c9120615b1dde66afe7a7c3db627c754e328f574a0ae313644e43ceef languageName: node linkType: hard @@ -17234,9 +18435,9 @@ __metadata: linkType: hard "@types/d3-force@npm:^3.0.0": - version: 3.0.6 - resolution: "@types/d3-force@npm:3.0.6" - checksum: 092d7a4efc1bef06de4a087e1bdb609e21588429ba2ce978785bb13e1ed7371dd08692408379d9582e03ed945c6cf117dff3b3ed1dd2141d94aac9e12994498d + version: 3.0.7 + resolution: "@types/d3-force@npm:3.0.7" + checksum: fd846e5d79dabd8d375b68c64cd218b4867e229eb6cb584b302ed9b96105cb267300fee63b0cd762417c28168d0a64047b7e6eff2acc8b5c561fded74f86fa8c languageName: node linkType: hard @@ -17273,27 +18474,27 @@ __metadata: linkType: hard "@types/d3-selection@npm:*, @types/d3-selection@npm:^3.0.1": - version: 3.0.7 - resolution: "@types/d3-selection@npm:3.0.7" - checksum: 715a0c7a78732650ae70b3487b3b6432b063482c1937bcd52c8b7a201563103170fdaa1f8c0ace882062521ac009f07fefb41198c8c43c5d3c518f8368c459d6 + version: 3.0.8 + resolution: "@types/d3-selection@npm:3.0.8" + checksum: 5655584116a97876d22c68387551d6e029d306894ca15c5b71635c2cc528288bddee67799ae2c677b028f2f26f99fcc8d598e47f5a67d5c1e3dd9b5507771f90 languageName: node linkType: hard "@types/d3-shape@npm:^1": - version: 1.3.9 - resolution: "@types/d3-shape@npm:1.3.9" + version: 1.3.10 + resolution: "@types/d3-shape@npm:1.3.10" dependencies: "@types/d3-path": ^1 - checksum: a4df0c09df4f97a017fbb229a096af6e25fe84e331dfb8c9b1f248a7f3df6f07c871879fabb31050cbec78f34127397a8134a16f2a2359ff681ec846cf886754 + checksum: c5fcb5da6388ffe5b0594d001940bf88589e174ca1b6cb6ffe3367fb8157b7bd70f15a555642abfa1acdf679860e0c1247bbc1f59395fcded29255638f661b55 languageName: node linkType: hard "@types/d3-shape@npm:^3.0.1, @types/d3-shape@npm:^3.1.0": - version: 3.1.3 - resolution: "@types/d3-shape@npm:3.1.3" + version: 3.1.4 + resolution: "@types/d3-shape@npm:3.1.4" dependencies: "@types/d3-path": "*" - checksum: ad17781ab4ce4b796954b86de7e14566c731726d39a1db7d73eaf50668a71e996d715450a0ff9f2720755e1b8643c3e88d47d45101a75c9d4ddbef51a636f6a0 + checksum: 3d529cc96127d8d58da05bc602e51bb7946171bdae96dcce9dae81c1549d3fd3f226a4e651c3302951a1ebf7803e86388c4afcdd654b0d40d203219ce68eb383 languageName: node linkType: hard @@ -17312,19 +18513,19 @@ __metadata: linkType: hard "@types/d3-zoom@npm:^3.0.1": - version: 3.0.5 - resolution: "@types/d3-zoom@npm:3.0.5" + version: 3.0.6 + resolution: "@types/d3-zoom@npm:3.0.6" dependencies: "@types/d3-interpolate": "*" "@types/d3-selection": "*" - checksum: acff15a30b2b81a99b46692c7aacc110c635a272e8d7e87af6aa5a01ebe9ebcafcaf475399d79ec73461a5ef037041dade49a6371d7587b759710123539b81b0 + checksum: 6b0ad10b6a7bab3acfdc92b0cd34c74698305a71e2fefb20eae1e3bac95cbf70c6434be5ea2c1c71bbe9fcb134c66da44fdd75673437199e9a90aea8aea0f7e7 languageName: node linkType: hard "@types/dagre@npm:^0.7.44": - version: 0.7.50 - resolution: "@types/dagre@npm:0.7.50" - checksum: a128048b5202c501a4595bbc17288aafbef90f3535e79385ddaf0842237e69aa5998dbc12bc2867244eefe3fb6c5026d9785bfdedea46d813f006066e06e8f44 + version: 0.7.51 + resolution: "@types/dagre@npm:0.7.51" + checksum: 6a23cc406082fe867dc0be0a117ae0c493bcfb6e08b83ea55a09b4f43e6eb5efc34a102d1a039fa82d2df700d55ee5481c0c3febdf710b972bb345c8a7f6c316 languageName: node linkType: hard @@ -17338,9 +18539,9 @@ __metadata: linkType: hard "@types/diff@npm:^5.0.0": - version: 5.0.6 - resolution: "@types/diff@npm:5.0.6" - checksum: 8ce79492a89d568b54792a8491d01b1ff69526e19560670ef2de078c467738cdc0aae81fc4f895e49f915a2b9bbf694f6f95a2e5544e7c0989448956eba9ff66 + version: 5.0.7 + resolution: "@types/diff@npm:5.0.7" + checksum: 38cad7b1e19789a11bfab2428409476ea204705e92c39dd13a9ba26b604930efcffb553352512296cb179adfb028e2d95e51f9df2b7443bb0a6ba306c8bc84d0 languageName: node linkType: hard @@ -17355,12 +18556,12 @@ __metadata: linkType: hard "@types/dockerode@npm:^3.3.0, @types/dockerode@npm:^3.3.8": - version: 3.3.20 - resolution: "@types/dockerode@npm:3.3.20" + version: 3.3.21 + resolution: "@types/dockerode@npm:3.3.21" dependencies: "@types/docker-modem": "*" "@types/node": "*" - checksum: cb2d7a923cfec9d92b69694e613f0becbea627d4d9d919ab349f28ea81d8f5fa71aa437d89b4bc5291a5edb7a07c643ccff2139e2d7ab92a58235d0b05d2a48d + checksum: 025c97cd2549f1b3f0dc6e117e24e3b7e56dcf3e482e2abe5f2d6961e56e637b0e1f1b4f032e065cd7245302064ac3ccdb9c7dfdb0fe43a5bfb2b5f4ccbf4a8d languageName: node linkType: hard @@ -17373,6 +18574,13 @@ __metadata: languageName: node linkType: hard +"@types/ejs@npm:^3.1.3": + version: 3.1.3 + resolution: "@types/ejs@npm:3.1.3" + checksum: b1b1c6c9d331d237523ebc410789f42edcdbb1d4cdd4a7a37ac61d2ce9c3fbcfbfe7d7f1a7f61c9334812347a0036afd52258ad2198f85545ebfb26d63475a75 + languageName: node + linkType: hard + "@types/es-aggregate-error@npm:^1.0.2": version: 1.0.2 resolution: "@types/es-aggregate-error@npm:1.0.2" @@ -17417,9 +18625,9 @@ __metadata: linkType: hard "@types/event-source-polyfill@npm:^1.0.0": - version: 1.0.2 - resolution: "@types/event-source-polyfill@npm:1.0.2" - checksum: f0813edfee0af768e6e0994b48f9f618df7a5d9023c383a8f35e9e805dec560222dc40fc583d5f1404977febb7353c27c1d0606c67d948a2d18634c18ae437a9 + version: 1.0.3 + resolution: "@types/event-source-polyfill@npm:1.0.3" + checksum: ba525e521ff82de5dad727066174c61fc59680a5703324c4c62cbb11c0a14fbabeabf4d17e6d5051ef995c232a40ff43a8d1f4cea0dde9564bf28c92bb361726 languageName: node linkType: hard @@ -17431,23 +18639,23 @@ __metadata: linkType: hard "@types/express-serve-static-core@npm:*, @types/express-serve-static-core@npm:^4.17.30, @types/express-serve-static-core@npm:^4.17.33, @types/express-serve-static-core@npm:^4.17.5": - version: 4.17.37 - resolution: "@types/express-serve-static-core@npm:4.17.37" + version: 4.17.39 + resolution: "@types/express-serve-static-core@npm:4.17.39" dependencies: "@types/node": "*" "@types/qs": "*" "@types/range-parser": "*" "@types/send": "*" - checksum: 2dab1380e45eb44e56ecc1be1c42c4b897364d2f2a08e03ca28fbcb1e6866e390217385435813711c046f9acd684424d088855dc32825d5cbecf72c60ecd037f + checksum: 4227b96a53f0cf19d01fdb77a74252660f8e70650b79167e591b04c66ec9c7330d0a00038939415f96664a67312b21798bbac150fe81bf613380849b96546c37 languageName: node linkType: hard "@types/express-session@npm:^1.17.2": - version: 1.17.8 - resolution: "@types/express-session@npm:1.17.8" + version: 1.17.9 + resolution: "@types/express-session@npm:1.17.9" dependencies: "@types/express": "*" - checksum: e1af8797b321ce05e0732c58aa982f00ab34b6edb337c289092e66c789ae1bbb4ca14d2caa0e0ff02522047402bcff828873b035e2785fb92f2381a0eb34240c + checksum: e66d50742a27389f3f1bb03b30eb96d74daf357db6cc5e549265e6610f2cff14539fec7e2b72c3f98ab1174eff7ed347e3fe4d3a2b9273a6d1367d78ee0add71 languageName: node linkType: hard @@ -17480,9 +18688,9 @@ __metadata: linkType: hard "@types/git-url-parse@npm:^9.0.0": - version: 9.0.1 - resolution: "@types/git-url-parse@npm:9.0.1" - checksum: 9d1678a0aff50b6e77a172965070de34d2e89bc6ccc96c94ea3b2ecf250c602c0daff4b9f4e0eb830e53e81ee273b08f15f728d42ec087f1a95e4f51d57b594a + version: 9.0.2 + resolution: "@types/git-url-parse@npm:9.0.2" + checksum: af0a935b80d76f2441686f55b43755f5ccc3e5ddb21ae9663df0f55a4954aa9f1f4ddbcad729e3f2365cd09a2a38abb14baec95b9f26aa67e490fc90797d5e0b languageName: node linkType: hard @@ -17555,9 +18763,9 @@ __metadata: linkType: hard "@types/http-errors@npm:^2.0.0": - version: 2.0.2 - resolution: "@types/http-errors@npm:2.0.2" - checksum: d7f14045240ac4b563725130942b8e5c8080bfabc724c8ff3f166ea928ff7ae02c5194763bc8f6aaf21897e8a44049b0492493b9de3e058247e58fdfe0f86692 + version: 2.0.3 + resolution: "@types/http-errors@npm:2.0.3" + checksum: ea9530fb6e8a0400c4f9aac4dd628c5074f0adc8d01e2cdb917c0b97c230dedf4fcc67eadb491377b0eff5778e566648e63613a9719e383185318b9ec8c009b9 languageName: node linkType: hard @@ -17573,28 +18781,28 @@ __metadata: linkType: hard "@types/http-proxy@npm:*, @types/http-proxy@npm:^1.17.4, @types/http-proxy@npm:^1.17.8": - version: 1.17.12 - resolution: "@types/http-proxy@npm:1.17.12" + version: 1.17.13 + resolution: "@types/http-proxy@npm:1.17.13" dependencies: "@types/node": "*" - checksum: 89700c8e3c8f2c59c87c8db8e7a070c97a3b30a4a38223aca6b8b817e6f2ca931f5a500e16ecadc1ebcfed2676cc60e073d8f887e621d84420298728ec6fd000 + checksum: 09e2d59db4689c46a6676496c355cac5499457ce61085121bfab64b8a825fde320574f22f981a8db3f1e9e977ddef55889f99da7f1ff485ac51f22b94308bf12 languageName: node linkType: hard "@types/humanize-duration@npm:^3.18.1, @types/humanize-duration@npm:^3.25.1, @types/humanize-duration@npm:^3.27.1": - version: 3.27.1 - resolution: "@types/humanize-duration@npm:3.27.1" - checksum: d3d69b64918d0cee4aa69580dd04d1b96e3385645d398e27b1535f8c6d0252b0235defb16619379a6196c6bbdfc7b6032f010b25cac44903a32b09db8aa06f30 + version: 3.27.2 + resolution: "@types/humanize-duration@npm:3.27.2" + checksum: e33fd83f859085cc8e34884f492ec028d5a5d9992c3b1be66f7b4949daef6b248b299d3f96b09a5ee077b2884c2bd99cdc3bd98531863c86527d6e662b3e8a82 languageName: node linkType: hard "@types/inquirer@npm:^8.1.3": - version: 8.2.7 - resolution: "@types/inquirer@npm:8.2.7" + version: 8.2.9 + resolution: "@types/inquirer@npm:8.2.9" dependencies: "@types/through": "*" rxjs: ^7.2.0 - checksum: 8f021bc2ad0cdcccbd55abd11a8ecc73f716ea108f2ffb5f8f605d23519b47acb46fdcac032d8c81e7c05f183c0f2300a0a394c449ad8980bc11f333708d4387 + checksum: eee240953215ec85901fdfa391595c2ee0b2e61fb9abf1c5708f665ae558effd1359252375956d3eb73364621c9edf5f74329e0c535814cd5fef68bc192408ca languageName: node linkType: hard @@ -17608,9 +18816,9 @@ __metadata: linkType: hard "@types/is-glob@npm:^4.0.2": - version: 4.0.2 - resolution: "@types/is-glob@npm:4.0.2" - checksum: 50b0a52b6d179781b36bfce35155e1e0dc66b62e2943153d7d7c7079c40ba6236528a254de8be6c52ff9a7a351996887802efd7fd763da3e2121315e4ffe2edf + version: 4.0.3 + resolution: "@types/is-glob@npm:4.0.3" + checksum: 65686e645f1e8125b5a01df5b2d2b247c8e5a23790363538005454d68ba7bd9662cd3bcfbdda9f8eb0a85fb5fccd164645555c8e0c34db63c8c65998fd5909dd languageName: node linkType: hard @@ -17649,21 +18857,21 @@ __metadata: linkType: hard "@types/jest-when@npm:^3.5.0": - version: 3.5.3 - resolution: "@types/jest-when@npm:3.5.3" + version: 3.5.4 + resolution: "@types/jest-when@npm:3.5.4" dependencies: "@types/jest": "*" - checksum: 5dc2661ad570b80b8f97d7dabc50a0229f21818eefc73024b88a70216c2666f56bd97769fef565263d7242878f8187531bb14427dcb06709e9b98a1671668e9d + checksum: 58a9a7b2dd81cb2c87c3d3539d03dc82c72a66282b3930237ad30641d0c27d7c2b9de2cb9da05b0d287bb6d890f82601935ca4e616a1828e5e05ebb4fc444270 languageName: node linkType: hard "@types/jest@npm:*, @types/jest@npm:^29.0.0": - version: 29.4.0 - resolution: "@types/jest@npm:29.4.0" + version: 29.5.6 + resolution: "@types/jest@npm:29.5.6" dependencies: expect: ^29.0.0 pretty-format: ^29.0.0 - checksum: 23760282362a252e6690314584d83a47512d4cd61663e957ed3398ecf98195fe931c45606ee2f9def12f8ed7d8aa102d492ec42d26facdaf8b78094a31e6568e + checksum: fa13a27bd1c8efd0381a419478769d0d6d3a8e93e1952d7ac3a16274e8440af6f73ed6f96ac1ff00761198badf2ee226b5ab5583a5d87a78d609ea78da5c5a24 languageName: node linkType: hard @@ -17678,11 +18886,11 @@ __metadata: linkType: hard "@types/jquery@npm:^3.3.34": - version: 3.5.22 - resolution: "@types/jquery@npm:3.5.22" + version: 3.5.25 + resolution: "@types/jquery@npm:3.5.25" dependencies: "@types/sizzle": "*" - checksum: b81c95e27bb30686c1ad5d3f414781243d0cbb468b91d5584dedd3d3ca07ac99796d876e257a7246d6d9b69916bbb0cd0508142ab9f88848605c6d431144d729 + checksum: 912ce4212447a7c640147345c6b46bde5b12bafc2c97e1abc76939174c3109e3dbb361a534af717be2da4e907bc257558a6bc631971fcc47f7e5f044d315183e languageName: node linkType: hard @@ -17701,19 +18909,19 @@ __metadata: linkType: hard "@types/js-yaml@npm:^4.0.0, @types/js-yaml@npm:^4.0.1": - version: 4.0.7 - resolution: "@types/js-yaml@npm:4.0.7" - checksum: e02eca876f3f19dbba62a26a18e59ec5df4e29300b99808fb0b4ca980cbfbb60e4a2c7d75d521728f5b2a3b92a0da5b9343497b2acf621b8ea019d48b40bbd78 + version: 4.0.8 + resolution: "@types/js-yaml@npm:4.0.8" + checksum: a5a77a5a1eac7e7fb667156c251c2b947ca4ddfdda570726369dd50bd5b2b1d0da2d0fb4273d1b10aa1782406d7b3da8923d957df4fb89dbfa1db06f43297de2 languageName: node linkType: hard "@types/jscodeshift@npm:^0.11.0": - version: 0.11.7 - resolution: "@types/jscodeshift@npm:0.11.7" + version: 0.11.9 + resolution: "@types/jscodeshift@npm:0.11.9" dependencies: ast-types: ^0.14.1 recast: ^0.20.3 - checksum: c6b81d537dca08c24db76e5e75d6056ef7c5b7651aebc319277321e2f2421984cc237ac71593dec374e49f9eae28c22150a37de3a572f141d55382c193091899 + checksum: a5b0bc6250dad3dfbaa9af71f56e65979cbfb632ee3d8da43bd79e729eaf231bc15356cc5de45c7e1f634881ab541f712cdbf554f89032273819c49bc397882e languageName: node linkType: hard @@ -17729,18 +18937,18 @@ __metadata: linkType: hard "@types/json-schema-merge-allof@npm:^0.6.0": - version: 0.6.2 - resolution: "@types/json-schema-merge-allof@npm:0.6.2" + version: 0.6.3 + resolution: "@types/json-schema-merge-allof@npm:0.6.3" dependencies: "@types/json-schema": "*" - checksum: 2be8f35eb47cb6f2344c15625010edd0983d258b0eba76e4193a1e80a3b3334bcb7efe1060594a8420cdcb092201e5ad3d78442f7af2edc548661c19c9a65440 + checksum: c59ddbe5ffb97bbd90dd1654f3745e60ff8854db3b99ba2059c54b71f8d303936577a8262cd875e738200c0e4d59657d8195b415f88bf9fc93dc61aeecde68d1 languageName: node linkType: hard "@types/json-schema@npm:*, @types/json-schema@npm:^7.0.11, @types/json-schema@npm:^7.0.12, @types/json-schema@npm:^7.0.4, @types/json-schema@npm:^7.0.5, @types/json-schema@npm:^7.0.6, @types/json-schema@npm:^7.0.7, @types/json-schema@npm:^7.0.8, @types/json-schema@npm:^7.0.9": - version: 7.0.13 - resolution: "@types/json-schema@npm:7.0.13" - checksum: 345df21a678fa72fb389f35f33de77833d09d4a142bb2bcb27c18690efa4cf70fc2876e43843cefb3fbdb9fcb12cd3e970a90936df30f53bbee899865ff605ab + version: 7.0.14 + resolution: "@types/json-schema@npm:7.0.14" + checksum: 4b3dd99616c7c808201c56f6c7f6552eb67b5c0c753ab3fa03a6cb549aae950da537e9558e53fa65fba23d1be624a1e4e8d20c15027efbe41e03ca56f2b04fb0 languageName: node linkType: hard @@ -17795,7 +19003,7 @@ __metadata: languageName: node linkType: hard -"@types/keyv@npm:*": +"@types/keyv@npm:*, @types/keyv@npm:^3.1.1": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" dependencies: @@ -17889,9 +19097,9 @@ __metadata: linkType: hard "@types/mime-types@npm:^2.1.0": - version: 2.1.2 - resolution: "@types/mime-types@npm:2.1.2" - checksum: 9e3c78f1c63211e0450901212566a046da68d4438a5e543333ec9b0be3259bd5d01532734dc51ead40104889b98d12c7663b65212a318aafad3e34c98204e9e1 + version: 2.1.3 + resolution: "@types/mime-types@npm:2.1.3" + checksum: 2b39ed5de6ee607b3738a30c54879b439a8344e6f86e50af3b275cfb7165ed4d13e7f9a54971f1de4f759dd68929351741cf31c1c42e380503e53be6970bbf82 languageName: node linkType: hard @@ -17917,27 +19125,27 @@ __metadata: linkType: hard "@types/minimist@npm:^1.2.0": - version: 1.2.3 - resolution: "@types/minimist@npm:1.2.3" - checksum: 666ea4f8c39dcbdfbc3171fe6b3902157c845cc9cb8cee33c10deb706cda5e0cc80f98ace2d6d29f6774b0dc21180c96cd73c592a1cbefe04777247c7ba0e84b + version: 1.2.4 + resolution: "@types/minimist@npm:1.2.4" + checksum: d7912f9a466312cbc1333800272b9208178140ef4da2ccec3fa82231c8e67f57f84275b3c19109c4f68f1b7b057baeacc6b80af1de14b58b46e6b54233e44c6a languageName: node linkType: hard "@types/mock-fs@npm:^4.10.0, @types/mock-fs@npm:^4.13.0": - version: 4.13.2 - resolution: "@types/mock-fs@npm:4.13.2" + version: 4.13.3 + resolution: "@types/mock-fs@npm:4.13.3" dependencies: "@types/node": "*" - checksum: 726975ae51f7b4068bb059c633ae28a1d85595d3036c235d3f9c9dc3a9c519dc6b811713af1b686807e80a484a6ec47a742a11b4ada32a6e74eee6538a9f4658 + checksum: 17b34bcec0348d2643b1945871d539f4bf42278a835baf46afbf9e19bcf40a91fec89047c1038b61bec952cb8e614406e79ea3ed22d943caf538a088a8b1b16c languageName: node linkType: hard "@types/morgan@npm:^1.9.0": - version: 1.9.6 - resolution: "@types/morgan@npm:1.9.6" + version: 1.9.7 + resolution: "@types/morgan@npm:1.9.7" dependencies: "@types/node": "*" - checksum: 6525248325a74342f929c958be69c0840c8f3a288e003a8904319cae92e531f17a8aa2700701e66775adcca7f9506dd630fec2f95dc04a3e73add04fde42aab8 + checksum: 3e66ad27bd0ec599981c13bc0425957be861dcb7a31ec464ce01ef98daae828092dbb8250758aeed65c5b28a4ebd449b0baa51064c0611b0e2c8358e2c27cae2 languageName: node linkType: hard @@ -17958,12 +19166,12 @@ __metadata: linkType: hard "@types/ndjson@npm:^2.0.1": - version: 2.0.2 - resolution: "@types/ndjson@npm:2.0.2" + version: 2.0.3 + resolution: "@types/ndjson@npm:2.0.3" dependencies: "@types/node": "*" "@types/through": "*" - checksum: 2f4d60229a01dadd4ed1d7df923223788cc4517a15d0fc07b56b3dbdefb567646334e173d7f976a8a7ce7b85df74bf78fd8805f41bdd9ebd8d135310f472a2cb + checksum: 837e295a192a054dcb7f0b2c109b87c42e3317dbe1f91a38f4194c6c46aac2284cef5a28c35d1e0063cecc411f0e89a7de6d9753406e3861c8434780b32090ea languageName: node linkType: hard @@ -18009,10 +19217,17 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:^16.11.26, @types/node@npm:^16.9.2": - version: 16.18.58 - resolution: "@types/node@npm:16.18.58" - checksum: 6d8404abc6c97bacd220f606441727adea923f3bd010d07d869f0dc6c4c6dc016430880af1f270edf1b84ae637a7fa5339e6adb3abba210a4820ac5e28f34067 +"@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": + version: 16.18.59 + resolution: "@types/node@npm:16.18.59" + checksum: 70f28744d239c48db056ff6355d2eb99305db54d1f9377b3f4458e92ffb4da8962a0c67665452d5f430cea2286ced256dac8f769660007aedd3676cf03ff28ad + languageName: node + linkType: hard + +"@types/node@npm:^17.0.36": + version: 17.0.45 + resolution: "@types/node@npm:17.0.45" + checksum: aa04366b9103b7d6cfd6b2ef64182e0eaa7d4462c3f817618486ea0422984c51fc69fd0d436eae6c9e696ddfdbec9ccaa27a917f7c2e8c75c5d57827fe3d95e8 languageName: node linkType: hard @@ -18038,9 +19253,9 @@ __metadata: linkType: hard "@types/nunjucks@npm:^3.1.4": - version: 3.2.4 - resolution: "@types/nunjucks@npm:3.2.4" - checksum: d154c6cc98320b6b9f48892eb4238f961b0196e24eb98252f9dceaa0b17bf70cd480a11d4ffda9f419d2570abf6d96524ebd3193b101f0fcf73c65af3e56d064 + version: 3.2.5 + resolution: "@types/nunjucks@npm:3.2.5" + checksum: 3b2990bb7999af5640d47c296ec01f9f96356a31b3aa935f3a7d9cac428b10b5061fe3b8328761ab267cb0151a040a4c8f9789d1f5c93179a00c9056c0676a64 languageName: node linkType: hard @@ -18061,30 +19276,30 @@ __metadata: linkType: hard "@types/parse-link-header@npm:^2.0.1": - version: 2.0.1 - resolution: "@types/parse-link-header@npm:2.0.1" - checksum: f76678612511365aefc23704f00f3262fcb1d6bd9c4dd83f783a38e50e3ccf26615f9a62c757952cab5af6f2bd8b3b1763ce391376458afce5fe47c6fe2b80e1 + version: 2.0.2 + resolution: "@types/parse-link-header@npm:2.0.2" + checksum: 5b586d44dc2a4671222358da90cd8f6143606efc06c319043404d9a323e574ae7297ecae304f60fbf0358bf659038f7c95b570252af7f869028a7e74cd8c8d55 languageName: node linkType: hard "@types/passport-auth0@npm:^1.0.5": - version: 1.0.7 - resolution: "@types/passport-auth0@npm:1.0.7" + version: 1.0.8 + resolution: "@types/passport-auth0@npm:1.0.8" dependencies: "@types/express": "*" "@types/passport": "*" - checksum: 75b17c3424054a8b0238905088a7c34efab771a0bccc5ce0270d5c6f238ba859fb7354531c0df4f39e7b2c28f10090f9c260ffc42773211fc216a9acbf11170a + checksum: fd4534596ec4daf61a8acf66c13a2434b54b5da269c86ed67d2d0cdf654be7cd5da808426150db91ce25ddf37a83c75424e17ad7feeea697b6eb9c4b85678833 languageName: node linkType: hard "@types/passport-github2@npm:^1.2.4": - version: 1.2.6 - resolution: "@types/passport-github2@npm:1.2.6" + version: 1.2.8 + resolution: "@types/passport-github2@npm:1.2.8" dependencies: "@types/express": "*" "@types/passport": "*" "@types/passport-oauth2": "*" - checksum: 72d1c8bc335d0d7f329372ac8bc7edb22c8bea151d4438d876036b6b768f4d12c8de305abbc25b650f373b902bae250008cb185074d2d78da49d15e981208d81 + checksum: 31ad5dfec1031e55c4faadf302e6467114e57bb093501c3f6d6fe5fc0c399a87a16a463725fa0bf4a0ac2944cf66cb458a9289aed727d21a67d97d833002994c languageName: node linkType: hard @@ -18100,11 +19315,11 @@ __metadata: linkType: hard "@types/passport-microsoft@npm:^1.0.0": - version: 1.0.1 - resolution: "@types/passport-microsoft@npm:1.0.1" + version: 1.0.2 + resolution: "@types/passport-microsoft@npm:1.0.2" dependencies: "@types/passport-oauth2": "*" - checksum: eec44ee6e14448b89434503a438021df6738513fa705b42a6d979a6b817ce89f939996a5d30707a64bca700dbc7c6ae52f1010ad2e986bb7ccd9f935d2114c9e + checksum: c8173898aba22d443cacf764da991be8461a034328bdff00ff048e76fa930d3a72e7d28f049e79c12bd35d46acdbf08f2e0f7796c7915b2018c0647d7e6ac589 languageName: node linkType: hard @@ -18120,22 +19335,22 @@ __metadata: linkType: hard "@types/passport-saml@npm:^1.1.3": - version: 1.1.4 - resolution: "@types/passport-saml@npm:1.1.4" + version: 1.1.5 + resolution: "@types/passport-saml@npm:1.1.5" dependencies: "@types/express": "*" "@types/passport": "*" - checksum: 2a71d64c72b0f6cf4cafcc21138f5c925cc9bc375a0dd7ece664e8b1254dd4a704566632bafa3e16032821a7f010de4023169589b643a3b952709cde228a8c8c + checksum: 505fdf8ccdac8a3e5730c37a98742af8c8557e2a1604b25759de1631939494089ea9f6935bf74ab46d227d6ff69d2ca9cf7deea05c44938d661fff6609a3bca2 languageName: node linkType: hard "@types/passport-strategy@npm:^0.2.35": - version: 0.2.36 - resolution: "@types/passport-strategy@npm:0.2.36" + version: 0.2.37 + resolution: "@types/passport-strategy@npm:0.2.37" dependencies: "@types/express": "*" "@types/passport": "*" - checksum: 7f626eb6a12110c1ce64dd5d5181fc0795e77d60123872e33f6fa1c02a2a19451207eaf03543232241a1d70bfe0318f2ffeef88f6309d7cbaa43d2bf6ec1d1e8 + checksum: ca85308b0bba74e299dde4c31a26fb11559d62c87daa9a2321edc0f99930efdf45913acf0bf54162623a98ad0559a277b2ad95c4b26b518db1e93a02ede89e28 languageName: node linkType: hard @@ -18149,20 +19364,20 @@ __metadata: linkType: hard "@types/pg@npm:^8.6.6": - version: 8.10.5 - resolution: "@types/pg@npm:8.10.5" + version: 8.10.7 + resolution: "@types/pg@npm:8.10.7" dependencies: "@types/node": "*" pg-protocol: "*" pg-types: ^4.0.1 - checksum: 532a3104ed7898e3c7806e00cbde486dcfc803d2e99e236066f1b675ff0546a1cb232fe6f2ffc60dac81d3df209b69dbe62a597b565bff0965dd622fef957afe + checksum: 90a616360844b6f877be21a24839af2115f5eb9c8e3eeaf577fa0f036c6c56ad140113fdd8268e63cf215bd0953d89caa1af05191dd89cb2e6d96eb6db6074d2 languageName: node linkType: hard "@types/ping@npm:^0.4.1": - version: 0.4.2 - resolution: "@types/ping@npm:0.4.2" - checksum: 4ea20f8c04d6d52ac9d4a551af03b940c273616fc647d9905197400f920f744c5224ee6913ef26b845633e8e44ebab4195e4fd71daf271090336f303069b7fef + version: 0.4.3 + resolution: "@types/ping@npm:0.4.3" + checksum: 8b0855eeeea0f894c90415dab8eb467cb7142b883cf30473663a93e82f58ad08f5fd9d07460d5f8e7fa3c5987c15c9a638e236a118a0cee9c566f95e53095406 languageName: node linkType: hard @@ -18173,10 +19388,10 @@ __metadata: languageName: node linkType: hard -"@types/prettier@npm:^2.1.5": - version: 2.4.3 - resolution: "@types/prettier@npm:2.4.3" - checksum: b240434daabac54700c862b0bb52a83fec396e0e9c847447119ba41fd8404d79aadfa174e6306fb094b29efadac586344b7606c3a71c286b71755ab2579d54df +"@types/prettier@npm:^2.0.0": + version: 2.7.3 + resolution: "@types/prettier@npm:2.7.3" + checksum: 705384209cea6d1433ff6c187c80dcc0b95d99d5c5ce21a46a9a58060c527973506822e428789d842761e0280d25e3359300f017fbe77b9755bc772ab3dc2f83 languageName: node linkType: hard @@ -18229,20 +19444,20 @@ __metadata: linkType: hard "@types/react-grid-layout@npm:^1.3.2": - version: 1.3.3 - resolution: "@types/react-grid-layout@npm:1.3.3" + version: 1.3.4 + resolution: "@types/react-grid-layout@npm:1.3.4" dependencies: "@types/react": "*" - checksum: 918173791c2b121f2780ccfcd36b6fe79c8b43d014ab8bbc37d6e70ee1849bfe81281450d121a2361ad15b4ddfbdfc95c9790ac600f732a20897844d735c455b + checksum: e960b250f22b799a52c740a68a73bf1c97ba97148abfe4c270c4d227a821c98b93487510a6270a57e394ff467eaaa7a3c1378e71ef4d78659fd65eb87873c923 languageName: node linkType: hard "@types/react-helmet@npm:^6.1.0": - version: 6.1.7 - resolution: "@types/react-helmet@npm:6.1.7" + version: 6.1.8 + resolution: "@types/react-helmet@npm:6.1.8" dependencies: "@types/react": "*" - checksum: d5ee8343f10f8746a022249c6401493baa73241a90b5002b59e8a6ce31650b9e1ab38d398eba5564af026f68eda107c8fcdca01fc046a163a96dc422710f6a77 + checksum: 380d6aa2760a43dab59d46f291caf9498abfda813e343aeaea8b814fe21a69b27e9ad97962b5541ad75179eb9b7875027e4fcc7009187b2f68251ac9f0923a2c languageName: node linkType: hard @@ -18268,11 +19483,11 @@ __metadata: linkType: hard "@types/react-syntax-highlighter@npm:^15.0.0": - version: 15.5.8 - resolution: "@types/react-syntax-highlighter@npm:15.5.8" + version: 15.5.9 + resolution: "@types/react-syntax-highlighter@npm:15.5.9" dependencies: "@types/react": "*" - checksum: 3bee4fe5901abc57c06b70aee528a7a2aa9617f8347fbe960f6b7ba14a231aa6fab892e1f6cd9b1e1321078183a9773fcf55488df63cfc7dfa8a307aa48040b3 + checksum: b03c2b4b62a950f29cb46a5eae95b81add333b0d2b107d1d35c4efe639529ba424dab364d09b3d57ecef0f0394369d6680caaed80872184ebf5fb84749095808 languageName: node linkType: hard @@ -18304,11 +19519,11 @@ __metadata: linkType: hard "@types/react-virtualized-auto-sizer@npm:^1.0.1": - version: 1.0.1 - resolution: "@types/react-virtualized-auto-sizer@npm:1.0.1" + version: 1.0.2 + resolution: "@types/react-virtualized-auto-sizer@npm:1.0.2" dependencies: "@types/react": "*" - checksum: 67eff0670a1991c2b16992274ada5f0b3f9d5c2d6209ef38e8f8ae2c0218211a3292882f5b7dd6f09519000dc20847629f049c9acc267e000626b7141e5927a6 + checksum: b94702678431df4efd0accc30c4657745a65f1128a46e5f68b52d269069c7aef0560ea8cb7f2c6c15fd2b47abd9efa0381ff0e847cceea4074cad73cbcece96a languageName: node linkType: hard @@ -18762,12 +19977,12 @@ __metadata: languageName: node linkType: hard -"@types/ws@npm:^8.0.0, @types/ws@npm:^8.5.3, @types/ws@npm:^8.5.5": - version: 8.5.5 - resolution: "@types/ws@npm:8.5.5" +"@types/ws@npm:*, @types/ws@npm:^8.0.0, @types/ws@npm:^8.5.3, @types/ws@npm:^8.5.5": + version: 8.5.8 + resolution: "@types/ws@npm:8.5.8" dependencies: "@types/node": "*" - checksum: d00bf8070e6938e3ccf933010921c6ce78ac3606696ce37a393b27a9a603f7bd93ea64f3c5fa295a2f743575ba9c9a9fdb904af0f5fe2229bf2adf0630386e4a + checksum: 4ad30de842834d4dd8e6e1476470752709d4165352a3a36780f23f4fdb686d4ac8ca5e16a0e0622940ddace910b856ff8a0baa2e24e41d204fb7a6a02ab2172b languageName: node linkType: hard @@ -19065,6 +20280,192 @@ __metadata: languageName: node linkType: hard +"@useoptic/json-pointer-helpers@npm:0.50.10": + version: 0.50.10 + resolution: "@useoptic/json-pointer-helpers@npm:0.50.10" + dependencies: + jsonpointer: ^5.0.1 + minimatch: 9.0.3 + checksum: 02785a8d260fa6b499b09c909578a6583cfcaff44d3dd9cec5f9b0c8e118283764b2e54420b2790218e3053531679ce0d0bdd50e170c92a9bdc28760792a0757 + languageName: node + linkType: hard + +"@useoptic/openapi-io@npm:0.50.10": + version: 0.50.10 + resolution: "@useoptic/openapi-io@npm:0.50.10" + dependencies: + "@apidevtools/json-schema-ref-parser": 9.0.9 + "@jsdevtools/ono": ^7.1.3 + "@useoptic/json-pointer-helpers": 0.50.10 + "@useoptic/openapi-utilities": 0.50.10 + ajv: ^8.6.0 + ajv-errors: ~3.0.0 + ajv-formats: ~2.1.0 + bottleneck: ^2.19.5 + chalk: ^4.1.2 + fast-deep-equal: ^3.1.3 + fast-json-patch: ^3.1.1 + is-url: ^1.2.4 + json-stable-stringify: ^1.0.1 + lodash.clonedeep: ^4.5.0 + lodash.sortby: ^4.7.0 + node-fetch: ^2.6.7 + openapi-types: ^12.0.2 + semver: ^7.5.4 + upath: ^2.0.1 + yaml: ^2.3.2 + yaml-ast-parser: ^0.0.43 + checksum: 02443ea9448e07b7ef7c2de509f2e31fe5751cc8eafefffb9b83cc83522296fc392da84ba9b95587e609efb485ced6dcc3b6aa270751bb211c5cc91ea33730ff + languageName: node + linkType: hard + +"@useoptic/openapi-utilities@npm:0.50.10": + version: 0.50.10 + resolution: "@useoptic/openapi-utilities@npm:0.50.10" + dependencies: + "@useoptic/json-pointer-helpers": 0.50.10 + ajv: ^8.6.0 + ajv-errors: ~3.0.0 + ajv-formats: ~2.1.0 + chalk: ^4.1.2 + fast-deep-equal: ^3.1.3 + is-url: ^1.2.4 + js-yaml: ^4.1.0 + json-stable-stringify: ^1.0.1 + lodash.groupby: ^4.6.0 + lodash.isequal: ^4.5.0 + lodash.omit: ^4.5.0 + node-machine-id: ^1.1.12 + openapi-types: ^12.0.2 + ts-invariant: ^0.9.3 + url-join: ^4.0.1 + yaml-ast-parser: ^0.0.43 + checksum: 687b8db975467ff4a3db03c6aa024c1be90cfaa5c1db6723c1446f13a87aab5b90556f76c244c29d6e3b41248ae2274bc185d9746b82f6942ce91ca617d8a249 + languageName: node + linkType: hard + +"@useoptic/optic@npm:^0.50.10": + version: 0.50.10 + resolution: "@useoptic/optic@npm:0.50.10" + dependencies: + "@babel/runtime": ^7.20.6 + "@httptoolkit/httpolyglot": ^2.0.1 + "@jsdevtools/ono": ^7.1.3 + "@octokit/rest": ^19.0.0 + "@sentry/node": 7.70.0 + "@sinclair/typebox": ^0.31.0 + "@stoplight/spectral-core": ^1.8.1 + "@useoptic/openapi-io": 0.50.10 + "@useoptic/openapi-utilities": 0.50.10 + "@useoptic/rulesets-base": 0.50.10 + "@useoptic/standard-rulesets": 0.50.10 + ajv: ^8.6.0 + ajv-formats: ~2.1.0 + analytics-node: ^6.2.0 + async-exit-hook: ^2.0.1 + axax: ^0.2.2 + bottleneck: ^2.19.5 + chalk: ^4.1.2 + commander: ^11.0.0 + conf: ^10.2.0 + crosspath: ^2.0.0 + dotenv: ^16.0.3 + exit-hook: ^2.2.1 + fast-deep-equal: ^3.1.3 + fast-glob: ^3.2.12 + fs-extra: ^11.1.0 + git-url-parse: ^13.1.0 + har-schema: ^2.0.0 + is-elevated: ^3.0.0 + is-url: ^1.2.4 + js-yaml: ^4.1.0 + json-schema-traverse: ^1.0.0 + json-stable-stringify: ^1.0.1 + latest-version: ^5.1.0 + lodash.chunk: ^4.2.0 + lodash.groupby: ^4.6.0 + lodash.sortby: ^4.7.0 + log: ^6.3.1 + log-node: ^8.0.3 + loglevel: ^1.8.0 + micromatch: ^4.0.5 + minimatch: 9.0.3 + mockttp: ^3.9.1 + node-fetch: ^2.6.7 + node-forge: ^1.2.1 + node-machine-id: ^1.1.12 + open: ^8.4.0 + ora: 5.4.1 + pluralize: 8.0.0 + portfinder: ^1.0.28 + postman-collection: ^4.1.7 + prompts: ^2.4.2 + semver: ^7.5.4 + slice-ansi: ^4.0.0 + stream-chain: ^2.2.5 + stream-json: ^1.7.4 + strip-ansi: ^6.0.1 + tar: ^6.1.11 + ts-invariant: ^0.9.4 + ts-results: ^3.3.0 + tunnel: ^0.0.6 + update-notifier: ^5.1.0 + url-join: ^4.0.1 + whatwg-mimetype: ^3.0.0 + bin: + optic: build/index.js + checksum: 3316ee1fad67320271fb1f7caf625661f8cb587bedcac2aba1d506a1d179cec5a45f35b189387898a3b341eab84f662094f34766c070cb001e9d7ed1b45df455 + languageName: node + linkType: hard + +"@useoptic/rulesets-base@npm:0.50.10": + version: 0.50.10 + resolution: "@useoptic/rulesets-base@npm:0.50.10" + dependencies: + "@stoplight/spectral-core": ^1.8.1 + "@stoplight/spectral-rulesets": ^1.14.1 + "@useoptic/json-pointer-helpers": 0.50.10 + "@useoptic/openapi-utilities": 0.50.10 + ajv: ^8.6.0 + lodash.pick: ^4.4.0 + node-fetch: ^2.6.7 + semver: ^7.5.4 + bin: + rulesets-base: build/index.js + checksum: 5b3f57458cc198ac27965096cb02b6fc8d32dea87ab85a75780b53fdbdbf7a452ffae67e82f95f1fdfddbb8d70347b491a17f46bee6bbbad23ce99a2bd865c2e + languageName: node + linkType: hard + +"@useoptic/standard-rulesets@npm:0.50.10": + version: 0.50.10 + resolution: "@useoptic/standard-rulesets@npm:0.50.10" + dependencies: + "@useoptic/openapi-utilities": 0.50.10 + "@useoptic/rulesets-base": 0.50.10 + ajv: ^8.6.0 + ajv-formats: ~2.1.0 + whatwg-mimetype: ^3.0.0 + bin: + standard-rulesets: build/index.js + checksum: 174b24489d2ba8d8797fc5542858d44c084514b5b7085e835b19fc70a64ca65e139f0a44a5ff2f364e4a2eaad69f0f0498b08013725503c6b9e7808b85b52cd0 + languageName: node + linkType: hard + +"@vitejs/plugin-react@npm:^4.0.4": + version: 4.1.0 + resolution: "@vitejs/plugin-react@npm:4.1.0" + dependencies: + "@babel/core": ^7.22.20 + "@babel/plugin-transform-react-jsx-self": ^7.22.5 + "@babel/plugin-transform-react-jsx-source": ^7.22.5 + "@types/babel__core": ^7.20.2 + react-refresh: ^0.14.0 + peerDependencies: + vite: ^4.2.0 + checksum: 73dd403f5bca4f3f99f0bd3dcbb0cc0ecf88f758b886fb599711be744ca93f20adafe1af3574a998ac7cbd24aaf67ac7fe06983d87088cbdf535540ab402d496 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.6, @webassemblyjs/ast@npm:^1.11.5": version: 1.11.6 resolution: "@webassemblyjs/ast@npm:1.11.6" @@ -19476,11 +20877,11 @@ __metadata: linkType: hard "acorn@npm:^8.2.4, acorn@npm:^8.4.1, acorn@npm:^8.5.0, acorn@npm:^8.7.1, acorn@npm:^8.9.0": - version: 8.9.0 - resolution: "acorn@npm:8.9.0" + version: 8.10.0 + resolution: "acorn@npm:8.10.0" bin: acorn: bin/acorn - checksum: 25dfb94952386ecfb847e61934de04a4e7c2dc21c2e700fc4e2ef27ce78cb717700c4c4f279cd630bb4774948633c3859fc16063ec8573bda4568e0a312e6744 + checksum: 538ba38af0cc9e5ef983aee196c4b8b4d87c0c94532334fa7e065b2c8a1f85863467bb774231aae91613fcda5e68740c15d97b1967ae3394d20faddddd8af61d languageName: node linkType: hard @@ -19500,6 +20901,15 @@ __metadata: languageName: node linkType: hard +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0": + version: 7.1.0 + resolution: "agent-base@npm:7.1.0" + dependencies: + debug: ^4.3.4 + checksum: f7828f991470a0cc22cb579c86a18cbae83d8a3cbed39992ab34fc7217c4d126017f1c74d0ab66be87f71455318a8ea3e757d6a37881b8d0f2a2c6aa55e5418f + languageName: node + linkType: hard + "agentkeepalive@npm:^4.1.3, agentkeepalive@npm:^4.1.4, agentkeepalive@npm:^4.2.1": version: 4.2.1 resolution: "agentkeepalive@npm:4.2.1" @@ -19647,6 +21057,31 @@ __metadata: languageName: node linkType: hard +"analytics-node@npm:^6.2.0": + version: 6.2.0 + resolution: "analytics-node@npm:6.2.0" + dependencies: + "@segment/loosely-validate-event": ^2.0.0 + axios: ^0.27.2 + axios-retry: 3.2.0 + lodash.isstring: ^4.0.1 + md5: ^2.2.1 + ms: ^2.0.0 + remove-trailing-slash: ^0.1.0 + uuid: ^8.3.2 + checksum: d682f99742255b0e00a5f7a9a6245736eb04917cb0eb4cb196c3f7f1f889632f2fef62e64d471df24fd35d4da7e6581db08b43fe770cc67c5f277158acef5267 + languageName: node + linkType: hard + +"ansi-align@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: ^4.1.0 + checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d + languageName: node + linkType: hard + "ansi-colors@npm:^4.1.1, ansi-colors@npm:^4.1.3": version: 4.1.3 resolution: "ansi-colors@npm:4.1.3" @@ -19870,6 +21305,15 @@ __metadata: languageName: node linkType: hard +"aria-hidden@npm:^1.1.1": + version: 1.2.3 + resolution: "aria-hidden@npm:1.2.3" + dependencies: + tslib: ^2.0.0 + checksum: 7d7d211629eef315e94ed3b064c6823d13617e609d3f9afab1c2ed86399bb8e90405f9bdd358a85506802766f3ecb468af985c67c846045a34b973bcc0289db9 + languageName: node + linkType: hard + "aria-query@npm:5.1.3, aria-query@npm:^5.0.0, aria-query@npm:^5.1.3": version: 5.1.3 resolution: "aria-query@npm:5.1.3" @@ -20098,6 +21542,15 @@ __metadata: languageName: node linkType: hard +"ast-types@npm:^0.13.4": + version: 0.13.4 + resolution: "ast-types@npm:0.13.4" + dependencies: + tslib: ^2.0.1 + checksum: 5a51f7b70588ecced3601845a0e203279ca2f5fdc184416a0a1640c93ec0a267241d6090a328e78eebb8de81f8754754e0a4f1558ba2a3d638f8ccbd0b1f0eff + languageName: node + linkType: hard + "ast-types@npm:^0.16.1": version: 0.16.1 resolution: "ast-types@npm:0.16.1" @@ -20123,6 +21576,13 @@ __metadata: languageName: node linkType: hard +"async-exit-hook@npm:^2.0.1": + version: 2.0.1 + resolution: "async-exit-hook@npm:2.0.1" + checksum: b72cbdd19ea90fa33a3a57b0dbff83e4bf2f4e4acd70b2b3847a588f9f16a45d38590ee13f285375dd919c224f60fa58dc3d315a87678d3aa24ff686d1c0200a + languageName: node + linkType: hard + "async-lock@npm:^1.1.0": version: 1.2.4 resolution: "async-lock@npm:1.2.4" @@ -20139,6 +21599,15 @@ __metadata: languageName: node linkType: hard +"async@npm:^2.6.2, async@npm:^2.6.4": + version: 2.6.4 + resolution: "async@npm:2.6.4" + dependencies: + lodash: ^4.17.14 + checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 + languageName: node + linkType: hard + "async@npm:^3.2.0, async@npm:^3.2.3, async@npm:^3.2.4": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -20169,6 +21638,16 @@ __metadata: languageName: node linkType: hard +"atlassian-openapi@npm:^1.0.8": + version: 1.0.17 + resolution: "atlassian-openapi@npm:1.0.17" + dependencies: + jsonpointer: ^5.0.0 + urijs: ^1.19.10 + checksum: 372a454e8f5e000e016e261b2151019f0b3dabfad908593c71aae23ebd5b9998c374ae3c0bf0d85dd55dbcca94d5d93e38edf02346d0bd2cb34d3fd20968ddaf + languageName: node + linkType: hard + "atomic-sleep@npm:^1.0.0": version: 1.0.0 resolution: "atomic-sleep@npm:1.0.0" @@ -20176,6 +21655,13 @@ __metadata: languageName: node linkType: hard +"atomically@npm:^1.7.0": + version: 1.7.0 + resolution: "atomically@npm:1.7.0" + checksum: 991153b17334597f93b58e831bea9851e57ed9cd41d8f33991be063f170b5cc8ec7ff8605f3eb95c1d389c2ad651039e9eb8f2b795e24833c2ceb944f347373a + languageName: node + linkType: hard + "auto-bind@npm:~4.0.0": version: 4.0.0 resolution: "auto-bind@npm:4.0.0" @@ -20243,6 +21729,13 @@ __metadata: languageName: node linkType: hard +"axax@npm:^0.2.2": + version: 0.2.2 + resolution: "axax@npm:0.2.2" + checksum: d1f20cf4186f4db3ee5e04da957759ff17e55a2e613d4f368d9f35ecc3257e88c3890a79338d2e0030d70d234385fc4f06b537cd2753e987954b3cb60519db0d + languageName: node + linkType: hard + "axe-core@npm:^4.6.2": version: 4.6.2 resolution: "axe-core@npm:4.6.2" @@ -20264,6 +21757,15 @@ __metadata: languageName: node linkType: hard +"axios-retry@npm:3.2.0": + version: 3.2.0 + resolution: "axios-retry@npm:3.2.0" + dependencies: + is-retry-allowed: ^1.1.0 + checksum: 411bedb3d2254bd05f9eaf8c9c7a7e14a985ad424f3448d6ec15e2a3584079c3bb99950c07465bdbab1138a5329ccb3e9d22c58db58c83253df4ca3f41e315e6 + languageName: node + linkType: hard + "axios@npm:0.27.2, axios@npm:^0.27.2": version: 0.27.2 resolution: "axios@npm:0.27.2" @@ -20322,20 +21824,20 @@ __metadata: languageName: node linkType: hard -"babel-jest@npm:^29.4.3": - version: 29.4.3 - resolution: "babel-jest@npm:29.4.3" +"babel-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "babel-jest@npm:29.7.0" dependencies: - "@jest/transform": ^29.4.3 + "@jest/transform": ^29.7.0 "@types/babel__core": ^7.1.14 babel-plugin-istanbul: ^6.1.1 - babel-preset-jest: ^29.4.3 + babel-preset-jest: ^29.6.3 chalk: ^4.0.0 graceful-fs: ^4.2.9 slash: ^3.0.0 peerDependencies: "@babel/core": ^7.8.0 - checksum: a1a95937adb5e717dbffc2eb9e583fa6d26c7e5d5b07bb492a2d7f68631510a363e9ff097eafb642ad642dfac9dc2b13872b584f680e166a4f0922c98ea95853 + checksum: ee6f8e0495afee07cac5e4ee167be705c711a8cc8a737e05a587a131fdae2b3c8f9aa55dfd4d9c03009ac2d27f2de63d8ba96d3e8460da4d00e8af19ef9a83f7 languageName: node linkType: hard @@ -20352,15 +21854,15 @@ __metadata: languageName: node linkType: hard -"babel-plugin-jest-hoist@npm:^29.4.3": - version: 29.4.3 - resolution: "babel-plugin-jest-hoist@npm:29.4.3" +"babel-plugin-jest-hoist@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-plugin-jest-hoist@npm:29.6.3" dependencies: "@babel/template": ^7.3.3 "@babel/types": ^7.3.3 "@types/babel__core": ^7.1.14 "@types/babel__traverse": ^7.0.6 - checksum: c8702a6db6b30ec39dfb9f8e72b501c13895231ed80b15ed2648448f9f0c7b7cc4b1529beac31802ae655f63479a05110ca612815aa25fb1b0e6c874e1589137 + checksum: 51250f22815a7318f17214a9d44650ba89551e6d4f47a2dc259128428324b52f5a73979d010cefd921fd5a720d8c1d55ad74ff601cd94c7bd44d5f6292fde2d1 languageName: node linkType: hard @@ -20510,15 +22012,15 @@ __metadata: languageName: node linkType: hard -"babel-preset-jest@npm:^29.4.3": - version: 29.4.3 - resolution: "babel-preset-jest@npm:29.4.3" +"babel-preset-jest@npm:^29.6.3": + version: 29.6.3 + resolution: "babel-preset-jest@npm:29.6.3" dependencies: - babel-plugin-jest-hoist: ^29.4.3 + babel-plugin-jest-hoist: ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 peerDependencies: "@babel/core": ^7.0.0 - checksum: a091721861ea2f8d969ace8fe06570cff8f2e847dbc6e4800abacbe63f72131abde615ce0a3b6648472c97e55a5be7f8bf7ae381e2b194ad2fa1737096febcf5 + checksum: aa4ff2a8a728d9d698ed521e3461a109a1e66202b13d3494e41eea30729a5e7cc03b3a2d56c594423a135429c37bf63a9fa8b0b9ce275298be3095a88c69f6fb languageName: node linkType: hard @@ -20581,6 +22083,13 @@ __metadata: languageName: node linkType: hard +"base64-arraybuffer@npm:^0.1.5": + version: 0.1.5 + resolution: "base64-arraybuffer@npm:0.1.5" + checksum: 44588c1b4460faf59643cf3bcf346a7ede9df70d97aec6dbee4fbae15f6b6220d679b8db076771ea4ef5713dd710e7db7a4a3f81bbb04c71fb06764697d9a021 + languageName: node + linkType: hard + "base64-arraybuffer@npm:^1.0.1": version: 1.0.2 resolution: "base64-arraybuffer@npm:1.0.2" @@ -20618,6 +22127,13 @@ __metadata: languageName: node linkType: hard +"basic-ftp@npm:^5.0.2": + version: 5.0.3 + resolution: "basic-ftp@npm:5.0.3" + checksum: 8b04e88eb85a64de9311721bb0707c9cd70453eefdd854cab85438e6f46fb6c597ddad57ed1acf0a9ede3c677b14e657f51051688a5f23d6f3ea7b5d9073b850 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -20800,7 +22316,7 @@ __metadata: languageName: node linkType: hard -"body-parser@npm:^1.20.0": +"body-parser@npm:^1.15.2, body-parser@npm:^1.20.0": version: 1.20.2 resolution: "body-parser@npm:1.20.2" dependencies: @@ -20846,7 +22362,7 @@ __metadata: languageName: node linkType: hard -"bottleneck@npm:^2.15.3": +"bottleneck@npm:^2.15.3, bottleneck@npm:^2.19.5": version: 2.19.5 resolution: "bottleneck@npm:2.19.5" checksum: c5eef1bbea12cef1f1405e7306e7d24860568b0f7ac5eeab706a86762b3fc65ef6d1c641c8a166e4db90f412fc5c948fc5ce8008a8cd3d28c7212ef9c3482bda @@ -20860,6 +22376,22 @@ __metadata: languageName: node linkType: hard +"boxen@npm:^5.0.0": + version: 5.1.2 + resolution: "boxen@npm:5.1.2" + dependencies: + ansi-align: ^3.0.0 + camelcase: ^6.2.0 + chalk: ^4.1.0 + cli-boxes: ^2.2.1 + string-width: ^4.2.2 + type-fest: ^0.20.2 + widest-line: ^3.1.0 + wrap-ansi: ^7.0.0 + checksum: 82d03e42a72576ff235123f17b7c505372fe05c83f75f61e7d4fa4bcb393897ec95ce766fecb8f26b915f0f7a7227d66e5ec7cef43f5b2bd9d3aeed47ec55877 + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -20911,6 +22443,13 @@ __metadata: languageName: node linkType: hard +"brotli-wasm@npm:^1.1.0": + version: 1.3.1 + resolution: "brotli-wasm@npm:1.3.1" + checksum: ec2931a989ee6f0bb52c2aabf23a0d230232d3bd69fb68ee3dab9542fc9ae2d4085d0e5338f71520c25a4a26cf1cfc991ce02910c24d63d42c7915c5722a3713 + languageName: node + linkType: hard + "browser-headers@npm:^0.4.1": version: 0.4.1 resolution: "browser-headers@npm:0.4.1" @@ -20925,6 +22464,15 @@ __metadata: languageName: node linkType: hard +"browser-resolve@npm:^2.0.0": + version: 2.0.0 + resolution: "browser-resolve@npm:2.0.0" + dependencies: + resolve: ^1.17.0 + checksum: 69225e73b555bd6d2a08fb93c7342cfcf3b5058b975099c52649cd5c3cec84c2066c5385084d190faedfb849684d9dabe10129f0cd401d1883572f2e6650f440 + languageName: node + linkType: hard + "browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" @@ -21047,6 +22595,16 @@ __metadata: languageName: node linkType: hard +"buffer-polyfill@npm:buffer@^6.0.3, buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.2.1 + checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 + languageName: node + linkType: hard + "buffer-writer@npm:2.0.0": version: 2.0.0 resolution: "buffer-writer@npm:2.0.0" @@ -21082,7 +22640,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0": +"buffer@npm:^5.5.0, buffer@npm:^5.7.1": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -21092,16 +22650,6 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^6.0.3": - version: 6.0.3 - resolution: "buffer@npm:6.0.3" - dependencies: - base64-js: ^1.3.1 - ieee754: ^1.2.1 - checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 - languageName: node - linkType: hard - "builtin-modules@npm:^3.0.0": version: 3.2.0 resolution: "builtin-modules@npm:3.2.0" @@ -21232,6 +22780,28 @@ __metadata: languageName: node linkType: hard +"cacheable-lookup@npm:^6.0.0": + version: 6.1.0 + resolution: "cacheable-lookup@npm:6.1.0" + checksum: 4e37afe897219b1035335b0765106a2c970ffa930497b43cac5000b860f3b17f48d004187279fae97e2e4cbf6a3693709b6d64af65279c7d6c8453321d36d118 + languageName: node + linkType: hard + +"cacheable-request@npm:^6.0.0": + version: 6.1.0 + resolution: "cacheable-request@npm:6.1.0" + dependencies: + clone-response: ^1.0.2 + get-stream: ^5.1.0 + http-cache-semantics: ^4.0.0 + keyv: ^3.0.0 + lowercase-keys: ^2.0.0 + normalize-url: ^4.1.0 + responselike: ^1.0.2 + checksum: b510b237b18d17e89942e9ee2d2a077cb38db03f12167fd100932dfa8fc963424bfae0bfa1598df4ae16c944a5484e43e03df8f32105b04395ee9495e9e4e9f1 + languageName: node + linkType: hard + "cacheable-request@npm:^7.0.2": version: 7.0.2 resolution: "cacheable-request@npm:7.0.2" @@ -21533,6 +23103,20 @@ __metadata: languageName: node linkType: hard +"charenc@npm:0.0.2": + version: 0.0.2 + resolution: "charenc@npm:0.0.2" + checksum: 81dcadbe57e861d527faf6dd3855dc857395a1c4d6781f4847288ab23cffb7b3ee80d57c15bba7252ffe3e5e8019db767757ee7975663ad2ca0939bb8fcaf2e5 + languageName: node + linkType: hard + +"charset@npm:^1.0.0": + version: 1.0.1 + resolution: "charset@npm:1.0.1" + checksum: adf747e7bc76c0e47dbfbb555ef376f5adb2e67cad35718f19a2d02e27589345ccaf3c6170c185d2cc82298241f8478cdcdcab8be8ec429bf56807885f3c5300 + languageName: node + linkType: hard + "check-types@npm:^11.2.3": version: 11.2.3 resolution: "check-types@npm:11.2.3" @@ -21582,6 +23166,13 @@ __metadata: languageName: node linkType: hard +"ci-info@npm:^2.0.0": + version: 2.0.0 + resolution: "ci-info@npm:2.0.0" + checksum: 3b374666a85ea3ca43fa49aa3a048d21c9b475c96eb13c133505d2324e7ae5efd6a454f41efe46a152269e9b6a00c9edbe63ec7fa1921957165aae16625acd67 + languageName: node + linkType: hard + "ci-info@npm:^3.1.0, ci-info@npm:^3.2.0, ci-info@npm:^3.7.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" @@ -21654,6 +23245,26 @@ __metadata: languageName: node linkType: hard +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585 + languageName: node + linkType: hard + +"cli-color@npm:^2.0.1": + version: 2.0.3 + resolution: "cli-color@npm:2.0.3" + dependencies: + d: ^1.0.1 + es5-ext: ^0.10.61 + es6-iterator: ^2.0.3 + memoizee: ^0.4.15 + timers-ext: ^0.1.7 + checksum: b1c5f3d0ec29cbe22be7a01d90bd0cfa080ffed6f1c321ea20ae3f10c6041f0e411e28ee2b98025945bee3548931deed1ae849b53c21b523ba74efef855cd73d + languageName: node + linkType: hard + "cli-cursor@npm:^3.1.0": version: 3.1.0 resolution: "cli-cursor@npm:3.1.0" @@ -21679,6 +23290,18 @@ __metadata: languageName: node linkType: hard +"cli-sprintf-format@npm:^1.1.1": + version: 1.1.1 + resolution: "cli-sprintf-format@npm:1.1.1" + dependencies: + cli-color: ^2.0.1 + es5-ext: ^0.10.53 + sprintf-kit: ^2.0.1 + supports-color: ^6.1.0 + checksum: d02360b42197d5bb087085bea1b2dccbe117b3e8026d0953f7bc65a78e07208377dd7c684696bd239538c03a2ce46da4b81addc6860188d16b0ba32d753bbbdc + languageName: node + linkType: hard + "cli-table@npm:^0.3.1": version: 0.3.6 resolution: "cli-table@npm:0.3.6" @@ -21825,7 +23448,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1": +"clsx@npm:^1.0.2, clsx@npm:^1.0.4, clsx@npm:^1.1.1, clsx@npm:^1.2.1": version: 1.2.1 resolution: "clsx@npm:1.2.1" checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 @@ -21889,6 +23512,20 @@ __metadata: languageName: node linkType: hard +"codemirror-graphql@npm:^2.0.10": + version: 2.0.10 + resolution: "codemirror-graphql@npm:2.0.10" + dependencies: + "@types/codemirror": ^0.0.90 + graphql-language-service: 5.2.0 + peerDependencies: + "@codemirror/language": 6.0.0 + codemirror: ^5.65.3 + graphql: ^15.5.0 || ^16.0.0 + checksum: d14e680168f407fd3d6ab09d88ea33640dd39f84d9ceb1ce605d9687d459fd08787109800a9847bbce72122e3318e975b255f65ddb19725d7a12781f4bf26506 + languageName: node + linkType: hard + "codemirror@npm:^5.65.3": version: 5.65.3 resolution: "codemirror@npm:5.65.3" @@ -22083,7 +23720,14 @@ __metadata: languageName: node linkType: hard -"commander@npm:*, commander@npm:11.0.0": +"commander@npm:*, commander@npm:^11.0.0": + version: 11.1.0 + resolution: "commander@npm:11.1.0" + checksum: fd1a8557c6b5b622c89ecdfde703242ab7db3b628ea5d1755784c79b8e7cb0d74d65b4a262289b533359cd58e1bfc0bf50245dfbcd2954682a6f367c828b79ef + languageName: node + linkType: hard + +"commander@npm:11.0.0": version: 11.0.0 resolution: "commander@npm:11.0.0" checksum: 6621954e1e1d078b4991c1f5bbd9439ad37aa7768d6ab4842de1dbd4d222c8a27e1b8e62108b3a92988614af45031d5bb2a2aaa92951f4d0c934d1a1ac564bb4 @@ -22160,7 +23804,7 @@ __metadata: languageName: node linkType: hard -"common-tags@npm:1.8.2": +"common-tags@npm:1.8.2, common-tags@npm:^1.8.0": version: 1.8.2 resolution: "common-tags@npm:1.8.2" checksum: 767a6255a84bbc47df49a60ab583053bb29a7d9687066a18500a516188a062c4e4cd52de341f22de0b07062e699b1b8fe3cfa1cb55b241cb9301aeb4f45b4dff @@ -22212,6 +23856,13 @@ __metadata: languageName: node linkType: hard +"component-type@npm:^1.2.1": + version: 1.2.1 + resolution: "component-type@npm:1.2.1" + checksum: 41a81f87425088c072dc99b7bd06d8c81057047a599955572bfa7f320e1f4d0b38422b2eee922e0ac6e4132376c572673dbf5eb02717898173ec11512bc06b34 + languageName: node + linkType: hard + "compress-commons@npm:^4.1.0": version: 4.1.0 resolution: "compress-commons@npm:4.1.0" @@ -22349,6 +24000,45 @@ __metadata: languageName: node linkType: hard +"conf@npm:^10.2.0": + version: 10.2.0 + resolution: "conf@npm:10.2.0" + dependencies: + ajv: ^8.6.3 + ajv-formats: ^2.1.1 + atomically: ^1.7.0 + debounce-fn: ^4.0.0 + dot-prop: ^6.0.1 + env-paths: ^2.2.1 + json-schema-typed: ^7.0.3 + onetime: ^5.1.2 + pkg-up: ^3.1.0 + semver: ^7.3.5 + checksum: 27066f38a25411c1e72e81a5219e2c7ed675cd39d8aa2a2f1797bb2c9255725e92e335d639334177a23d488b22b1290bbe0708e9a005574e5d83d5432df72bd3 + languageName: node + linkType: hard + +"configstore@npm:^5.0.1": + version: 5.0.1 + resolution: "configstore@npm:5.0.1" + dependencies: + dot-prop: ^5.2.0 + graceful-fs: ^4.1.2 + make-dir: ^3.0.0 + unique-string: ^2.0.0 + write-file-atomic: ^3.0.0 + xdg-basedir: ^4.0.0 + checksum: 60ef65d493b63f96e14b11ba7ec072fdbf3d40110a94fb7199d1c287761bdea5c5244e76b2596325f30c1b652213aa75de96ea20afd4a5f82065e61ea090988e + languageName: node + linkType: hard + +"connect-history-api-fallback@npm:^1.6.0": + version: 1.6.0 + resolution: "connect-history-api-fallback@npm:1.6.0" + checksum: 804ca2be28c999032ecd37a9f71405e5d7b7a4b3defcebbe41077bb8c5a0a150d7b59f51dcc33b2de30bc7e217a31d10f8cfad27e8e74c2fc7655eeba82d6e7e + languageName: node + linkType: hard + "connect-history-api-fallback@npm:^2.0.0": version: 2.0.0 resolution: "connect-history-api-fallback@npm:2.0.0" @@ -22366,7 +24056,19 @@ __metadata: languageName: node linkType: hard -"consola@npm:^2.15.0": +"connect@npm:^3.7.0": + version: 3.7.0 + resolution: "connect@npm:3.7.0" + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: ~1.3.3 + utils-merge: 1.0.1 + checksum: 96e1c4effcf219b065c7823e57351c94366d2e2a6952fa95e8212bffb35c86f1d5a3f9f6c5796d4cd3a5fdda628368b1c3cc44bf19c66cfd68fe9f9cab9177e2 + languageName: node + linkType: hard + +"consola@npm:^2.15.0, consola@npm:^2.15.3": version: 2.15.3 resolution: "consola@npm:2.15.3" checksum: 8ef7a09b703ec67ac5c389a372a33b6dc97eda6c9876443a60d76a3076eea0259e7f67a4e54fd5a52f97df73690822d090cf8b7e102b5761348afef7c6d03e28 @@ -22448,7 +24150,7 @@ __metadata: languageName: node linkType: hard -"convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.6.0, convert-source-map@npm:^1.7.0": +"convert-source-map@npm:^1.5.0, convert-source-map@npm:^1.6.0": version: 1.9.0 resolution: "convert-source-map@npm:1.9.0" checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 @@ -22493,7 +24195,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0, cookie@npm:~0.5.0": +"cookie@npm:0.5.0, cookie@npm:^0.5.0, cookie@npm:~0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 @@ -22560,7 +24262,14 @@ __metadata: languageName: node linkType: hard -"cors@npm:^2.8.5": +"cors-gate@npm:^1.1.3": + version: 1.1.3 + resolution: "cors-gate@npm:1.1.3" + checksum: 8480e24ccc77a0a150c3cb555ae07fc4e2fa0034a2585c0c91efa3c44b91936d31abf1c5a87b09726253b491e0b66ed491face942502bbc38f87bb309f931fc6 + languageName: node + linkType: hard + +"cors@npm:^2.8.4, cors@npm:^2.8.5": version: 2.8.5 resolution: "cors@npm:2.8.5" dependencies: @@ -22677,7 +24386,24 @@ __metadata: languageName: node linkType: hard -"create-require@npm:^1.1.0": +"create-jest@npm:^29.7.0": + version: 29.7.0 + resolution: "create-jest@npm:29.7.0" + dependencies: + "@jest/types": ^29.6.3 + chalk: ^4.0.0 + exit: ^0.1.2 + graceful-fs: ^4.2.9 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + prompts: ^2.0.1 + bin: + create-jest: bin/create-jest.js + checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945 + languageName: node + linkType: hard + +"create-require@npm:^1.1.0, create-require@npm:^1.1.1": version: 1.1.1 resolution: "create-require@npm:1.1.1" checksum: a9a1503d4390d8b59ad86f4607de7870b39cad43d929813599a23714831e81c520bddf61bcdd1f8e30f05fd3a2b71ae8538e946eb2786dc65c2bbc520f692eff @@ -22742,6 +24468,19 @@ __metadata: languageName: node linkType: hard +"cross-spawn@npm:^6.0.0": + version: 6.0.5 + resolution: "cross-spawn@npm:6.0.5" + dependencies: + nice-try: ^1.0.4 + path-key: ^2.0.1 + semver: ^5.5.0 + shebang-command: ^1.2.0 + which: ^1.2.9 + checksum: f893bb0d96cd3d5751d04e67145bdddf25f99449531a72e82dcbbd42796bbc8268c1076c6b3ea51d4d455839902804b94bc45dfb37ecbb32ea8e54a6741c3ab9 + 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": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -22753,6 +24492,22 @@ __metadata: languageName: node linkType: hard +"crosspath@npm:^2.0.0": + version: 2.0.0 + resolution: "crosspath@npm:2.0.0" + dependencies: + "@types/node": ^17.0.36 + checksum: a209e02562504b91da143f1b3fe4c1f7e5dfc20c9c83b2538f802e243ad16fadc3d529aa4e5f47430f91f92f4c1ed0860206c9fcb4e0ad737723a44b32a7f61b + languageName: node + linkType: hard + +"crypt@npm:0.0.2": + version: 0.0.2 + resolution: "crypt@npm:0.0.2" + checksum: baf4c7bbe05df656ec230018af8cf7dbe8c14b36b98726939cef008d473f6fe7a4fad906cfea4062c93af516f1550a3f43ceb4d6615329612c6511378ed9fe34 + languageName: node + linkType: hard + "crypto-browserify@npm:^3.11.0": version: 3.12.0 resolution: "crypto-browserify@npm:3.12.0" @@ -22772,6 +24527,13 @@ __metadata: languageName: node linkType: hard +"crypto-random-string@npm:^2.0.0": + version: 2.0.0 + resolution: "crypto-random-string@npm:2.0.0" + checksum: 0283879f55e7c16fdceacc181f87a0a65c53bc16ffe1d58b9d19a6277adcd71900d02bb2c4843dd55e78c51e30e89b0fec618a7f170ebcc95b33182c28f05fd6 + languageName: node + linkType: hard + "css-box-model@npm:^1.2.0": version: 1.2.1 resolution: "css-box-model@npm:1.2.1" @@ -22838,16 +24600,16 @@ __metadata: languageName: node linkType: hard -"css-select@npm:^4.1.3": - version: 4.1.3 - resolution: "css-select@npm:4.1.3" +"css-select@npm:^4.1.3, css-select@npm:^4.2.1": + version: 4.3.0 + resolution: "css-select@npm:4.3.0" dependencies: boolbase: ^1.0.0 - css-what: ^5.0.0 - domhandler: ^4.2.0 - domutils: ^2.6.0 - nth-check: ^2.0.0 - checksum: 40928f1aa6c71faf36430e7f26bcbb8ab51d07b98b754caacb71906400a195df5e6c7020a94f2982f02e52027b9bd57c99419220cf7020968c3415f14e4be5f8 + css-what: ^6.0.1 + domhandler: ^4.3.1 + domutils: ^2.8.0 + nth-check: ^2.0.1 + checksum: d6202736839194dd7f910320032e7cfc40372f025e4bf21ca5bf6eb0a33264f322f50ba9c0adc35dadd342d3d6fae5ca244779a4873afbfa76561e343f2058e0 languageName: node linkType: hard @@ -22911,14 +24673,7 @@ __metadata: languageName: node linkType: hard -"css-what@npm:^5.0.0": - version: 5.1.0 - resolution: "css-what@npm:5.1.0" - checksum: 0b75d1bac95c885c168573c85744a6c6843d8c33345f54f717218b37ea6296b0e99bb12105930ea170fd4a921990392a7c790c16c585c1d8960c49e2b7ec39f7 - languageName: node - linkType: hard - -"css-what@npm:^6.1.0": +"css-what@npm:^6.0.1, css-what@npm:^6.1.0": version: 6.1.0 resolution: "css-what@npm:6.1.0" checksum: b975e547e1e90b79625918f84e67db5d33d896e6de846c9b584094e529f0c63e2ab85ee33b9daffd05bff3a146a1916bec664e18bb76dd5f66cbff9fc13b2bbe @@ -23259,6 +25014,16 @@ __metadata: languageName: node linkType: hard +"d@npm:1, d@npm:^1.0.1": + version: 1.0.1 + resolution: "d@npm:1.0.1" + dependencies: + es5-ext: ^0.10.50 + type: ^1.0.1 + checksum: 49ca0639c7b822db670de93d4fbce44b4aa072cd848c76292c9978a8cd0fff1028763020ff4b0f147bd77bfe29b4c7f82e0f71ade76b2a06100543cdfd948d19 + languageName: node + linkType: hard + "dagre@npm:^0.8.5": version: 0.8.5 resolution: "dagre@npm:0.8.5" @@ -23285,6 +25050,13 @@ __metadata: languageName: node linkType: hard +"data-uri-to-buffer@npm:^6.0.0": + version: 6.0.1 + resolution: "data-uri-to-buffer@npm:6.0.1" + checksum: 9140e68c585ae33d950f5943bd476751346c8b789ae80b01a578a33cb8f7f706d1ca7378aff2b1878b2a6d9a8c88c55cc286d88191c8b8ead8255c3c4d934530 + languageName: node + linkType: hard + "data-urls@npm:^2.0.0": version: 2.0.0 resolution: "data-urls@npm:2.0.0" @@ -23344,6 +25116,15 @@ __metadata: languageName: node linkType: hard +"debounce-fn@npm:^4.0.0": + version: 4.0.0 + resolution: "debounce-fn@npm:4.0.0" + dependencies: + mimic-fn: ^3.0.0 + checksum: 7bf8d142b46a88453bbd6eda083f303049b4c8554af5114bdadfc2da56031030664360e81211ae08b708775e6904db7e6d72a421c4ff473344f4521c2c5e4a22 + languageName: node + linkType: hard + "debounce@npm:^1.1.0, debounce@npm:^1.2.0": version: 1.2.1 resolution: "debounce@npm:1.2.1" @@ -23372,7 +25153,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.2.7": +"debug@npm:^3.1.1, debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" dependencies: @@ -23435,6 +25216,15 @@ __metadata: languageName: node linkType: hard +"decompress-response@npm:^3.3.0": + version: 3.3.0 + resolution: "decompress-response@npm:3.3.0" + dependencies: + mimic-response: ^1.0.0 + checksum: 952552ac3bd7de2fc18015086b09468645c9638d98a551305e485230ada278c039c91116e946d07894b39ee53c0f0d5b6473f25a224029344354513b412d7380 + languageName: node + linkType: hard + "decompress-response@npm:^4.2.0": version: 4.2.1 resolution: "decompress-response@npm:4.2.1" @@ -23453,10 +25243,15 @@ __metadata: languageName: node linkType: hard -"dedent@npm:^0.7.0": - version: 0.7.0 - resolution: "dedent@npm:0.7.0" - checksum: 87de191050d9a40dd70cad01159a0bcf05ecb59750951242070b6abf9569088684880d00ba92a955b4058804f16eeaf91d604f283929b4f614d181cd7ae633d2 +"dedent@npm:^1.0.0": + version: 1.5.1 + resolution: "dedent@npm:1.5.1" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: c3c300a14edf1bdf5a873f9e4b22e839d62490bc5c8d6169c1f15858a1a76733d06a9a56930e963d677a2ceeca4b6b0894cc5ea2f501aa382ca5b92af3413c2a languageName: node linkType: hard @@ -23531,6 +25326,13 @@ __metadata: languageName: node linkType: hard +"defer-to-connect@npm:^1.0.1": + version: 1.1.3 + resolution: "defer-to-connect@npm:1.1.3" + checksum: 9491b301dcfa04956f989481ba7a43c2231044206269eb4ab64a52d6639ee15b1252262a789eb4239fb46ab63e44d4e408641bae8e0793d640aee55398cb3930 + languageName: node + linkType: hard + "defer-to-connect@npm:^2.0.0": version: 2.0.0 resolution: "defer-to-connect@npm:2.0.0" @@ -23555,6 +25357,17 @@ __metadata: languageName: node linkType: hard +"degenerator@npm:^5.0.0": + version: 5.0.1 + resolution: "degenerator@npm:5.0.1" + dependencies: + ast-types: ^0.13.4 + escodegen: ^2.1.0 + esprima: ^4.0.1 + checksum: a64fa39cdf6c2edd75188157d32338ee9de7193d7dbb2aeb4acb1eb30fa4a15ed80ba8dae9bd4d7b085472cf174a5baf81adb761aaa8e326771392c922084152 + languageName: node + linkType: hard + "del@npm:^7.0.0": version: 7.1.0 resolution: "del@npm:7.1.0" @@ -23658,6 +25471,15 @@ __metadata: languageName: node linkType: hard +"destroyable-server@npm:^1.0.0": + version: 1.0.0 + resolution: "destroyable-server@npm:1.0.0" + dependencies: + "@types/node": "*" + checksum: ac81b26f616a9d0aaa9cb759fa5a5a186f887025362329f7ddc909f53090f4aea0d1b75c4dda23e210faee536e2d7352de017261e1625b7b18108f0e630efa1f + languageName: node + linkType: hard + "detect-indent@npm:^6.0.0": version: 6.1.0 resolution: "detect-indent@npm:6.1.0" @@ -23679,6 +25501,13 @@ __metadata: languageName: node linkType: hard +"detect-node-es@npm:^1.1.0": + version: 1.1.0 + resolution: "detect-node-es@npm:1.1.0" + checksum: e46307d7264644975b71c104b9f028ed1d3d34b83a15b8a22373640ce5ea630e5640b1078b8ea15f202b54641da71e4aa7597093bd4b91f113db520a26a37449 + languageName: node + linkType: hard + "detect-node@npm:^2.0.4": version: 2.0.4 resolution: "detect-node@npm:2.0.4" @@ -23901,6 +25730,13 @@ __metadata: languageName: node linkType: hard +"domain-browser@npm:^4.22.0": + version: 4.22.0 + resolution: "domain-browser@npm:4.22.0" + checksum: e7ce1c19073e17dec35cfde050a3ddaac437d3ba8b870adabf9d5682e665eab3084df05de432dedf25b34303f0a2c71ac30f1cdba61b1aea018047b10de3d988 + languageName: node + linkType: hard + "domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": version: 2.3.0 resolution: "domelementtype@npm:2.3.0" @@ -23926,12 +25762,12 @@ __metadata: languageName: node linkType: hard -"domhandler@npm:^4.0.0, domhandler@npm:^4.2.0": - version: 4.3.0 - resolution: "domhandler@npm:4.3.0" +"domhandler@npm:^4.0.0, domhandler@npm:^4.2.0, domhandler@npm:^4.3.1": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" dependencies: domelementtype: ^2.2.0 - checksum: d2a2dbf40dd99abf936b65ad83c6b530afdb3605a87cad37a11b5d9220e68423ebef1b86c89e0f6d93ffaf315cc327cf1a988652e7a9a95cce539e3984f4c64d + checksum: 4c665ceed016e1911bf7d1dadc09dc888090b64dee7851cccd2fcf5442747ec39c647bb1cb8c8919f8bbdd0f0c625a6bafeeed4b2d656bbecdbae893f43ffaaa languageName: node linkType: hard @@ -23958,7 +25794,7 @@ __metadata: languageName: node linkType: hard -"domutils@npm:^2.5.2, domutils@npm:^2.6.0": +"domutils@npm:^2.5.2, domutils@npm:^2.8.0": version: 2.8.0 resolution: "domutils@npm:2.8.0" dependencies: @@ -23990,7 +25826,7 @@ __metadata: languageName: node linkType: hard -"dot-prop@npm:^5.1.0": +"dot-prop@npm:^5.1.0, dot-prop@npm:^5.2.0": version: 5.3.0 resolution: "dot-prop@npm:5.3.0" dependencies: @@ -23999,10 +25835,26 @@ __metadata: languageName: node linkType: hard -"dotenv@npm:^16.0.0": - version: 16.0.0 - resolution: "dotenv@npm:16.0.0" - checksum: 664cebb51f0a9a1d1b930f51f0271e72e26d62feaecc9dc03df39453dd494b4e724809ca480fb3ec3213382b1ed3f791aaeb83569a137f9329ce58efd4853dbf +"dot-prop@npm:^6.0.1": + version: 6.0.1 + resolution: "dot-prop@npm:6.0.1" + dependencies: + is-obj: ^2.0.0 + checksum: 0f47600a4b93e1dc37261da4e6909652c008832a5d3684b5bf9a9a0d3f4c67ea949a86dceed9b72f5733ed8e8e6383cc5958df3bbd0799ee317fd181f2ece700 + languageName: node + linkType: hard + +"dotenv-expand@npm:^8.0.2": + version: 8.0.3 + resolution: "dotenv-expand@npm:8.0.3" + checksum: 128ce90ac825b543de3ece0154a51b056ab0dc36bb26d97a68cd0b8707327ecd3c182fb6ac63b26a0fcdfa85064419906a1065cb634f1f9dc08ad311375f1fc0 + languageName: node + linkType: hard + +"dotenv@npm:^16.0.0, dotenv@npm:^16.0.3": + version: 16.3.1 + resolution: "dotenv@npm:16.3.1" + checksum: 15d75e7279018f4bafd0ee9706593dd14455ddb71b3bcba9c52574460b7ccaf67d5cf8b2c08a5af1a9da6db36c956a04a1192b101ee102a3e0cf8817bbcf3dfd languageName: node linkType: hard @@ -24020,6 +25872,13 @@ __metadata: languageName: node linkType: hard +"duplexer3@npm:^0.1.4": + version: 0.1.5 + resolution: "duplexer3@npm:0.1.5" + checksum: e677cb4c48f031ca728601d6a20bf6aed4c629d69ef9643cb89c67583d673c4ec9317cc6427501f38bd8c368d3a18f173987cc02bd99d8cf8fe3d94259a22a20 + languageName: node + linkType: hard + "duplexer@npm:^0.1.2": version: 0.1.2 resolution: "duplexer@npm:0.1.2" @@ -24027,6 +25886,18 @@ __metadata: languageName: node linkType: hard +"duplexify@npm:^3.5.1": + version: 3.7.1 + resolution: "duplexify@npm:3.7.1" + dependencies: + end-of-stream: ^1.0.0 + inherits: ^2.0.1 + readable-stream: ^2.0.0 + stream-shift: ^1.0.0 + checksum: 3c2ed2223d956a5da713dae12ba8295acb61d9acd966ccbba938090d04f4574ca4dca75cca089b5077c2d7e66101f32e6ea9b36a78ca213eff574e7a8b8accf2 + languageName: node + linkType: hard + "duplexify@npm:^4.0.0": version: 4.1.1 resolution: "duplexify@npm:4.1.1" @@ -24039,6 +25910,16 @@ __metadata: languageName: node linkType: hard +"duration@npm:^0.2.2": + version: 0.2.2 + resolution: "duration@npm:0.2.2" + dependencies: + d: 1 + es5-ext: ~0.10.46 + checksum: 907f4fdb2d5304744b419466846b41076bb0b2f5cde4ca02e78dd8d679b3ae14c29350d3f3a852006f5b6df0c6848efb7b38a6e4ae1b5dbadab5c46a2af22f91 + languageName: node + linkType: hard + "e2e-test@workspace:*, e2e-test@workspace:packages/e2e-test": version: 0.0.0-use.local resolution: "e2e-test@workspace:packages/e2e-test" @@ -24120,13 +26001,13 @@ __metadata: linkType: hard "ejs@npm:^3.1.6": - version: 3.1.7 - resolution: "ejs@npm:3.1.7" + version: 3.1.9 + resolution: "ejs@npm:3.1.9" dependencies: jake: ^10.8.5 bin: ejs: bin/cli.js - checksum: fe40764af39955ce8f8b116716fc8b911959946698edb49ecab85df597746c07aa65d5b74ead28a1e2ffa75b0f92d9bedd752f1c29437da6137b3518271e988c + checksum: af6f10eb815885ff8a8cfacc42c6b6cf87daf97a4884f87a30e0c3271fedd85d76a3a297d9c33a70e735b97ee632887f85e32854b9cdd3a2d97edf931519a35f languageName: node linkType: hard @@ -24219,7 +26100,7 @@ __metadata: languageName: node linkType: hard -"end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": +"end-of-stream@npm:^1.0.0, end-of-stream@npm:^1.1.0, end-of-stream@npm:^1.4.1": version: 1.4.4 resolution: "end-of-stream@npm:1.4.4" dependencies: @@ -24282,10 +26163,10 @@ __metadata: languageName: node linkType: hard -"env-paths@npm:^2.2.0": - version: 2.2.0 - resolution: "env-paths@npm:2.2.0" - checksum: ba2aea38301aafd69086be1f8cb453b92946e4840cb0de9d1c88a67e6f43a6174dcddb60b218ec36db8720b12de46b0d93c2f97ad9bbec6a267b479ab37debb6 +"env-paths@npm:^2.2.0, env-paths@npm:^2.2.1": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 65b5df55a8bab92229ab2b40dad3b387fad24613263d103a97f91c9fe43ceb21965cd3392b1ccb5d77088021e525c4e0481adb309625d0cb94ade1d1fb8dc17e languageName: node linkType: hard @@ -24469,6 +26350,17 @@ __metadata: languageName: node linkType: hard +"es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.46, es5-ext@npm:^0.10.50, es5-ext@npm:^0.10.53, es5-ext@npm:^0.10.61, es5-ext@npm:~0.10.14, es5-ext@npm:~0.10.2, es5-ext@npm:~0.10.46": + version: 0.10.62 + resolution: "es5-ext@npm:0.10.62" + dependencies: + es6-iterator: ^2.0.3 + es6-symbol: ^3.1.3 + next-tick: ^1.1.0 + checksum: 25f42f6068cfc6e393cf670bc5bba249132c5f5ec2dd0ed6e200e6274aca2fed8e9aec8a31c76031744c78ca283c57f0b41c7e737804c6328c7b8d3fbcba7983 + languageName: node + linkType: hard + "es6-error@npm:^4.1.1": version: 4.1.1 resolution: "es6-error@npm:4.1.1" @@ -24476,6 +26368,17 @@ __metadata: languageName: node linkType: hard +"es6-iterator@npm:^2.0.3": + version: 2.0.3 + resolution: "es6-iterator@npm:2.0.3" + dependencies: + d: 1 + es5-ext: ^0.10.35 + es6-symbol: ^3.1.1 + checksum: 6e48b1c2d962c21dee604b3d9f0bc3889f11ed5a8b33689155a2065d20e3107e2a69cc63a71bd125aeee3a589182f8bbcb5c8a05b6a8f38fa4205671b6d09697 + languageName: node + linkType: hard + "es6-object-assign@npm:^1.1.0": version: 1.1.0 resolution: "es6-object-assign@npm:1.1.0" @@ -24483,6 +26386,28 @@ __metadata: languageName: node linkType: hard +"es6-symbol@npm:^3.1.1, es6-symbol@npm:^3.1.3": + version: 3.1.3 + resolution: "es6-symbol@npm:3.1.3" + dependencies: + d: ^1.0.1 + ext: ^1.1.2 + checksum: cd49722c2a70f011eb02143ef1c8c70658d2660dead6641e160b94619f408b9cf66425515787ffe338affdf0285ad54f4eae30ea5bd510e33f8659ec53bcaa70 + languageName: node + linkType: hard + +"es6-weak-map@npm:^2.0.3": + version: 2.0.3 + resolution: "es6-weak-map@npm:2.0.3" + dependencies: + d: 1 + es5-ext: ^0.10.46 + es6-iterator: ^2.0.3 + es6-symbol: ^3.1.1 + checksum: 19ca15f46d50948ce78c2da5f21fb5b1ef45addd4fe17b5df952ff1f2a3d6ce4781249bc73b90995257264be2a98b2ec749bb2aba0c14b5776a1154178f9c927 + languageName: node + linkType: hard + "esbuild-loader@npm:^2.18.0": version: 2.21.0 resolution: "esbuild-loader@npm:2.21.0" @@ -24576,84 +26501,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.19.0": - version: 0.19.4 - resolution: "esbuild@npm:0.19.4" - dependencies: - "@esbuild/android-arm": 0.19.4 - "@esbuild/android-arm64": 0.19.4 - "@esbuild/android-x64": 0.19.4 - "@esbuild/darwin-arm64": 0.19.4 - "@esbuild/darwin-x64": 0.19.4 - "@esbuild/freebsd-arm64": 0.19.4 - "@esbuild/freebsd-x64": 0.19.4 - "@esbuild/linux-arm": 0.19.4 - "@esbuild/linux-arm64": 0.19.4 - "@esbuild/linux-ia32": 0.19.4 - "@esbuild/linux-loong64": 0.19.4 - "@esbuild/linux-mips64el": 0.19.4 - "@esbuild/linux-ppc64": 0.19.4 - "@esbuild/linux-riscv64": 0.19.4 - "@esbuild/linux-s390x": 0.19.4 - "@esbuild/linux-x64": 0.19.4 - "@esbuild/netbsd-x64": 0.19.4 - "@esbuild/openbsd-x64": 0.19.4 - "@esbuild/sunos-x64": 0.19.4 - "@esbuild/win32-arm64": 0.19.4 - "@esbuild/win32-ia32": 0.19.4 - "@esbuild/win32-x64": 0.19.4 - dependenciesMeta: - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 024309a16365b020815a30e9a3a9354894a391cf1adbfad7f44a975cf161ab5e961619b30e1ec8ea02994631d71e6b38831119be69f8ccb610c32bbe21addc79 - languageName: node - linkType: hard - -"esbuild@npm:~0.18.20": +"esbuild@npm:^0.18.10, esbuild@npm:~0.18.20": version: 0.18.20 resolution: "esbuild@npm:0.18.20" dependencies: @@ -24730,6 +26578,83 @@ __metadata: languageName: node linkType: hard +"esbuild@npm:^0.19.0": + version: 0.19.5 + resolution: "esbuild@npm:0.19.5" + dependencies: + "@esbuild/android-arm": 0.19.5 + "@esbuild/android-arm64": 0.19.5 + "@esbuild/android-x64": 0.19.5 + "@esbuild/darwin-arm64": 0.19.5 + "@esbuild/darwin-x64": 0.19.5 + "@esbuild/freebsd-arm64": 0.19.5 + "@esbuild/freebsd-x64": 0.19.5 + "@esbuild/linux-arm": 0.19.5 + "@esbuild/linux-arm64": 0.19.5 + "@esbuild/linux-ia32": 0.19.5 + "@esbuild/linux-loong64": 0.19.5 + "@esbuild/linux-mips64el": 0.19.5 + "@esbuild/linux-ppc64": 0.19.5 + "@esbuild/linux-riscv64": 0.19.5 + "@esbuild/linux-s390x": 0.19.5 + "@esbuild/linux-x64": 0.19.5 + "@esbuild/netbsd-x64": 0.19.5 + "@esbuild/openbsd-x64": 0.19.5 + "@esbuild/sunos-x64": 0.19.5 + "@esbuild/win32-arm64": 0.19.5 + "@esbuild/win32-ia32": 0.19.5 + "@esbuild/win32-x64": 0.19.5 + dependenciesMeta: + "@esbuild/android-arm": + optional: true + "@esbuild/android-arm64": + optional: true + "@esbuild/android-x64": + optional: true + "@esbuild/darwin-arm64": + optional: true + "@esbuild/darwin-x64": + optional: true + "@esbuild/freebsd-arm64": + optional: true + "@esbuild/freebsd-x64": + optional: true + "@esbuild/linux-arm": + optional: true + "@esbuild/linux-arm64": + optional: true + "@esbuild/linux-ia32": + optional: true + "@esbuild/linux-loong64": + optional: true + "@esbuild/linux-mips64el": + optional: true + "@esbuild/linux-ppc64": + optional: true + "@esbuild/linux-riscv64": + optional: true + "@esbuild/linux-s390x": + optional: true + "@esbuild/linux-x64": + optional: true + "@esbuild/netbsd-x64": + optional: true + "@esbuild/openbsd-x64": + optional: true + "@esbuild/sunos-x64": + optional: true + "@esbuild/win32-arm64": + optional: true + "@esbuild/win32-ia32": + optional: true + "@esbuild/win32-x64": + optional: true + bin: + esbuild: bin/esbuild + checksum: 5a0227cf6ffffa3076714d88230af1dfdd2fc363d91bd712a81fb91230c315a395e2c9b7588eee62986aeebf4999804b9b1b59eeab8e2457184eb0056bfe20c8 + languageName: node + linkType: hard + "escalade@npm:^3.1.1": version: 3.1.1 resolution: "escalade@npm:3.1.1" @@ -24737,6 +26662,13 @@ __metadata: languageName: node linkType: hard +"escape-goat@npm:^2.0.0": + version: 2.1.1 + resolution: "escape-goat@npm:2.1.1" + checksum: ce05c70c20dd7007b60d2d644b625da5412325fdb57acf671ba06cb2ab3cd6789e2087026921a05b665b0a03fadee2955e7fc0b9a67da15a6551a980b260eba7 + languageName: node + linkType: hard + "escape-html@npm:^1.0.3, escape-html@npm:~1.0.3": version: 1.0.3 resolution: "escape-html@npm:1.0.3" @@ -24791,14 +26723,13 @@ __metadata: languageName: node linkType: hard -"escodegen@npm:^2.0.0": - version: 2.0.0 - resolution: "escodegen@npm:2.0.0" +"escodegen@npm:^2.0.0, escodegen@npm:^2.1.0": + version: 2.1.0 + resolution: "escodegen@npm:2.1.0" dependencies: esprima: ^4.0.1 estraverse: ^5.2.0 esutils: ^2.0.2 - optionator: ^0.8.1 source-map: ~0.6.1 dependenciesMeta: source-map: @@ -24806,7 +26737,7 @@ __metadata: bin: escodegen: bin/escodegen.js esgenerate: bin/esgenerate.js - checksum: 5aa6b2966fafe0545e4e77936300cc94ad57cfe4dc4ebff9950492eaba83eef634503f12d7e3cbd644ecc1bab388ad0e92b06fd32222c9281a75d1cf02ec6cef + checksum: 096696407e161305cd05aebb95134ad176708bc5cb13d0dcc89a5fcbb959b8ed757e7f2591a5f8036f8f4952d4a724de0df14cd419e29212729fa6df5ce16bf6 languageName: node linkType: hard @@ -25196,6 +27127,16 @@ __metadata: languageName: node linkType: hard +"event-emitter@npm:^0.3.5": + version: 0.3.5 + resolution: "event-emitter@npm:0.3.5" + dependencies: + d: 1 + es5-ext: ~0.10.14 + checksum: 27c1399557d9cd7e0aa0b366c37c38a4c17293e3a10258e8b692a847dd5ba9fb90429c3a5a1eeff96f31f6fa03ccbd31d8ad15e00540b22b22f01557be706030 + languageName: node + linkType: hard + "event-source-polyfill@npm:1.0.25": version: 1.0.25 resolution: "event-source-polyfill@npm:1.0.25" @@ -25475,6 +27416,7 @@ __metadata: "@types/react": "*" "@types/react-dom": "*" "@types/zen-observable": ^0.8.0 + "@vitejs/plugin-react": ^4.0.4 cross-env: ^7.0.0 history: ^5.0.0 react: ^18.0.2 @@ -25482,6 +27424,9 @@ __metadata: react-router: ^6.3.0 react-router-dom: ^6.3.0 react-use: ^17.2.4 + vite: ^4.4.9 + vite-plugin-html: ^3.2.0 + vite-plugin-node-polyfills: ^0.14.1 zen-observable: ^0.10.0 languageName: unknown linkType: soft @@ -25500,6 +27445,8 @@ __metadata: "@backstage/plugin-azure-devops-backend": "workspace:^" "@backstage/plugin-badges-backend": "workspace:^" "@backstage/plugin-catalog-backend": "workspace:^" + "@backstage/plugin-catalog-backend-module-backstage-openapi": "workspace:^" + "@backstage/plugin-catalog-backend-module-openapi": "workspace:^" "@backstage/plugin-catalog-backend-module-scaffolder-entity-model": "workspace:^" "@backstage/plugin-catalog-backend-module-unprocessed": "workspace:^" "@backstage/plugin-devtools-backend": "workspace:^" @@ -25626,6 +27573,21 @@ __metadata: languageName: node linkType: hard +"execa@npm:^1.0.0": + version: 1.0.0 + resolution: "execa@npm:1.0.0" + dependencies: + cross-spawn: ^6.0.0 + get-stream: ^4.0.0 + is-stream: ^1.1.0 + npm-run-path: ^2.0.0 + p-finally: ^1.0.0 + signal-exit: ^3.0.0 + strip-eof: ^1.0.0 + checksum: ddf1342c1c7d02dd93b41364cd847640f6163350d9439071abf70bf4ceb1b9b2b2e37f54babb1d8dc1df8e0d8def32d0e81e74a2e62c3e1d70c303eb4c306bc4 + languageName: node + linkType: hard + "execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1" @@ -25643,6 +27605,13 @@ __metadata: languageName: node linkType: hard +"exit-hook@npm:^2.2.1": + version: 2.2.1 + resolution: "exit-hook@npm:2.2.1" + checksum: 1aa8359b6c5590a012d6cadf9cd337d227291bfcaa8970dc585d73dffef0582af34ed8ac56f6164f8979979fb417cff1eb49f03cdfd782f9332a30c773f0ada0 + languageName: node + linkType: hard + "exit-on-epipe@npm:~1.0.1": version: 1.0.1 resolution: "exit-on-epipe@npm:1.0.1" @@ -25677,16 +27646,25 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0, expect@npm:^29.4.3": - version: 29.4.3 - resolution: "expect@npm:29.4.3" +"expect@npm:^29.0.0, expect@npm:^29.7.0": + version: 29.7.0 + resolution: "expect@npm:29.7.0" dependencies: - "@jest/expect-utils": ^29.4.3 - jest-get-type: ^29.4.3 - jest-matcher-utils: ^29.4.3 - jest-message-util: ^29.4.3 - jest-util: ^29.4.3 - checksum: ff9dd8c50c0c6fd4b2b00f6dbd7ab0e2063fe1953be81a8c10ae1c005c7f5667ba452918e2efb055504b72b701a4f82575a081a0a7158efb16d87991b0366feb + "@jest/expect-utils": ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 + checksum: 9257f10288e149b81254a0fda8ffe8d54a7061cd61d7515779998b012579d2b8c22354b0eb901daf0145f347403da582f75f359f4810c007182ad3fb318b5c0c + languageName: node + linkType: hard + +"expiry-map@npm:^2.0.0": + version: 2.0.0 + resolution: "expiry-map@npm:2.0.0" + dependencies: + map-age-cleaner: ^0.2.0 + checksum: 9be8662e1a5c1084fb6d0ddc5402658dd06101c330454062b2f5efbf1477259d272e54ec16663d7d12a93d08ed510535781c36acb214696c5bc3a690a02a7a9d languageName: node linkType: hard @@ -25765,7 +27743,7 @@ __metadata: languageName: node linkType: hard -"express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1, express@npm:^4.18.2": +"express@npm:^4.14.0, express@npm:^4.17.1, express@npm:^4.17.3, express@npm:^4.18.1, express@npm:^4.18.2": version: 4.18.2 resolution: "express@npm:4.18.2" dependencies: @@ -25804,6 +27782,15 @@ __metadata: languageName: node linkType: hard +"ext@npm:^1.1.2": + version: 1.7.0 + resolution: "ext@npm:1.7.0" + dependencies: + type: ^2.7.2 + checksum: ef481f9ef45434d8c867cfd09d0393b60945b7c8a1798bedc4514cb35aac342ccb8d8ecb66a513e6a2b4ec1e294a338e3124c49b29736f8e7c735721af352c31 + languageName: node + linkType: hard + "extend@npm:3.0.2, extend@npm:^3.0.0, extend@npm:^3.0.2, extend@npm:~3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" @@ -25898,7 +27885,7 @@ __metadata: languageName: node linkType: hard -"fast-json-patch@npm:^3.0.0-1, fast-json-patch@npm:^3.1.0": +"fast-json-patch@npm:^3.0.0-1, fast-json-patch@npm:^3.1.0, fast-json-patch@npm:^3.1.1": version: 3.1.1 resolution: "fast-json-patch@npm:3.1.1" checksum: c4525b61b2471df60d4b025b4118b036d99778a93431aa44d1084218182841d82ce93056f0f3bbd731a24e6a8e69820128adf1873eb2199a26c62ef58d137833 @@ -26131,6 +28118,13 @@ __metadata: languageName: node linkType: hard +"file-type@npm:3.9.0": + version: 3.9.0 + resolution: "file-type@npm:3.9.0" + checksum: 1db70b2485ac77c4edb4b8753c1874ee6194123533f43c2651820f96b518f505fa570b093fedd6672eb105ba9fb89c62f84b6492e46788e39c3447aed37afa2d + languageName: node + linkType: hard + "file-type@npm:^16.5.4": version: 16.5.4 resolution: "file-type@npm:16.5.4" @@ -26181,6 +28175,21 @@ __metadata: languageName: node linkType: hard +"finalhandler@npm:1.1.2": + version: 1.1.2 + resolution: "finalhandler@npm:1.1.2" + dependencies: + debug: 2.6.9 + encodeurl: ~1.0.2 + escape-html: ~1.0.3 + on-finished: ~2.3.0 + parseurl: ~1.3.3 + statuses: ~1.5.0 + unpipe: ~1.0.0 + checksum: 617880460c5138dd7ccfd555cb5dde4d8f170f4b31b8bd51e4b646bb2946c30f7db716428a1f2882d730d2b72afb47d1f67cc487b874cb15426f95753a88965e + languageName: node + linkType: hard + "finalhandler@npm:1.2.0": version: 1.2.0 resolution: "finalhandler@npm:1.2.0" @@ -26534,6 +28543,36 @@ __metadata: languageName: node linkType: hard +"framer-motion@npm:^6.5.1": + version: 6.5.1 + resolution: "framer-motion@npm:6.5.1" + dependencies: + "@emotion/is-prop-valid": ^0.8.2 + "@motionone/dom": 10.12.0 + framesync: 6.0.1 + hey-listen: ^1.0.8 + popmotion: 11.0.3 + style-value-types: 5.0.0 + tslib: ^2.1.0 + peerDependencies: + react: ">=16.8 || ^17.0.0 || ^18.0.0" + react-dom: ">=16.8 || ^17.0.0 || ^18.0.0" + dependenciesMeta: + "@emotion/is-prop-valid": + optional: true + checksum: 737959063137b4ccafe01e0ac0c9e5a9531bf3f729f62c34ca7a5d7955e6664f70affd22b044f7db51df41acb21d120a4f71a860e17a80c4db766ad66f2153a1 + languageName: node + linkType: hard + +"framesync@npm:6.0.1": + version: 6.0.1 + resolution: "framesync@npm:6.0.1" + dependencies: + tslib: ^2.1.0 + checksum: a23ebe8f7e20a32c0b99c2f8175b6f07af3ec6316aad52a2316316a6d011d717af8d2175dcc2827031c59fabb30232ed3e19a720a373caba7f070e1eae436325 + languageName: node + linkType: hard + "fresh@npm:0.5.2": version: 0.5.2 resolution: "fresh@npm:0.5.2" @@ -26585,6 +28624,17 @@ __metadata: languageName: node linkType: hard +"fs-extra@npm:^11.1.0": + version: 11.1.1 + resolution: "fs-extra@npm:11.1.1" + dependencies: + graceful-fs: ^4.2.0 + jsonfile: ^6.0.1 + universalify: ^2.0.0 + checksum: fb883c68245b2d777fbc1f2082c9efb084eaa2bbf9fddaa366130d196c03608eebef7fb490541276429ee1ca99f317e2d73e96f5ca0999eefedf5a624ae1edfd + languageName: node + linkType: hard + "fs-extra@npm:^7.0.1, fs-extra@npm:~7.0.1": version: 7.0.1 resolution: "fs-extra@npm:7.0.1" @@ -26651,7 +28701,7 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:2.3.2, fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": +"fsevents@npm:2.3.2": version: 2.3.2 resolution: "fsevents@npm:2.3.2" dependencies: @@ -26661,7 +28711,17 @@ __metadata: languageName: node linkType: hard -"fsevents@patch:fsevents@2.3.2#~builtin, fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin": +"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: latest + checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@2.3.2#~builtin": version: 2.3.2 resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" dependencies: @@ -26670,6 +28730,15 @@ __metadata: languageName: node linkType: hard +"fsevents@patch:fsevents@^2.3.2#~builtin, fsevents@patch:fsevents@~2.3.2#~builtin, fsevents@patch:fsevents@~2.3.3#~builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=18f3a7" + dependencies: + node-gyp: latest + conditions: os=darwin + languageName: node + linkType: hard + "function-bind@npm:^1.1.1": version: 1.1.1 resolution: "function-bind@npm:1.1.1" @@ -26813,6 +28882,13 @@ __metadata: languageName: node linkType: hard +"get-nonce@npm:^1.0.0": + version: 1.0.1 + resolution: "get-nonce@npm:1.0.1" + checksum: e2614e43b4694c78277bb61b0f04583d45786881289285c73770b07ded246a98be7e1f78b940c80cbe6f2b07f55f0b724e6db6fd6f1bcbd1e8bdac16521074ed + languageName: node + linkType: hard + "get-package-type@npm:^0.1.0": version: 0.1.0 resolution: "get-package-type@npm:0.1.0" @@ -26834,6 +28910,15 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:^4.0.0, get-stream@npm:^4.1.0": + version: 4.1.0 + resolution: "get-stream@npm:4.1.0" + dependencies: + pump: ^3.0.0 + checksum: 443e1914170c15bd52ff8ea6eff6dfc6d712b031303e36302d2778e3de2506af9ee964d6124010f7818736dcfde05c04ba7ca6cc26883106e084357a17ae7d73 + languageName: node + linkType: hard + "get-stream@npm:^5.1.0": version: 5.1.0 resolution: "get-stream@npm:5.1.0" @@ -26860,7 +28945,7 @@ __metadata: languageName: node linkType: hard -"get-tsconfig@npm:^4.4.0, get-tsconfig@npm:^4.7.0": +"get-tsconfig@npm:^4.7.2": version: 4.7.2 resolution: "get-tsconfig@npm:4.7.2" dependencies: @@ -26869,6 +28954,18 @@ __metadata: languageName: node linkType: hard +"get-uri@npm:^6.0.1": + version: 6.0.2 + resolution: "get-uri@npm:6.0.2" + dependencies: + basic-ftp: ^5.0.2 + data-uri-to-buffer: ^6.0.0 + debug: ^4.3.4 + fs-extra: ^8.1.0 + checksum: 762de3b0e3d4e7afc966e4ce93be587d70c270590da9b4c8fbff888362656c055838d926903d1774cbfeed4d392b4d6def4b2c06d48c050580070426a3a8629b + languageName: node + linkType: hard + "getopts@npm:2.3.0": version: 2.3.0 resolution: "getopts@npm:2.3.0" @@ -27006,6 +29103,15 @@ __metadata: languageName: node linkType: hard +"global-dirs@npm:^3.0.0": + version: 3.0.1 + resolution: "global-dirs@npm:3.0.1" + dependencies: + ini: 2.0.0 + checksum: 70147b80261601fd40ac02a104581432325c1c47329706acd773f3a6ce99bb36d1d996038c85ccacd482ad22258ec233c586b6a91535b1a116b89663d49d6438 + languageName: node + linkType: hard + "global-modules@npm:^2.0.0": version: 2.0.0 resolution: "global-modules@npm:2.0.0" @@ -27176,6 +29282,25 @@ __metadata: languageName: node linkType: hard +"got@npm:^9.6.0": + version: 9.6.0 + resolution: "got@npm:9.6.0" + dependencies: + "@sindresorhus/is": ^0.14.0 + "@szmarczak/http-timer": ^1.1.2 + cacheable-request: ^6.0.0 + decompress-response: ^3.3.0 + duplexer3: ^0.1.4 + get-stream: ^4.1.0 + lowercase-keys: ^1.0.1 + mimic-response: ^1.0.1 + p-cancelable: ^1.0.0 + to-readable-stream: ^1.0.0 + url-parse-lax: ^3.0.0 + checksum: 941807bd9704bacf5eb401f0cc1212ffa1f67c6642f2d028fd75900471c221b1da2b8527f4553d2558f3faeda62ea1cf31665f8b002c6137f5de8732f07370b0 + languageName: node + linkType: hard + "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.5, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.10, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.10 resolution: "graceful-fs@npm:4.2.10" @@ -27197,7 +29322,7 @@ __metadata: languageName: node linkType: hard -"graphiql@npm:^1.5.12, graphiql@npm:^1.8.8": +"graphiql@npm:^1.8.8": version: 1.11.5 resolution: "graphiql@npm:1.11.5" dependencies: @@ -27214,6 +29339,22 @@ __metadata: languageName: node linkType: hard +"graphiql@npm:^3.0.6": + version: 3.0.6 + resolution: "graphiql@npm:3.0.6" + dependencies: + "@graphiql/react": ^0.19.4 + "@graphiql/toolkit": ^0.9.1 + graphql-language-service: ^5.2.0 + markdown-it: ^12.2.0 + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: 34d35ca4b46ce1e0cef0362f15d091481d2275e4a5b537b5715cfbaee15b1e114c31f35a63558a6f74a754687fbf4bc977411818af34e2e471a96902370c40f2 + languageName: node + linkType: hard + "graphlib@npm:^2.1.8": version: 2.1.8 resolution: "graphlib@npm:2.1.8" @@ -27248,17 +29389,26 @@ __metadata: languageName: node linkType: hard -"graphql-language-service@npm:^5.0.6": - version: 5.0.6 - resolution: "graphql-language-service@npm:5.0.6" +"graphql-http@npm:^1.22.0": + version: 1.22.0 + resolution: "graphql-http@npm:1.22.0" + peerDependencies: + graphql: ">=0.11 <=16" + checksum: 5f7ad26a95bb362e0d75dfdf83e588c1e52631792fbeb4b08566c1d2ce46cb186a82219bba5653257a35643ebbfa6a00870f1dc0fb2c36f9aa1d125f44b1ffe5 + languageName: node + linkType: hard + +"graphql-language-service@npm:5.2.0, graphql-language-service@npm:^5.0.6, graphql-language-service@npm:^5.2.0": + version: 5.2.0 + resolution: "graphql-language-service@npm:5.2.0" dependencies: nullthrows: ^1.0.0 - vscode-languageserver-types: ^3.15.1 + vscode-languageserver-types: ^3.17.1 peerDependencies: graphql: ^15.5.0 || ^16.0.0 bin: graphql: dist/temp-bin.js - checksum: a7155ba934aa428278cce0f460fa3b8b12020a26a0355e60738974617a66d9b2f1bb7d41cbd72a1620a29e61f10ca438cbf72cbf2405b8f653edddfc69fec02a + checksum: b053c6b7158d0ee7a3e55391bfd8be956fc5380211ca586b3a252007845e119540fb40efcc438975eaebc5ef25f46973f7ff4d9543c66e14ebd992957e0299b7 languageName: node linkType: hard @@ -27290,6 +29440,17 @@ __metadata: languageName: node linkType: hard +"graphql-subscriptions@npm:^1.1.0": + version: 1.2.1 + resolution: "graphql-subscriptions@npm:1.2.1" + dependencies: + iterall: ^1.3.0 + peerDependencies: + graphql: ^0.10.5 || ^0.11.3 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 + checksum: 2b9533c6774e7be46acd6fbee528aab06429f15dc222eabd991e82c02bf74e390b638dffa1a3fd86c1e26212c40a42a0418d7f4a7c3a1edf0534978ef128e528 + languageName: node + linkType: hard + "graphql-tag@npm:^2.10.3, graphql-tag@npm:^2.11.0, graphql-tag@npm:^2.12.6": version: 2.12.6 resolution: "graphql-tag@npm:2.12.6" @@ -27349,6 +29510,13 @@ __metadata: languageName: node linkType: hard +"graphql@npm:^14.0.2 || ^15.5": + version: 15.8.0 + resolution: "graphql@npm:15.8.0" + checksum: 423325271db8858428641b9aca01699283d1fe5b40ef6d4ac622569ecca927019fce8196208b91dd1d8eb8114f00263fe661d241d0eb40c10e5bfd650f86ec5e + languageName: node + linkType: hard + "graphql@npm:^16.0.0, graphql@npm:^16.8.1": version: 16.8.1 resolution: "graphql@npm:16.8.1" @@ -27523,6 +29691,13 @@ __metadata: languageName: node linkType: hard +"has-yarn@npm:^2.1.0": + version: 2.1.0 + resolution: "has-yarn@npm:2.1.0" + checksum: 5eb1d0bb8518103d7da24532bdbc7124ffc6d367b5d3c10840b508116f2f1bcbcf10fd3ba843ff6e2e991bdf9969fd862d42b2ed58aade88343326c950b7e7f7 + languageName: node + linkType: hard + "has@npm:^1.0.3": version: 1.0.3 resolution: "has@npm:1.0.3" @@ -27626,6 +29801,13 @@ __metadata: languageName: node linkType: hard +"hey-listen@npm:^1.0.8": + version: 1.0.8 + resolution: "hey-listen@npm:1.0.8" + checksum: 6bad60b367688f5348e25e7ca3276a74b59ac5a09b0455e6ff8ab7d4a9e38cd2116c708a7dcd8a954d27253ce1d8717ec891d175723ea739885b828cf44e4072 + languageName: node + linkType: hard + "highlight.js@npm:^10.1.0, highlight.js@npm:^10.4.1, highlight.js@npm:^10.6.0, highlight.js@npm:^10.7.2, highlight.js@npm:~10.7.0": version: 10.7.3 resolution: "highlight.js@npm:10.7.3" @@ -27750,7 +29932,7 @@ __metadata: languageName: node linkType: hard -"html-minifier-terser@npm:^6.0.2": +"html-minifier-terser@npm:^6.0.2, html-minifier-terser@npm:^6.1.0": version: 6.1.0 resolution: "html-minifier-terser@npm:6.1.0" dependencies: @@ -27820,6 +30002,17 @@ __metadata: languageName: node linkType: hard +"http-encoding@npm:^1.5.1": + version: 1.5.1 + resolution: "http-encoding@npm:1.5.1" + dependencies: + brotli-wasm: ^1.1.0 + pify: ^5.0.0 + zstd-codec: ^0.1.4 + checksum: 534aa2facb0ae529fa88b9778867472247711626b90030fd4351572c6147fb5e895d9d2e305e7dc5cc993345f2fbdb17ca99345651bf76dbac39a07f552af2ac + 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" @@ -27874,6 +30067,16 @@ __metadata: languageName: node linkType: hard +"http-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "http-proxy-agent@npm:7.0.0" + dependencies: + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 48d4fac997917e15f45094852b63b62a46d0c8a4f0b9c6c23ca26d27b8df8d178bed88389e604745e748bd9a01f5023e25093722777f0593c3f052009ff438b6 + languageName: node + linkType: hard + "http-proxy-middleware@npm:^2.0.0, http-proxy-middleware@npm:^2.0.3, http-proxy-middleware@npm:^2.0.6": version: 2.0.6 resolution: "http-proxy-middleware@npm:2.0.6" @@ -27903,6 +30106,13 @@ __metadata: languageName: node linkType: hard +"http-reasons@npm:0.1.0": + version: 0.1.0 + resolution: "http-reasons@npm:0.1.0" + checksum: da232d6e958416593989e4078a0fdf0508de19e1efb88fc461e1e214f067c2a8827bb1bb906296b0d7c1108cb6aca8cda964c793d9130c4f1491c98369c7a29c + languageName: node + linkType: hard + "http-signature@npm:~1.2.0": version: 1.2.0 resolution: "http-signature@npm:1.2.0" @@ -27924,6 +30134,16 @@ __metadata: languageName: node linkType: hard +"http2-wrapper@npm:^2.2.0": + version: 2.2.0 + resolution: "http2-wrapper@npm:2.2.0" + dependencies: + quick-lru: ^5.1.1 + resolve-alpn: ^1.2.0 + checksum: 6fd20e5cb6a58151715b3581e06a62a47df943187d2d1f69e538a50cccb7175dd334ecfde7900a37d18f3e13a1a199518a2c211f39860e81e9a16210c199cfaa + languageName: node + linkType: hard + "https-browserify@npm:^1.0.0": version: 1.0.0 resolution: "https-browserify@npm:1.0.0" @@ -27941,6 +30161,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.2": + version: 7.0.2 + resolution: "https-proxy-agent@npm:7.0.2" + dependencies: + agent-base: ^7.0.2 + debug: 4 + checksum: 088969a0dd476ea7a0ed0a2cf1283013682b08f874c3bc6696c83fa061d2c157d29ef0ad3eb70a2046010bb7665573b2388d10fdcb3e410a66995e5248444292 + languageName: node + linkType: hard + "human-id@npm:^1.0.2": version: 1.0.2 resolution: "human-id@npm:1.0.2" @@ -28130,12 +30360,12 @@ __metadata: linkType: hard "import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1": - version: 3.2.1 - resolution: "import-fresh@npm:3.2.1" + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" dependencies: parent-module: ^1.0.0 resolve-from: ^4.0.0 - checksum: caef42418a087c3951fb676943a7f21ba8971aa07f9b622dff4af7edcef4160e1b172dccd85a88d7eb109cf41406a4592f70259e6b3b33aeafd042bb61f81d96 + checksum: 2cacfad06e652b1edc50be650f7ec3be08c5e5a6f6d12d035c440a42a8cc028e60a5b99ca08a77ab4d6b1346da7d971915828f33cdab730d3d42f08242d09baa languageName: node linkType: hard @@ -28155,6 +30385,13 @@ __metadata: languageName: node linkType: hard +"import-lazy@npm:^2.1.0": + version: 2.1.0 + resolution: "import-lazy@npm:2.1.0" + checksum: 05294f3b9dd4971d3a996f0d2f176410fb6745d491d6e73376429189f5c1c3d290548116b2960a7cf3e89c20cdf11431739d1d2d8c54b84061980795010e803a + languageName: node + linkType: hard + "import-lazy@npm:~4.0.0": version: 4.0.0 resolution: "import-lazy@npm:4.0.0" @@ -28242,6 +30479,13 @@ __metadata: languageName: node linkType: hard +"ini@npm:2.0.0": + version: 2.0.0 + resolution: "ini@npm:2.0.0" + checksum: e7aadc5fb2e4aefc666d74ee2160c073995a4061556b1b5b4241ecb19ad609243b9cceafe91bae49c219519394bbd31512516cb22a3b1ca6e66d869e0447e84e + languageName: node + linkType: hard + "ini@npm:^1.3.5, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" @@ -28379,10 +30623,17 @@ __metadata: languageName: node linkType: hard -"ip@npm:^1.1.5": - version: 1.1.5 - resolution: "ip@npm:1.1.5" - checksum: 30133981f082a060a32644f6a7746e9ba7ac9e2bc07ecc8bbdda3ee8ca9bec1190724c390e45a1ee7695e7edfd2a8f7dda2c104ec5f7ac5068c00648504c7e5a +"ip@npm:^1.1.8": + version: 1.1.8 + resolution: "ip@npm:1.1.8" + checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb + languageName: node + linkType: hard + +"ip@npm:^2.0.0": + version: 2.0.0 + resolution: "ip@npm:2.0.0" + checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349 languageName: node linkType: hard @@ -28417,6 +30668,15 @@ __metadata: languageName: node linkType: hard +"is-admin@npm:^3.0.0": + version: 3.0.0 + resolution: "is-admin@npm:3.0.0" + dependencies: + execa: ^1.0.0 + checksum: f0e14254ab5dba0ab0b2ede34de799868d3fc8368e8928aadcd624c1430e59c0e83a369f978d8691eb38e2c48b0dc22f24693c415dcd2c2251189ca8a19a94cf + languageName: node + linkType: hard + "is-alphabetical@npm:^1.0.0": version: 1.0.4 resolution: "is-alphabetical@npm:1.0.4" @@ -28530,6 +30790,13 @@ __metadata: languageName: node linkType: hard +"is-buffer@npm:~1.1.6": + version: 1.1.6 + resolution: "is-buffer@npm:1.1.6" + checksum: 4a186d995d8bbf9153b4bd9ff9fd04ae75068fe695d29025d25e592d9488911eeece84eefbd8fa41b8ddcc0711058a71d4c466dcf6f1f6e1d83830052d8ca707 + languageName: node + linkType: hard + "is-builtin-module@npm:^3.1.0": version: 3.1.0 resolution: "is-builtin-module@npm:3.1.0" @@ -28546,6 +30813,17 @@ __metadata: languageName: node linkType: hard +"is-ci@npm:^2.0.0": + version: 2.0.0 + resolution: "is-ci@npm:2.0.0" + dependencies: + ci-info: ^2.0.0 + bin: + is-ci: bin.js + checksum: 77b869057510f3efa439bbb36e9be429d53b3f51abd4776eeea79ab3b221337fe1753d1e50058a9e2c650d38246108beffb15ccfd443929d77748d8c0cc90144 + languageName: node + linkType: hard + "is-ci@npm:^3.0.1": version: 3.0.1 resolution: "is-ci@npm:3.0.1" @@ -28598,6 +30876,16 @@ __metadata: languageName: node linkType: hard +"is-elevated@npm:^3.0.0": + version: 3.0.0 + resolution: "is-elevated@npm:3.0.0" + dependencies: + is-admin: ^3.0.0 + is-root: ^2.1.0 + checksum: 3d15eb223a0bfb3f22ac53e980b2e85d27891bd9840e5da3e04b84fe58bc3f49bdda3577c96ff62dd78c9af4a53cd8d2e7a1ac024ce71bbbc2be4c3d2bb9166a + languageName: node + linkType: hard + "is-extglob@npm:^2.1.1": version: 2.1.1 resolution: "is-extglob@npm:2.1.1" @@ -28674,6 +30962,16 @@ __metadata: languageName: node linkType: hard +"is-installed-globally@npm:^0.4.0": + version: 0.4.0 + resolution: "is-installed-globally@npm:0.4.0" + dependencies: + global-dirs: ^3.0.0 + is-path-inside: ^3.0.2 + checksum: 3359840d5982d22e9b350034237b2cda2a12bac1b48a721912e1ab8e0631dd07d45a2797a120b7b87552759a65ba03e819f1bd63f2d7ab8657ec0b44ee0bf399 + languageName: node + linkType: hard + "is-interactive@npm:^1.0.0": version: 1.0.0 resolution: "is-interactive@npm:1.0.0" @@ -28735,6 +31033,13 @@ __metadata: languageName: node linkType: hard +"is-npm@npm:^5.0.0": + version: 5.0.0 + resolution: "is-npm@npm:5.0.0" + checksum: 9baff02b0c69a3d3c79b162cb2f9e67fb40ef6d172c16601b2e2471c21e9a4fa1fc9885a308d7bc6f3a3cd2a324c27fa0bf284c133c3349bb22571ab70d041cc + languageName: node + linkType: hard + "is-number-object@npm:^1.0.4": version: 1.0.7 resolution: "is-number-object@npm:1.0.7" @@ -28765,7 +31070,7 @@ __metadata: languageName: node linkType: hard -"is-path-inside@npm:^3.0.3": +"is-path-inside@npm:^3.0.2, is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 @@ -28830,7 +31135,7 @@ __metadata: languageName: node linkType: hard -"is-promise@npm:^2.1.0": +"is-promise@npm:^2.1.0, is-promise@npm:^2.2.2": version: 2.2.2 resolution: "is-promise@npm:2.2.2" checksum: 18bf7d1c59953e0ad82a1ed963fb3dc0d135c8f299a14f89a17af312fc918373136e56028e8831700e1933519630cc2fd4179a777030330fde20d34e96f40c78 @@ -28886,6 +31191,13 @@ __metadata: languageName: node linkType: hard +"is-retry-allowed@npm:^1.1.0": + version: 1.2.0 + resolution: "is-retry-allowed@npm:1.2.0" + checksum: 50d700a89ae31926b1c91b3eb0104dbceeac8790d8b80d02f5c76d9a75c2056f1bb24b5268a8a018dead606bddf116b2262e5ac07401eb8b8783b266ed22558d + languageName: node + linkType: hard + "is-root@npm:^2.1.0": version: 2.1.0 resolution: "is-root@npm:2.1.0" @@ -28934,7 +31246,7 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^1.0.1": +"is-stream@npm:^1.0.1, is-stream@npm:^1.1.0": version: 1.1.0 resolution: "is-stream@npm:1.1.0" checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae @@ -28991,7 +31303,7 @@ __metadata: languageName: node linkType: hard -"is-typedarray@npm:~1.0.0": +"is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 @@ -29023,6 +31335,13 @@ __metadata: languageName: node linkType: hard +"is-url@npm:^1.2.4": + version: 1.2.4 + resolution: "is-url@npm:1.2.4" + checksum: 100e74b3b1feab87a43ef7653736e88d997eb7bd32e71fd3ebc413e58c1cbe56269699c776aaea84244b0567f2a7d68dfaa512a062293ed2f9fdecb394148432 + languageName: node + linkType: hard + "is-utf8@npm:^0.2.0, is-utf8@npm:^0.2.1": version: 0.2.1 resolution: "is-utf8@npm:0.2.1" @@ -29072,6 +31391,13 @@ __metadata: languageName: node linkType: hard +"is-yarn-global@npm:^0.3.0": + version: 0.3.0 + resolution: "is-yarn-global@npm:0.3.0" + checksum: bca013d65fee2862024c9fbb3ba13720ffca2fe750095174c1c80922fdda16402b5c233f5ac9e265bc12ecb5446e7b7f519a32d9541788f01d4d44e24d2bf481 + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -29192,6 +31518,13 @@ __metadata: languageName: node linkType: hard +"isomorphic-timers-promises@npm:^1.0.1": + version: 1.0.1 + resolution: "isomorphic-timers-promises@npm:1.0.1" + checksum: 16ef59f0fbcceba1a037c74b5f7195d252ae058724ccd3e53b37ad034e8498f5532084e8ab18e7940ba3fa8fca2f21403d00eed15802ab1f7cab7c099cba62a8 + languageName: node + linkType: hard + "isomorphic-ws@npm:5.0.0, isomorphic-ws@npm:^5.0.0": version: 5.0.0 resolution: "isomorphic-ws@npm:5.0.0" @@ -29201,6 +31534,15 @@ __metadata: languageName: node linkType: hard +"isomorphic-ws@npm:^4.0.1": + version: 4.0.1 + resolution: "isomorphic-ws@npm:4.0.1" + peerDependencies: + ws: "*" + checksum: d7190eadefdc28bdb93d67b5f0c603385aaf87724fa2974abb382ac1ec9756ed2cfb27065cbe76122879c2d452e2982bc4314317f3d6c737ddda6c047328771a + languageName: node + linkType: hard + "isstream@npm:~0.1.2": version: 0.1.2 resolution: "isstream@npm:0.1.2" @@ -29215,7 +31557,7 @@ __metadata: languageName: node linkType: hard -"istanbul-lib-instrument@npm:^5.0.4, istanbul-lib-instrument@npm:^5.1.0": +"istanbul-lib-instrument@npm:^5.0.4": version: 5.1.0 resolution: "istanbul-lib-instrument@npm:5.1.0" dependencies: @@ -29228,6 +31570,19 @@ __metadata: languageName: node linkType: hard +"istanbul-lib-instrument@npm:^6.0.0": + version: 6.0.1 + resolution: "istanbul-lib-instrument@npm:6.0.1" + dependencies: + "@babel/core": ^7.12.3 + "@babel/parser": ^7.14.7 + "@istanbuljs/schema": ^0.1.2 + istanbul-lib-coverage: ^3.2.0 + semver: ^7.5.4 + checksum: fb23472e739cfc9b027cefcd7d551d5e7ca7ff2817ae5150fab99fe42786a7f7b56a29a2aa8309c37092e18297b8003f9c274f50ca4360949094d17fbac81472 + languageName: node + linkType: hard + "istanbul-lib-report@npm:^3.0.0": version: 3.0.0 resolution: "istanbul-lib-report@npm:3.0.0" @@ -29260,7 +31615,7 @@ __metadata: languageName: node linkType: hard -"iterall@npm:^1.2.1": +"iterall@npm:^1.2.1, iterall@npm:^1.3.0": version: 1.3.0 resolution: "iterall@npm:1.3.0" checksum: c78b99678f8c99be488cca7f33e4acca9b72c1326e050afbaf023f086e55619ee466af0464af94a0cb3f292e60cb5bac53a8fd86bd4249ecad26e09f17bb158b @@ -29323,58 +31678,59 @@ __metadata: languageName: node linkType: hard -"jest-changed-files@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-changed-files@npm:29.4.3" +"jest-changed-files@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-changed-files@npm:29.7.0" dependencies: execa: ^5.0.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - checksum: 9a70bd8e92b37e18ad26d8bea97c516f41119fb7046b4255a13c76d557b0e54fa0629726de5a093fadfd6a0a08ce45da65a57086664d505b8db4b3133133e141 + checksum: 963e203893c396c5dfc75e00a49426688efea7361b0f0e040035809cecd2d46b3c01c02be2d9e8d38b1138357d2de7719ea5b5be21f66c10f2e9685a5a73bb99 languageName: node linkType: hard -"jest-circus@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-circus@npm:29.4.3" +"jest-circus@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-circus@npm:29.7.0" dependencies: - "@jest/environment": ^29.4.3 - "@jest/expect": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/environment": ^29.7.0 + "@jest/expect": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 co: ^4.6.0 - dedent: ^0.7.0 + dedent: ^1.0.0 is-generator-fn: ^2.0.0 - jest-each: ^29.4.3 - jest-matcher-utils: ^29.4.3 - jest-message-util: ^29.4.3 - jest-runtime: ^29.4.3 - jest-snapshot: ^29.4.3 - jest-util: ^29.4.3 + jest-each: ^29.7.0 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-runtime: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 p-limit: ^3.1.0 - pretty-format: ^29.4.3 + pretty-format: ^29.7.0 + pure-rand: ^6.0.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 2739bef9c888743b49ff3fe303131381618e5d2f250f613a91240d9c86e19e6874fc904cbd8bcb02ec9ec59a84e5dae4ffec929f0c6171e87ddbc05508a137f4 + checksum: 349437148924a5a109c9b8aad6d393a9591b4dac1918fc97d81b7fc515bc905af9918495055071404af1fab4e48e4b04ac3593477b1d5dcf48c4e71b527c70a7 languageName: node linkType: hard -"jest-cli@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-cli@npm:29.4.3" +"jest-cli@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-cli@npm:29.7.0" dependencies: - "@jest/core": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/core": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 chalk: ^4.0.0 + create-jest: ^29.7.0 exit: ^0.1.2 - graceful-fs: ^4.2.9 import-local: ^3.0.2 - jest-config: ^29.4.3 - jest-util: ^29.4.3 - jest-validate: ^29.4.3 - prompts: ^2.0.1 + jest-config: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 yargs: ^17.3.1 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 @@ -29383,34 +31739,34 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: f4c9f6d76cde2c60a4169acbebb3f862728be03bcf3fe0077d2e55da7f9f3c3e9483cfa6e936832d35eabf96ee5ebf0300c4b0bd43cffff099801793466bfdd8 + checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36 languageName: node linkType: hard -"jest-config@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-config@npm:29.4.3" +"jest-config@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-config@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 - "@jest/test-sequencer": ^29.4.3 - "@jest/types": ^29.4.3 - babel-jest: ^29.4.3 + "@jest/test-sequencer": ^29.7.0 + "@jest/types": ^29.6.3 + babel-jest: ^29.7.0 chalk: ^4.0.0 ci-info: ^3.2.0 deepmerge: ^4.2.2 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-circus: ^29.4.3 - jest-environment-node: ^29.4.3 - jest-get-type: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.4.3 - jest-runner: ^29.4.3 - jest-util: ^29.4.3 - jest-validate: ^29.4.3 + jest-circus: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-get-type: ^29.6.3 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-runner: ^29.7.0 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 micromatch: ^4.0.4 parse-json: ^5.2.0 - pretty-format: ^29.4.3 + pretty-format: ^29.7.0 slash: ^3.0.0 strip-json-comments: ^3.1.1 peerDependencies: @@ -29421,7 +31777,7 @@ __metadata: optional: true ts-node: optional: true - checksum: 92f9a9c6850b18682cb01892774a33967472af23a5844438d8c68077d5f2a29b15b665e4e4db7de3d74002a6dca158cd5b2cb9f5debfd2cce5e1aee6c74e3873 + checksum: 4cabf8f894c180cac80b7df1038912a3fc88f96f2622de33832f4b3314f83e22b08fb751da570c0ab2b7988f21604bdabade95e3c0c041068ac578c085cf7dff languageName: node linkType: hard @@ -29446,72 +31802,72 @@ __metadata: languageName: node linkType: hard -"jest-diff@npm:^29.2.0, jest-diff@npm:^29.4.3": - version: 29.6.3 - resolution: "jest-diff@npm:29.6.3" +"jest-diff@npm:^29.2.0, jest-diff@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" dependencies: chalk: ^4.0.0 diff-sequences: ^29.6.3 jest-get-type: ^29.6.3 - pretty-format: ^29.6.3 - checksum: 23b0a88efeab36566386f059f3da340754d2860969cbc34805154e2377714e37e3130e21a791fc68008fb460bbf5edd7ec43c16d96d15797b32ccfae5160fe37 + pretty-format: ^29.7.0 + checksum: 08e24a9dd43bfba1ef07a6374e5af138f53137b79ec3d5cc71a2303515335898888fa5409959172e1e05de966c9e714368d15e8994b0af7441f0721ee8e1bb77 languageName: node linkType: hard -"jest-docblock@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-docblock@npm:29.4.3" +"jest-docblock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-docblock@npm:29.7.0" dependencies: detect-newline: ^3.0.0 - checksum: e0e9df1485bb8926e5b33478cdf84b3387d9caf3658e7dc1eaa6dc34cb93dea0d2d74797f6e940f0233a88f3dadd60957f2288eb8f95506361f85b84bf8661df + checksum: 66390c3e9451f8d96c5da62f577a1dad701180cfa9b071c5025acab2f94d7a3efc2515cfa1654ebe707213241541ce9c5530232cdc8017c91ed64eea1bd3b192 languageName: node linkType: hard -"jest-each@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-each@npm:29.4.3" +"jest-each@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-each@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 chalk: ^4.0.0 - jest-get-type: ^29.4.3 - jest-util: ^29.4.3 - pretty-format: ^29.4.3 - checksum: 1f72738338399efab0139eaea18bc198be0c6ed889770c8cbfa70bf9c724e8171fe1d3a29a94f9f39b8493ee6b2529bb350fb7c7c75e0d7eddfd28c253c79f9d + jest-get-type: ^29.6.3 + jest-util: ^29.7.0 + pretty-format: ^29.7.0 + checksum: e88f99f0184000fc8813f2a0aa79e29deeb63700a3b9b7928b8a418d7d93cd24933608591dbbdea732b473eb2021c72991b5cc51a17966842841c6e28e6f691c languageName: node linkType: hard "jest-environment-jsdom@npm:^29.0.2": - version: 29.4.3 - resolution: "jest-environment-jsdom@npm:29.4.3" + version: 29.7.0 + resolution: "jest-environment-jsdom@npm:29.7.0" dependencies: - "@jest/environment": ^29.4.3 - "@jest/fake-timers": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/jsdom": ^20.0.0 "@types/node": "*" - jest-mock: ^29.4.3 - jest-util: ^29.4.3 + jest-mock: ^29.7.0 + jest-util: ^29.7.0 jsdom: ^20.0.0 peerDependencies: canvas: ^2.5.0 peerDependenciesMeta: canvas: optional: true - checksum: 3fb29bb4b472e05a38fdb235aa936ad469dfa2f6c1cab97fe3d1a7c585351976d05c7bbbd715b9747f070a225dcf10a9166df1461e0fb838ea7a377a8e64bed4 + checksum: 559aac134c196fccc1dfc794d8fc87377e9f78e894bb13012b0831d88dec0abd7ece99abec69da564b8073803be4f04a9eb4f4d1bb80e29eec0cb252c254deb8 languageName: node linkType: hard -"jest-environment-node@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-environment-node@npm:29.4.3" +"jest-environment-node@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-environment-node@npm:29.7.0" dependencies: - "@jest/environment": ^29.4.3 - "@jest/fake-timers": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-mock: ^29.4.3 - jest-util: ^29.4.3 - checksum: 3c7362edfdbd516e83af7367c95dde35761a482b174de9735c07633405486ec73e19624e9bea4333fca33c24e8d65eaa1aa6594e0cb6bfeeeb564ccc431ee61d + jest-mock: ^29.7.0 + jest-util: ^29.7.0 + checksum: 501a9966292cbe0ca3f40057a37587cb6def25e1e0c5e39ac6c650fe78d3c70a2428304341d084ac0cced5041483acef41c477abac47e9a290d5545fd2f15646 languageName: node linkType: hard @@ -29522,66 +31878,43 @@ __metadata: languageName: node linkType: hard -"jest-get-type@npm:^29.4.3, jest-get-type@npm:^29.6.3": +"jest-get-type@npm:^29.6.3": version: 29.6.3 resolution: "jest-get-type@npm:29.6.3" checksum: 88ac9102d4679d768accae29f1e75f592b760b44277df288ad76ce5bf038c3f5ce3719dea8aa0f035dac30e9eb034b848ce716b9183ad7cc222d029f03e92205 languageName: node linkType: hard -"jest-haste-map@npm:29.4.3": - version: 29.4.3 - resolution: "jest-haste-map@npm:29.4.3" +"jest-haste-map@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-haste-map@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/graceful-fs": ^4.1.3 "@types/node": "*" anymatch: ^3.0.3 fb-watchman: ^2.0.0 fsevents: ^2.3.2 graceful-fs: ^4.2.9 - jest-regex-util: ^29.4.3 - jest-util: ^29.4.3 - jest-worker: ^29.4.3 + jest-regex-util: ^29.6.3 + jest-util: ^29.7.0 + jest-worker: ^29.7.0 micromatch: ^4.0.4 walker: ^1.0.8 dependenciesMeta: fsevents: optional: true - checksum: c7a83ebe6008b3fe96a96235e8153092e54b14df68e0f4205faedec57450df26b658578495a71c6d82494c01fbb44bca98c1506a6b2b9c920696dcc5d2e2bc59 + checksum: c2c8f2d3e792a963940fbdfa563ce14ef9e14d4d86da645b96d3cd346b8d35c5ce0b992ee08593939b5f718cf0a1f5a90011a056548a1dbf58397d4356786f01 languageName: node linkType: hard -"jest-haste-map@patch:jest-haste-map@npm%3A29.4.3#./.yarn/patches/jest-haste-map-npm-29.4.3-19b03fcef3.patch::locator=root%40workspace%3A.": - version: 29.4.3 - resolution: "jest-haste-map@patch:jest-haste-map@npm%3A29.4.3#./.yarn/patches/jest-haste-map-npm-29.4.3-19b03fcef3.patch::version=29.4.3&hash=1e431f&locator=root%40workspace%3A." +"jest-leak-detector@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-leak-detector@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 - "@types/graceful-fs": ^4.1.3 - "@types/node": "*" - anymatch: ^3.0.3 - fb-watchman: ^2.0.0 - fsevents: ^2.3.2 - graceful-fs: ^4.2.9 - jest-regex-util: ^29.4.3 - jest-util: ^29.4.3 - jest-worker: ^29.4.3 - micromatch: ^4.0.4 - walker: ^1.0.8 - dependenciesMeta: - fsevents: - optional: true - checksum: a25fd71cc650acac4295172b2b84212872c4100a0b8e627e1f4b70a7e74d895a20274a4b0449aafdcb8273da055013fdc8765e55cbcf6dc46ae0351e974ed0c2 - languageName: node - linkType: hard - -"jest-leak-detector@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-leak-detector@npm:29.4.3" - dependencies: - jest-get-type: ^29.4.3 - pretty-format: ^29.4.3 - checksum: ec2b45e6f0abce81bd0dd0f6fd06b433c24d1ec865267af7640fae540ec868b93752598e407a9184d9c7419cbf32e8789007cc8c1be1a84f8f7321a0f8ad01f1 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: e3950e3ddd71e1d0c22924c51a300a1c2db6cf69ec1e51f95ccf424bcc070f78664813bef7aed4b16b96dfbdeea53fe358f8aeaaea84346ae15c3735758f1605 languageName: node linkType: hard @@ -29597,15 +31930,15 @@ __metadata: languageName: node linkType: hard -"jest-matcher-utils@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-matcher-utils@npm:29.4.3" +"jest-matcher-utils@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-matcher-utils@npm:29.7.0" dependencies: chalk: ^4.0.0 - jest-diff: ^29.4.3 - jest-get-type: ^29.4.3 - pretty-format: ^29.4.3 - checksum: 9e13cbe42d2113bab2691110c7c3ba5cec3b94abad2727e1de90929d0f67da444e9b2066da3b476b5bf788df53a8ede0e0a950cfb06a04e4d6d566d115ee4f1d + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + pretty-format: ^29.7.0 + checksum: d7259e5f995d915e8a37a8fd494cb7d6af24cd2a287b200f831717ba0d015190375f9f5dc35393b8ba2aae9b2ebd60984635269c7f8cff7d85b077543b7744cd languageName: node linkType: hard @@ -29626,31 +31959,31 @@ __metadata: languageName: node linkType: hard -"jest-message-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-message-util@npm:29.4.3" +"jest-message-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-message-util@npm:29.7.0" dependencies: "@babel/code-frame": ^7.12.13 - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/stack-utils": ^2.0.0 chalk: ^4.0.0 graceful-fs: ^4.2.9 micromatch: ^4.0.4 - pretty-format: ^29.4.3 + pretty-format: ^29.7.0 slash: ^3.0.0 stack-utils: ^2.0.3 - checksum: 64f06b9550021e68da0059020bea8691283cf818918810bb67192d7b7fb9b691c7eadf55c2ca3cd04df5394918f2327245077095cdc0d6b04be3532d2c7d0ced + checksum: a9d025b1c6726a2ff17d54cc694de088b0489456c69106be6b615db7a51b7beb66788bea7a59991a019d924fbf20f67d085a445aedb9a4d6760363f4d7d09930 languageName: node linkType: hard -"jest-mock@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-mock@npm:29.4.3" +"jest-mock@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-mock@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/node": "*" - jest-util: ^29.4.3 - checksum: 8eb4a29b02d2cd03faac0290b6df6d23b4ffa43f72b21c7fff3c7dd04a2797355b1e85862b70b15341dd33ee3a693b17db5520a6f6e6b81ee75601987de6a1a2 + jest-util: ^29.7.0 + checksum: 81ba9b68689a60be1482212878973700347cb72833c5e5af09895882b9eb5c4e02843a1bbdf23f94c52d42708bab53a30c45a3482952c9eec173d1eaac5b86c5 languageName: node linkType: hard @@ -29666,128 +31999,124 @@ __metadata: languageName: node linkType: hard -"jest-regex-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-regex-util@npm:29.4.3" - checksum: 96fc7fc28cd4dd73a63c13a526202c4bd8b351d4e5b68b1a2a2c88da3308c2a16e26feaa593083eb0bac38cca1aa9dd05025412e7de013ba963fb8e66af22b8a +"jest-regex-util@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-regex-util@npm:29.6.3" + checksum: 0518beeb9bf1228261695e54f0feaad3606df26a19764bc19541e0fc6e2a3737191904607fb72f3f2ce85d9c16b28df79b7b1ec9443aa08c3ef0e9efda6f8f2a languageName: node linkType: hard -"jest-resolve-dependencies@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-resolve-dependencies@npm:29.4.3" +"jest-resolve-dependencies@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve-dependencies@npm:29.7.0" dependencies: - jest-regex-util: ^29.4.3 - jest-snapshot: ^29.4.3 - checksum: 3ad934cd2170c9658d8800f84a975dafc866ec85b7ce391c640c09c3744ced337787620d8667dc8d1fa5e0b1493f973caa1a1bb980e4e6a50b46a1720baf0bd1 + jest-regex-util: ^29.6.3 + jest-snapshot: ^29.7.0 + checksum: aeb75d8150aaae60ca2bb345a0d198f23496494677cd6aefa26fc005faf354061f073982175daaf32b4b9d86b26ca928586344516e3e6969aa614cb13b883984 languageName: node linkType: hard -"jest-resolve@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-resolve@npm:29.4.3" +"jest-resolve@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-resolve@npm:29.7.0" dependencies: chalk: ^4.0.0 graceful-fs: ^4.2.9 - jest-haste-map: ^29.4.3 + jest-haste-map: ^29.7.0 jest-pnp-resolver: ^1.2.2 - jest-util: ^29.4.3 - jest-validate: ^29.4.3 + jest-util: ^29.7.0 + jest-validate: ^29.7.0 resolve: ^1.20.0 resolve.exports: ^2.0.0 slash: ^3.0.0 - checksum: 056a66beccf833f3c7e5a8fc9bfec218886e87b0b103decdbdf11893669539df489d1490cd6d5f0eea35731e8be0d2e955a6710498f970d2eae734da4df029dc + checksum: 0ca218e10731aa17920526ec39deaec59ab9b966237905ffc4545444481112cd422f01581230eceb7e82d86f44a543d520a71391ec66e1b4ef1a578bd5c73487 languageName: node linkType: hard -"jest-runner@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-runner@npm:29.4.3" +"jest-runner@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runner@npm:29.7.0" dependencies: - "@jest/console": ^29.4.3 - "@jest/environment": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/transform": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/console": ^29.7.0 + "@jest/environment": ^29.7.0 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 emittery: ^0.13.1 graceful-fs: ^4.2.9 - jest-docblock: ^29.4.3 - jest-environment-node: ^29.4.3 - jest-haste-map: ^29.4.3 - jest-leak-detector: ^29.4.3 - jest-message-util: ^29.4.3 - jest-resolve: ^29.4.3 - jest-runtime: ^29.4.3 - jest-util: ^29.4.3 - jest-watcher: ^29.4.3 - jest-worker: ^29.4.3 + jest-docblock: ^29.7.0 + jest-environment-node: ^29.7.0 + jest-haste-map: ^29.7.0 + jest-leak-detector: ^29.7.0 + jest-message-util: ^29.7.0 + jest-resolve: ^29.7.0 + jest-runtime: ^29.7.0 + jest-util: ^29.7.0 + jest-watcher: ^29.7.0 + jest-worker: ^29.7.0 p-limit: ^3.1.0 source-map-support: 0.5.13 - checksum: c41108e5da01e0b8fdc2a06c5042eb49bb1d8db0e0d4651769fd1b9fe84ab45188617c11a3a8e1c83748b29bfe57dd77001ec57e86e3e3c30f3534e0314f8882 + checksum: f0405778ea64812bf9b5c50b598850d94ccf95d7ba21f090c64827b41decd680ee19fcbb494007cdd7f5d0d8906bfc9eceddd8fa583e753e736ecd462d4682fb languageName: node linkType: hard -"jest-runtime@npm:^29.0.2, jest-runtime@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-runtime@npm:29.4.3" +"jest-runtime@npm:^29.0.2, jest-runtime@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-runtime@npm:29.7.0" dependencies: - "@jest/environment": ^29.4.3 - "@jest/fake-timers": ^29.4.3 - "@jest/globals": ^29.4.3 - "@jest/source-map": ^29.4.3 - "@jest/test-result": ^29.4.3 - "@jest/transform": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/environment": ^29.7.0 + "@jest/fake-timers": ^29.7.0 + "@jest/globals": ^29.7.0 + "@jest/source-map": ^29.6.3 + "@jest/test-result": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 cjs-module-lexer: ^1.0.0 collect-v8-coverage: ^1.0.0 glob: ^7.1.3 graceful-fs: ^4.2.9 - jest-haste-map: ^29.4.3 - jest-message-util: ^29.4.3 - jest-mock: ^29.4.3 - jest-regex-util: ^29.4.3 - jest-resolve: ^29.4.3 - jest-snapshot: ^29.4.3 - jest-util: ^29.4.3 + jest-haste-map: ^29.7.0 + jest-message-util: ^29.7.0 + jest-mock: ^29.7.0 + jest-regex-util: ^29.6.3 + jest-resolve: ^29.7.0 + jest-snapshot: ^29.7.0 + jest-util: ^29.7.0 slash: ^3.0.0 strip-bom: ^4.0.0 - checksum: b99f8a910d1a38e7476058ba04ad44dfd3d93e837bb7c301d691e646a1085412fde87f06fbe271c9145f0e72d89400bfa7f6994bc30d456c7742269f37d0f570 + checksum: d19f113d013e80691e07047f68e1e3448ef024ff2c6b586ce4f90cd7d4c62a2cd1d460110491019719f3c59bfebe16f0e201ed005ef9f80e2cf798c374eed54e languageName: node linkType: hard -"jest-snapshot@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-snapshot@npm:29.4.3" +"jest-snapshot@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-snapshot@npm:29.7.0" dependencies: "@babel/core": ^7.11.6 "@babel/generator": ^7.7.2 "@babel/plugin-syntax-jsx": ^7.7.2 "@babel/plugin-syntax-typescript": ^7.7.2 - "@babel/traverse": ^7.7.2 "@babel/types": ^7.3.3 - "@jest/expect-utils": ^29.4.3 - "@jest/transform": ^29.4.3 - "@jest/types": ^29.4.3 - "@types/babel__traverse": ^7.0.6 - "@types/prettier": ^2.1.5 + "@jest/expect-utils": ^29.7.0 + "@jest/transform": ^29.7.0 + "@jest/types": ^29.6.3 babel-preset-current-node-syntax: ^1.0.0 chalk: ^4.0.0 - expect: ^29.4.3 + expect: ^29.7.0 graceful-fs: ^4.2.9 - jest-diff: ^29.4.3 - jest-get-type: ^29.4.3 - jest-haste-map: ^29.4.3 - jest-matcher-utils: ^29.4.3 - jest-message-util: ^29.4.3 - jest-util: ^29.4.3 + jest-diff: ^29.7.0 + jest-get-type: ^29.6.3 + jest-matcher-utils: ^29.7.0 + jest-message-util: ^29.7.0 + jest-util: ^29.7.0 natural-compare: ^1.4.0 - pretty-format: ^29.4.3 - semver: ^7.3.5 - checksum: 79ba52f2435e23ce72b1309be4b17fdbcb299d1c2ce97ebb61df9a62711e9463035f63b4c849181b2fe5aa17b3e09d30ee4668cc25fb3c6f59511c010b4d9494 + pretty-format: ^29.7.0 + semver: ^7.5.3 + checksum: 86821c3ad0b6899521ce75ee1ae7b01b17e6dfeff9166f2cf17f012e0c5d8c798f30f9e4f8f7f5bed01ea7b55a6bc159f5eda778311162cbfa48785447c237ad languageName: node linkType: hard @@ -29805,47 +32134,47 @@ __metadata: languageName: node linkType: hard -"jest-util@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-util@npm:29.4.3" +"jest-util@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-util@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 "@types/node": "*" chalk: ^4.0.0 ci-info: ^3.2.0 graceful-fs: ^4.2.9 picomatch: ^2.2.3 - checksum: 606b3e6077895baf8fb4ad4d08c134f37a6b81d5ba77ae654c942b1ae4b7294ab3b5a0eb93db34f129407b367970cf3b76bf5c80897b30f215f2bc8bf20a5f3f + checksum: 042ab4980f4ccd4d50226e01e5c7376a8556b472442ca6091a8f102488c0f22e6e8b89ea874111d2328a2080083bf3225c86f3788c52af0bd0345a00eb57a3ca languageName: node linkType: hard -"jest-validate@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-validate@npm:29.4.3" +"jest-validate@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-validate@npm:29.7.0" dependencies: - "@jest/types": ^29.4.3 + "@jest/types": ^29.6.3 camelcase: ^6.2.0 chalk: ^4.0.0 - jest-get-type: ^29.4.3 + jest-get-type: ^29.6.3 leven: ^3.1.0 - pretty-format: ^29.4.3 - checksum: 983e56430d86bed238448cae031535c1d908f760aa312cd4a4ec0e92f3bc1b6675415ddf57cdeceedb8ad9c698e5bcd10f0a856dfc93a8923bdecc7733f4ba80 + pretty-format: ^29.7.0 + checksum: 191fcdc980f8a0de4dbdd879fa276435d00eb157a48683af7b3b1b98b0f7d9de7ffe12689b617779097ff1ed77601b9f7126b0871bba4f776e222c40f62e9dae languageName: node linkType: hard -"jest-watcher@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-watcher@npm:29.4.3" +"jest-watcher@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-watcher@npm:29.7.0" dependencies: - "@jest/test-result": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/test-result": ^29.7.0 + "@jest/types": ^29.6.3 "@types/node": "*" ansi-escapes: ^4.2.1 chalk: ^4.0.0 emittery: ^0.13.1 - jest-util: ^29.4.3 + jest-util: ^29.7.0 string-length: ^4.0.1 - checksum: 44b64991b3414db853c3756f14690028f4edef7aebfb204a4291cc1901c2239fa27a8687c5c5abbecc74bf613e0bb9b1378bf766430c9febcc71e9c0cb5ad8fc + checksum: 67e6e7fe695416deff96b93a14a561a6db69389a0667e9489f24485bb85e5b54e12f3b2ba511ec0b777eca1e727235b073e3ebcdd473d68888650489f88df92f languageName: node linkType: hard @@ -29890,26 +32219,26 @@ __metadata: languageName: node linkType: hard -"jest-worker@npm:^29.4.3": - version: 29.4.3 - resolution: "jest-worker@npm:29.4.3" +"jest-worker@npm:^29.7.0": + version: 29.7.0 + resolution: "jest-worker@npm:29.7.0" dependencies: "@types/node": "*" - jest-util: ^29.4.3 + jest-util: ^29.7.0 merge-stream: ^2.0.0 supports-color: ^8.0.0 - checksum: c99ae66f257564613e72c5797c3a68f21a22e1c1fb5f30d14695ff5b508a0d2405f22748f13a3df8d1015b5e16abb130170f81f047ff68f58b6b1d2ff6ebc51b + checksum: 30fff60af49675273644d408b650fc2eb4b5dcafc5a0a455f238322a8f9d8a98d847baca9d51ff197b6747f54c7901daa2287799230b856a0f48287d131f8c13 languageName: node linkType: hard "jest@npm:^29.0.2": - version: 29.4.3 - resolution: "jest@npm:29.4.3" + version: 29.7.0 + resolution: "jest@npm:29.7.0" dependencies: - "@jest/core": ^29.4.3 - "@jest/types": ^29.4.3 + "@jest/core": ^29.7.0 + "@jest/types": ^29.6.3 import-local: ^3.0.2 - jest-cli: ^29.4.3 + jest-cli: ^29.7.0 peerDependencies: node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: @@ -29917,7 +32246,7 @@ __metadata: optional: true bin: jest: bin/jest.js - checksum: 084d10d1ceaade3c40e6d3bbd71b9b71b8919ba6fbd6f1f6699bdc259a6ba2f7350c7ccbfa10c11f7e3e01662853650a6244210179542fe4ba87e77dc3f3109f + checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b languageName: node linkType: hard @@ -29953,6 +32282,13 @@ __metadata: languageName: node linkType: hard +"join-component@npm:^1.1.0": + version: 1.1.0 + resolution: "join-component@npm:1.1.0" + checksum: b904c2f98549e4195022caca3a7dc837f9706c670ff333f3d617f2aed23bce2841322a999734683b6ab8e202568ad810c11ff79b58a64df66888153f04750239 + languageName: node + linkType: hard + "jose@npm:^4.14.6, jose@npm:^4.15.1, jose@npm:^4.6.0": version: 4.15.3 resolution: "jose@npm:4.15.3" @@ -30247,6 +32583,13 @@ __metadata: languageName: node linkType: hard +"json-buffer@npm:3.0.0": + version: 3.0.0 + resolution: "json-buffer@npm:3.0.0" + checksum: 0cecacb8025370686a916069a2ff81f7d55167421b6aa7270ee74e244012650dd6bce22b0852202ea7ff8624fce50ff0ec1bdf95914ccb4553426e290d5a63fa + languageName: node + linkType: hard + "json-buffer@npm:3.0.1, json-buffer@npm:^3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -30341,6 +32684,13 @@ __metadata: languageName: node linkType: hard +"json-schema-typed@npm:^7.0.3": + version: 7.0.3 + resolution: "json-schema-typed@npm:7.0.3" + checksum: e861b19e97e3cc2b29a429147890157827eeda16ab639a0765b935cf3e22aeb6abbba108e23aef442da806bb1f402bdff21da9c5cb30015f8007594565e110b5 + languageName: node + linkType: hard + "json-schema@npm:0.4.0, json-schema@npm:^0.4.0": version: 0.4.0 resolution: "json-schema@npm:0.4.0" @@ -30816,6 +33166,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^3.0.0": + version: 3.1.0 + resolution: "keyv@npm:3.1.0" + dependencies: + json-buffer: 3.0.0 + checksum: bb7e8f3acffdbafbc2dd5b63f377fe6ec4c0e2c44fc82720449ef8ab54f4a7ce3802671ed94c0f475ae0a8549703353a2124561fcf3317010c141b32ca1ce903 + languageName: node + linkType: hard + "keyv@npm:^4.0.0, keyv@npm:^4.5.2": version: 4.5.4 resolution: "keyv@npm:4.5.4" @@ -30945,6 +33304,15 @@ __metadata: languageName: node linkType: hard +"latest-version@npm:^5.1.0": + version: 5.1.0 + resolution: "latest-version@npm:5.1.0" + dependencies: + package-json: ^6.3.0 + checksum: fbc72b071eb66c40f652441fd783a9cca62f08bf42433651937f078cd9ef94bf728ec7743992777826e4e89305aef24f234b515e6030503a2cbee7fc9bdc2c0f + languageName: node + linkType: hard + "launch-editor@npm:^2.6.0": version: 2.6.0 resolution: "launch-editor@npm:2.6.0" @@ -31156,6 +33524,13 @@ __metadata: languageName: node linkType: hard +"liquid-json@npm:0.3.1": + version: 0.3.1 + resolution: "liquid-json@npm:0.3.1" + checksum: b215fb17e7c9409e69a207a0ae275710311c274156862f34a78401dde363f97f15b863462fcc487f2fc3fb1d778d7d9f1d537bf00925a272b506a36e90826e2c + languageName: node + linkType: hard + "listr2@npm:6.6.1": version: 6.6.1 resolution: "listr2@npm:6.6.1" @@ -31286,6 +33661,13 @@ __metadata: languageName: node linkType: hard +"lodash.chunk@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.chunk@npm:4.2.0" + checksum: 6286c6d06814fbeda502164015c42ef53a9194e6ebaac52ec2b41e83344aefe7bc3d94fdfec525adcd2c66cefdf05dc333b6a1128e4de739797342315c17cbc7 + languageName: node + linkType: hard + "lodash.clonedeep@npm:^4.5.0": version: 4.5.0 resolution: "lodash.clonedeep@npm:4.5.0" @@ -31349,6 +33731,13 @@ __metadata: languageName: node linkType: hard +"lodash.groupby@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.groupby@npm:4.6.0" + checksum: e2d4d13d12790a1cacab3f5f120b7c072a792224e83b2f403218866d18efde76024b2579996dfebb230a61ce06469332e16639103669a35a605287e19ced6b9b + languageName: node + linkType: hard + "lodash.has@npm:^4.5.2": version: 4.5.2 resolution: "lodash.has@npm:4.5.2" @@ -31384,7 +33773,7 @@ __metadata: languageName: node linkType: hard -"lodash.isequal@npm:^4.5.0": +"lodash.isequal@npm:^4.0.0, lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 @@ -31447,6 +33836,13 @@ __metadata: languageName: node linkType: hard +"lodash.pick@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.pick@npm:4.4.0" + checksum: 2c36cab7da6b999a20bd3373b40e31a3ef81fa264f34a6979c852c5bc8ac039379686b27380f0cb8e3781610844fafec6949c6fbbebc059c98f8fa8570e3675f + languageName: node + linkType: hard + "lodash.sortby@npm:^4.7.0": version: 4.7.0 resolution: "lodash.sortby@npm:4.7.0" @@ -31489,13 +33885,31 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4.17.21, lodash@npm:^4.15.0, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15, lodash@npm:~4.17.21": +"lodash@npm:4.17.21, lodash@npm:^4.15.0, lodash@npm:^4.16.4, lodash@npm:^4.17.10, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.0, lodash@npm:~4.17.15, lodash@npm:~4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 languageName: node linkType: hard +"log-node@npm:^8.0.3": + version: 8.0.3 + resolution: "log-node@npm:8.0.3" + dependencies: + ansi-regex: ^5.0.1 + cli-color: ^2.0.1 + cli-sprintf-format: ^1.1.1 + d: ^1.0.1 + es5-ext: ^0.10.53 + sprintf-kit: ^2.0.1 + supports-color: ^8.1.1 + type: ^2.5.0 + peerDependencies: + log: ^6.0.0 + checksum: d6e634e22098a2453e84324e49cb7aeead7cb3b9e117ed8e5097384de6310b68c327e47a62e20c0c118877aad401d5eb1f14445f6c0b1793ef16221089fc8610 + languageName: node + linkType: hard + "log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" @@ -31531,6 +33945,21 @@ __metadata: languageName: node linkType: hard +"log@npm:^6.3.1": + version: 6.3.1 + resolution: "log@npm:6.3.1" + dependencies: + d: ^1.0.1 + duration: ^0.2.2 + es5-ext: ^0.10.53 + event-emitter: ^0.3.5 + sprintf-kit: ^2.0.1 + type: ^2.5.0 + uni-global: ^1.0.0 + checksum: 21800f4b55acb7878ec90fcf626d45002b22d91e74270357981305c7e0b7429599c62072b57e807fe73e07fc7229827e983f836f17401d661b22dcfc14519ea5 + languageName: node + linkType: hard + "logform@npm:^2.3.2, logform@npm:^2.4.0": version: 2.5.1 resolution: "logform@npm:2.5.1" @@ -31545,10 +33974,10 @@ __metadata: languageName: node linkType: hard -"loglevel@npm:^1.6.8": - version: 1.8.0 - resolution: "loglevel@npm:1.8.0" - checksum: 41aeea17de24aba8dba68084a31fe9189648bce4f39c1277e021bb276c3c53a75b0d337395919cf271068ad40ecefabad0e4fdeb4a8f11908beee532b898f4a7 +"loglevel@npm:^1.6.8, loglevel@npm:^1.8.0": + version: 1.8.1 + resolution: "loglevel@npm:1.8.1" + checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d languageName: node linkType: hard @@ -31602,6 +34031,13 @@ __metadata: languageName: node linkType: hard +"lowercase-keys@npm:^1.0.0, lowercase-keys@npm:^1.0.1": + version: 1.0.1 + resolution: "lowercase-keys@npm:1.0.1" + checksum: 4d045026595936e09953e3867722e309415ff2c80d7701d067546d75ef698dac218a4f53c6d1d0e7368b47e45fd7529df47e6cb56fbb90523ba599f898b3d147 + languageName: node + linkType: hard + "lowercase-keys@npm:^2.0.0": version: 2.0.0 resolution: "lowercase-keys@npm:2.0.0" @@ -31647,7 +34083,7 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.10.1, lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": +"lru-cache@npm:^7.10.1, lru-cache@npm:^7.14.0, lru-cache@npm:^7.14.1, lru-cache@npm:^7.7.1": version: 7.18.3 resolution: "lru-cache@npm:7.18.3" checksum: e550d772384709deea3f141af34b6d4fa392e2e418c1498c078de0ee63670f1f46f5eee746e8ef7e69e1c895af0d4224e62ee33e66a543a14763b0f2e74c1356 @@ -31661,6 +34097,22 @@ __metadata: languageName: node linkType: hard +"lru-queue@npm:^0.1.0": + version: 0.1.0 + resolution: "lru-queue@npm:0.1.0" + dependencies: + es5-ext: ~0.10.2 + checksum: 7f2c53c5e7f2de20efb6ebb3086b7aea88d6cf9ae91ac5618ece974122960c4e8ed04988e81d92c3e63d60b12c556b14d56ef7a9c5a4627b23859b813e39b1a2 + languageName: node + linkType: hard + +"lru_map@npm:^0.3.3": + version: 0.3.3 + resolution: "lru_map@npm:0.3.3" + checksum: ca9dd43c65ed7a4f117c548028101c5b6855e10923ea9d1f635af53ad20c5868ff428c364d454a7b57fe391b89c704982275410c3c5099cca5aeee00d76e169a + languageName: node + linkType: hard + "lunr@npm:^2.3.9": version: 2.3.9 resolution: "lunr@npm:2.3.9" @@ -31831,6 +34283,15 @@ __metadata: languageName: node linkType: hard +"map-age-cleaner@npm:^0.2.0": + version: 0.2.0 + resolution: "map-age-cleaner@npm:0.2.0" + dependencies: + p-defer: ^1.0.0 + checksum: 13a6810b76b0067efa7f4b0f3dc58b58b4a4b5faa4cae5a0e8d5d59eda04d7074724eee426c9b5890a1d7e14d1e2902a090587acc8e2430198e79ab1556a2dad + languageName: node + linkType: hard + "map-cache@npm:^0.2.0": version: 0.2.2 resolution: "map-cache@npm:0.2.2" @@ -31951,6 +34412,17 @@ __metadata: languageName: node linkType: hard +"md5@npm:^2.2.1": + version: 2.3.0 + resolution: "md5@npm:2.3.0" + dependencies: + charenc: 0.0.2 + crypt: 0.0.2 + is-buffer: ~1.1.6 + checksum: a63cacf4018dc9dee08c36e6f924a64ced735b37826116c905717c41cebeb41a522f7a526ba6ad578f9c80f02cb365033ccd67fe186ffbcc1a1faeb75daa9b6e + languageName: node + linkType: hard + "mdast-util-definitions@npm:^5.0.0": version: 5.1.0 resolution: "mdast-util-definitions@npm:5.1.0" @@ -32185,6 +34657,22 @@ __metadata: languageName: node linkType: hard +"memoizee@npm:^0.4.15": + version: 0.4.15 + resolution: "memoizee@npm:0.4.15" + dependencies: + d: ^1.0.1 + es5-ext: ^0.10.53 + es6-weak-map: ^2.0.3 + event-emitter: ^0.3.5 + is-promise: ^2.2.2 + lru-queue: ^0.1.0 + next-tick: ^1.1.0 + timers-ext: ^0.1.7 + checksum: 4065d94416dbadac56edf5947bf342beca0e9f051f33ad60d7c4baf3f6ca0f3c6fdb770c5caed5a89c0ceaf9121428582f396445d591785281383d60aa883418 + languageName: node + linkType: hard + "meow@npm:^6.0.0": version: 6.1.1 resolution: "meow@npm:6.1.1" @@ -32608,6 +35096,15 @@ __metadata: languageName: node linkType: hard +"mime-format@npm:2.0.1": + version: 2.0.1 + resolution: "mime-format@npm:2.0.1" + dependencies: + charset: ^1.0.0 + checksum: 294a29035e8d430bba2cb5985a1bf31d9f97effe53bcaf269a816ed054c10b7883fa838f30aabaaccdd44d553dce40f32c39ec0efe21b58fa26a9dbfb02015cd + languageName: node + linkType: hard + "mime-types@npm:2.1.18": version: 2.1.18 resolution: "mime-types@npm:2.1.18" @@ -32617,7 +35114,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.0.8, mime-types@npm:^2.1.12, 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": +"mime-types@npm:2.1.35, mime-types@npm:^2.0.8, mime-types@npm:^2.1.12, 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: @@ -32660,6 +35157,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^3.0.0": + version: 3.1.0 + resolution: "mimic-fn@npm:3.1.0" + checksum: f7b167f9115b8bbdf2c3ee55dce9149d14be9e54b237259c4bc1d8d0512ea60f25a1b323f814eb1fe8f5a541662804bcfcfff3202ca58df143edb986849d58db + languageName: node + linkType: hard + "mimic-fn@npm:^4.0.0": version: 4.0.0 resolution: "mimic-fn@npm:4.0.0" @@ -32667,7 +35171,7 @@ __metadata: languageName: node linkType: hard -"mimic-response@npm:^1.0.0": +"mimic-response@npm:^1.0.0, mimic-response@npm:^1.0.1": version: 1.0.1 resolution: "mimic-response@npm:1.0.1" checksum: 034c78753b0e622bc03c983663b1cdf66d03861050e0c8606563d149bc2b02d63f62ce4d32be4ab50d0553ae0ffe647fc34d1f5281184c6e1e8cf4d85e8d9823 @@ -32747,6 +35251,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:9.0.3, minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 + languageName: node + linkType: hard + "minimatch@npm:^5.0.0, minimatch@npm:^5.0.1, minimatch@npm:^5.1.0, minimatch@npm:^5.1.1, minimatch@npm:^5.1.2": version: 5.1.6 resolution: "minimatch@npm:5.1.6" @@ -32765,15 +35278,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^9.0.1": - version: 9.0.1 - resolution: "minimatch@npm:9.0.1" - dependencies: - brace-expansion: ^2.0.1 - checksum: 97f5f5284bb57dc65b9415dec7f17a0f6531a33572193991c60ff18450dcfad5c2dad24ffeaf60b5261dccd63aae58cc3306e2209d57e7f88c51295a532d8ec3 - languageName: node - linkType: hard - "minimist-options@npm:^4.0.2": version: 4.1.0 resolution: "minimist-options@npm:4.1.0" @@ -32966,7 +35470,7 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:^0.5.4": +"mkdirp@npm:^0.5.4, mkdirp@npm:^0.5.5, mkdirp@npm:^0.5.6": version: 0.5.6 resolution: "mkdirp@npm:0.5.6" dependencies: @@ -33007,6 +35511,55 @@ __metadata: languageName: node linkType: hard +"mockttp@npm:^3.9.1": + version: 3.9.4 + resolution: "mockttp@npm:3.9.4" + dependencies: + "@graphql-tools/schema": ^8.5.0 + "@graphql-tools/utils": ^8.8.0 + "@httptoolkit/httpolyglot": ^2.1.1 + "@httptoolkit/subscriptions-transport-ws": ^0.11.2 + "@httptoolkit/websocket-stream": ^6.0.1 + "@types/cors": ^2.8.6 + "@types/node": "*" + base64-arraybuffer: ^0.1.5 + body-parser: ^1.15.2 + cacheable-lookup: ^6.0.0 + common-tags: ^1.8.0 + connect: ^3.7.0 + cors: ^2.8.4 + cors-gate: ^1.1.3 + cross-fetch: ^3.1.5 + destroyable-server: ^1.0.0 + express: ^4.14.0 + graphql: ^14.0.2 || ^15.5 + graphql-http: ^1.22.0 + graphql-subscriptions: ^1.1.0 + graphql-tag: ^2.12.6 + http-encoding: ^1.5.1 + http2-wrapper: ^2.2.0 + https-proxy-agent: ^5.0.1 + isomorphic-ws: ^4.0.1 + lodash: ^4.16.4 + lru-cache: ^7.14.0 + native-duplexpair: ^1.0.0 + node-forge: ^1.2.1 + pac-proxy-agent: ^7.0.0 + parse-multipart-data: ^1.4.0 + performance-now: ^2.1.0 + portfinder: 1.0.28 + read-tls-client-hello: ^1.0.0 + semver: ^7.5.3 + socks-proxy-agent: ^7.0.0 + typed-error: ^3.0.2 + uuid: ^8.3.2 + ws: ^8.8.0 + bin: + mockttp: dist/admin/admin-bin.js + checksum: 2e0b984d77a94e6a754e44c85a7ff2ded13ba42fd6cabf125b677a8a57eff543c896bf3ecb522799d3efbe18733bf019fbe707044f098fdd5e8e4bc2c0b1df4f + languageName: node + linkType: hard + "moment@npm:^2.29.1": version: 2.29.4 resolution: "moment@npm:2.29.4" @@ -33237,6 +35790,13 @@ __metadata: languageName: node linkType: hard +"native-duplexpair@npm:^1.0.0": + version: 1.0.0 + resolution: "native-duplexpair@npm:1.0.0" + checksum: d849a8cb78c59eb12326fde2a84fedc26568b4317da46d061e7110a35961230b674a04ec2496860c2eb5f05288176c7ce0eb3a51eb0ed6b76a4263f637461f9d + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -33290,6 +35850,27 @@ __metadata: languageName: node linkType: hard +"netmask@npm:^2.0.2": + version: 2.0.2 + resolution: "netmask@npm:2.0.2" + checksum: c65cb8d3f7ea5669edddb3217e4c96910a60d0d9a4b52d9847ff6b28b2d0277cd8464eee0ef85133cdee32605c57940cacdd04a9a019079b091b6bba4cb0ec22 + languageName: node + linkType: hard + +"next-tick@npm:1, next-tick@npm:^1.1.0": + version: 1.1.0 + resolution: "next-tick@npm:1.1.0" + checksum: 83b5cf36027a53ee6d8b7f9c0782f2ba87f4858d977342bfc3c20c21629290a2111f8374d13a81221179603ffc4364f38374b5655d17b6a8f8a8c77bdea4fe8b + languageName: node + linkType: hard + +"nice-try@npm:^1.0.4": + version: 1.0.5 + resolution: "nice-try@npm:1.0.5" + checksum: 0b4af3b5bb5d86c289f7a026303d192a7eb4417231fe47245c460baeabae7277bcd8fd9c728fb6bd62c30b3e15cd6620373e2cf33353b095d8b403d3e8a15aff + languageName: node + linkType: hard + "nimma@npm:0.2.2": version: 0.2.2 resolution: "nimma@npm:0.2.2" @@ -33416,7 +35997,7 @@ __metadata: languageName: node linkType: hard -"node-forge@npm:^1, node-forge@npm:^1.3.1": +"node-forge@npm:^1, node-forge@npm:^1.2.1, node-forge@npm:^1.3.1": version: 1.3.1 resolution: "node-forge@npm:1.3.1" checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 @@ -33484,6 +36065,16 @@ __metadata: languageName: node linkType: hard +"node-html-parser@npm:^5.3.3": + version: 5.4.2 + resolution: "node-html-parser@npm:5.4.2" + dependencies: + css-select: ^4.2.1 + he: 1.2.0 + checksum: 2d2391147c83b402786eeab95d23ea4e24ca8608e0e70a2823bfd4f2a248be13a8cc31acfd55a0109e051131e4f0c17d7ada8d999ce70ff2e342ab0110f5da59 + languageName: node + linkType: hard + "node-html-parser@npm:^6.1.1": version: 6.1.5 resolution: "node-html-parser@npm:6.1.5" @@ -33532,6 +36123,13 @@ __metadata: languageName: node linkType: hard +"node-machine-id@npm:^1.1.12": + version: 1.1.12 + resolution: "node-machine-id@npm:1.1.12" + checksum: e23088a0fb4a77a1d6484b7f09a22992fd3e0054d4f2e427692b4c7081e6cf30118ba07b6113b6c89f1ce46fd26ec5ab1d76dcaf6c10317717889124511283a5 + languageName: node + linkType: hard + "node-releases@npm:^2.0.13": version: 2.0.13 resolution: "node-releases@npm:2.0.13" @@ -33549,6 +36147,41 @@ __metadata: languageName: node linkType: hard +"node-stdlib-browser@npm:^1.2.0": + version: 1.2.0 + resolution: "node-stdlib-browser@npm:1.2.0" + dependencies: + assert: ^2.0.0 + browser-resolve: ^2.0.0 + browserify-zlib: ^0.2.0 + buffer: ^5.7.1 + console-browserify: ^1.1.0 + constants-browserify: ^1.0.0 + create-require: ^1.1.1 + crypto-browserify: ^3.11.0 + domain-browser: ^4.22.0 + events: ^3.0.0 + https-browserify: ^1.0.0 + isomorphic-timers-promises: ^1.0.1 + os-browserify: ^0.3.0 + path-browserify: ^1.0.1 + pkg-dir: ^5.0.0 + process: ^0.11.10 + punycode: ^1.4.1 + querystring-es3: ^0.2.1 + readable-stream: ^3.6.0 + stream-browserify: ^3.0.0 + stream-http: ^3.2.0 + string_decoder: ^1.0.0 + timers-browserify: ^2.0.4 + tty-browserify: 0.0.1 + url: ^0.11.0 + util: ^0.12.4 + vm-browserify: ^1.0.1 + checksum: fe491f0839319fd9bb95964c6f7da81fc7fde4c3ac9062aa367f19bc5a6060d0d9e423d3de4196cb51f8259d6aaf6cf380048c48a86eb3721c6223dd0dcc5bfd + languageName: node + linkType: hard + "nodemon@npm:^3.0.1": version: 3.0.1 resolution: "nodemon@npm:3.0.1" @@ -33637,6 +36270,13 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:^4.1.0": + version: 4.5.1 + resolution: "normalize-url@npm:4.5.1" + checksum: 9a9dee01df02ad23e171171893e56e22d752f7cff86fb96aafeae074819b572ea655b60f8302e2d85dbb834dc885c972cc1c573892fea24df46b2765065dd05a + languageName: node + linkType: hard + "normalize-url@npm:^6.0.1": version: 6.1.0 resolution: "normalize-url@npm:6.1.0" @@ -33750,6 +36390,15 @@ __metadata: languageName: node linkType: hard +"npm-run-path@npm:^2.0.0": + version: 2.0.2 + resolution: "npm-run-path@npm:2.0.2" + dependencies: + path-key: ^2.0.0 + checksum: acd5ad81648ba4588ba5a8effb1d98d2b339d31be16826a118d50f182a134ac523172101b82eab1d01cb4c2ba358e857d54cfafd8163a1ffe7bd52100b741125 + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -33792,7 +36441,7 @@ __metadata: languageName: node linkType: hard -"nth-check@npm:^2.0.0, nth-check@npm:^2.0.1": +"nth-check@npm:^2.0.1": version: 2.1.1 resolution: "nth-check@npm:2.1.1" dependencies: @@ -34090,6 +36739,17 @@ __metadata: languageName: node linkType: hard +"openapi-merge@npm:^1.3.2": + version: 1.3.2 + resolution: "openapi-merge@npm:1.3.2" + dependencies: + atlassian-openapi: ^1.0.8 + lodash: ^4.17.15 + ts-is-present: ^1.1.1 + checksum: 53284a563270177422db8c7536544913c133dfc5cc7058a1043f3092b5aa997b8224a83c59569d18620f94ccf0a014fcb735e22941a9259b2c60861002f01638 + languageName: node + linkType: hard + "openapi-sampler@npm:^1.2.1": version: 1.2.1 resolution: "openapi-sampler@npm:1.2.1" @@ -34100,7 +36760,7 @@ __metadata: languageName: node linkType: hard -"openapi-types@npm:^12.0.0": +"openapi-types@npm:^12.0.0, openapi-types@npm:^12.0.2": version: 12.1.3 resolution: "openapi-types@npm:12.1.3" checksum: 7fa5547f87a58d2aa0eba6e91d396f42d7d31bc3ae140e61b5d60b47d2fd068b48776f42407d5a8da7280cf31195aa128c2fc285e8bb871d1105edee5647a0bb @@ -34176,7 +36836,7 @@ __metadata: languageName: node linkType: hard -"ora@npm:^5.3.0, ora@npm:^5.4.1": +"ora@npm:5.4.1, ora@npm:^5.3.0, ora@npm:^5.4.1": version: 5.4.1 resolution: "ora@npm:5.4.1" dependencies: @@ -34221,6 +36881,13 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^1.0.0": + version: 1.1.0 + resolution: "p-cancelable@npm:1.1.0" + checksum: 2db3814fef6d9025787f30afaee4496a8857a28be3c5706432cbad76c688a6db1874308f48e364a42f5317f5e41e8e7b4f2ff5c8ff2256dbb6264bc361704ece + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.0.0 resolution: "p-cancelable@npm:2.0.0" @@ -34228,6 +36895,13 @@ __metadata: languageName: node linkType: hard +"p-defer@npm:^1.0.0": + version: 1.0.0 + resolution: "p-defer@npm:1.0.0" + checksum: 4271b935c27987e7b6f229e5de4cdd335d808465604644cb7b4c4c95bef266735859a93b16415af8a41fd663ee9e3b97a1a2023ca9def613dba1bad2a0da0c7b + languageName: node + linkType: hard + "p-filter@npm:^2.1.0": version: 2.1.0 resolution: "p-filter@npm:2.1.0" @@ -34376,6 +37050,45 @@ __metadata: languageName: node linkType: hard +"pac-proxy-agent@npm:^7.0.0": + version: 7.0.1 + resolution: "pac-proxy-agent@npm:7.0.1" + dependencies: + "@tootallnate/quickjs-emscripten": ^0.23.0 + agent-base: ^7.0.2 + debug: ^4.3.4 + get-uri: ^6.0.1 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.2 + pac-resolver: ^7.0.0 + socks-proxy-agent: ^8.0.2 + checksum: 3d4aa48ec1c19db10158ecc1c4c9a9f77792294412d225ceb3dfa45d5a06950dca9755e2db0d9b69f12769119bea0adf2b24390d9c73c8d81df75e28245ae451 + languageName: node + linkType: hard + +"pac-resolver@npm:^7.0.0": + version: 7.0.0 + resolution: "pac-resolver@npm:7.0.0" + dependencies: + degenerator: ^5.0.0 + ip: ^1.1.8 + netmask: ^2.0.2 + checksum: fa3a898c09848e93e35f5e23443fea36ddb393a851c76a23664a5bf3fcbe58ff77a0bcdae1e4f01b9ea87ea493c52e14d97a0fe39f92474d14cd45559c6e3cde + languageName: node + linkType: hard + +"package-json@npm:^6.3.0": + version: 6.5.0 + resolution: "package-json@npm:6.5.0" + dependencies: + got: ^9.6.0 + registry-auth-token: ^4.0.0 + registry-url: ^5.0.0 + semver: ^6.2.0 + checksum: cc9f890d3667d7610e6184decf543278b87f657d1ace0deb4a9c9155feca738ef88f660c82200763d3348010f4e42e9c7adc91e96ab0f86a770955995b5351e2 + languageName: node + linkType: hard + "packet-reader@npm:1.0.0": version: 1.0.0 resolution: "packet-reader@npm:1.0.0" @@ -34530,6 +37243,13 @@ __metadata: languageName: node linkType: hard +"parse-multipart-data@npm:^1.4.0": + version: 1.5.0 + resolution: "parse-multipart-data@npm:1.5.0" + checksum: a385fb6609a7b393ee7e82042d5f923beaa7fb7d81d430db560869b719574f62f39a30e77fd711fbfa6fe3e212a8e6f81fd2126a80876a3c13dc1ae975eb5d91 + languageName: node + linkType: hard + "parse-path@npm:^7.0.0": version: 7.0.0 resolution: "parse-path@npm:7.0.0" @@ -34814,6 +37534,13 @@ __metadata: languageName: node linkType: hard +"path-key@npm:^2.0.0, path-key@npm:^2.0.1": + version: 2.0.1 + resolution: "path-key@npm:2.0.1" + checksum: f7ab0ad42fe3fb8c7f11d0c4f849871e28fbd8e1add65c370e422512fc5887097b9cf34d09c1747d45c942a8c1e26468d6356e2df3f740bf177ab8ca7301ebfd + languageName: node + linkType: hard + "path-key@npm:^3.0.0, path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -34905,6 +37632,13 @@ __metadata: languageName: node linkType: hard +"pathe@npm:^0.2.0": + version: 0.2.0 + resolution: "pathe@npm:0.2.0" + checksum: 9a8149ce152088f30d15b0b03a7c128ba21f16b4dc1f3f90fe38eee9f6d0f1d6da8e4e47bd2a4f9e14aaac7c30ed01cfc86216479011de2bdc598b65e6f19f41 + languageName: node + linkType: hard + "pause@npm:0.0.1": version: 0.0.1 resolution: "pause@npm:0.0.1" @@ -35197,6 +37931,15 @@ __metadata: languageName: node linkType: hard +"pkg-dir@npm:^5.0.0": + version: 5.0.0 + resolution: "pkg-dir@npm:5.0.0" + dependencies: + find-up: ^5.0.0 + checksum: b167bb8dac7bbf22b1d5e30ec223e6b064b84b63010c9d49384619a36734caf95ed23ad23d4f9bd975e8e8082b60a83395f43a89bb192df53a7c25a38ecb57d9 + languageName: node + linkType: hard + "pkg-up@npm:^3.1.0": version: 3.1.0 resolution: "pkg-up@npm:3.1.0" @@ -35244,7 +37987,7 @@ __metadata: languageName: node linkType: hard -"pluralize@npm:^8.0.0": +"pluralize@npm:8.0.0, pluralize@npm:^8.0.0": version: 8.0.0 resolution: "pluralize@npm:8.0.0" checksum: 08931d4a6a4a5561a7f94f67a31c17e6632cb21e459ab3ff4f6f629d9a822984cf8afef2311d2005fbea5d7ef26016ebb090db008e2d8bce39d0a9a9d218736e @@ -35258,6 +38001,18 @@ __metadata: languageName: node linkType: hard +"popmotion@npm:11.0.3": + version: 11.0.3 + resolution: "popmotion@npm:11.0.3" + dependencies: + framesync: 6.0.1 + hey-listen: ^1.0.8 + style-value-types: 5.0.0 + tslib: ^2.1.0 + checksum: 9fe7d03b4ec0e85bfb9dadc23b745147bfe42e16f466ba06e6327197d0e38b72015afc2f918a8051dedc3680310417f346ffdc463be6518e2e92e98f48e30268 + languageName: node + linkType: hard + "popper.js@npm:1.16.1-lts": version: 1.16.1-lts resolution: "popper.js@npm:1.16.1-lts" @@ -35272,6 +38027,28 @@ __metadata: languageName: node linkType: hard +"portfinder@npm:1.0.28": + version: 1.0.28 + resolution: "portfinder@npm:1.0.28" + dependencies: + async: ^2.6.2 + debug: ^3.1.1 + mkdirp: ^0.5.5 + checksum: 91fef602f13f8f4c64385d0ad2a36cc9dc6be0b8d10a2628ee2c3c7b9917ab4fefb458815b82cea2abf4b785cd11c9b4e2d917ac6fa06f14b6fa880ca8f8928c + languageName: node + linkType: hard + +"portfinder@npm:^1.0.28, portfinder@npm:^1.0.32": + version: 1.0.32 + resolution: "portfinder@npm:1.0.32" + dependencies: + async: ^2.6.4 + debug: ^3.2.7 + mkdirp: ^0.5.6 + checksum: 116b4aed1b9e16f6d5503823d966d9ffd41b1c2339e27f54c06cd2f3015a9d8ef53e2a53b57bc0a25af0885977b692007353aa28f9a0a98a44335cb50487240d + languageName: node + linkType: hard + "postcss-calc@npm:^8.0.0": version: 8.0.0 resolution: "postcss-calc@npm:8.0.0" @@ -35680,7 +38457,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.21": +"postcss@npm:^8.1.0, postcss@npm:^8.4.21, postcss@npm:^8.4.27": version: 8.4.31 resolution: "postcss@npm:8.4.31" dependencies: @@ -35758,6 +38535,34 @@ __metadata: languageName: node linkType: hard +"postman-collection@npm:^4.1.7": + version: 4.2.1 + resolution: "postman-collection@npm:4.2.1" + dependencies: + "@faker-js/faker": 5.5.3 + file-type: 3.9.0 + http-reasons: 0.1.0 + iconv-lite: 0.6.3 + liquid-json: 0.3.1 + lodash: 4.17.21 + mime-format: 2.0.1 + mime-types: 2.1.35 + postman-url-encoder: 3.0.5 + semver: 7.5.4 + uuid: 8.3.2 + checksum: b027a084d35ac29244e5a47b533178ff7fe6437924320d17b5dd275461689be32030e5b22469da3f72b113353269b37be09b4353db532672975f830772dd70e0 + languageName: node + linkType: hard + +"postman-url-encoder@npm:3.0.5": + version: 3.0.5 + resolution: "postman-url-encoder@npm:3.0.5" + dependencies: + punycode: ^2.1.1 + checksum: d46b52cf9aa344b86152ac422470854707ab4c6466c2ac7e2b86c5791b0ca2364f1b2957fbae32f7bdc80d0d027ddd78d6c799eb73119a2a4ab00b60e8daa4c4 + languageName: node + linkType: hard + "prebuild-install@npm:^7.1.1": version: 7.1.1 resolution: "prebuild-install@npm:7.1.1" @@ -35813,6 +38618,13 @@ __metadata: languageName: node linkType: hard +"prepend-http@npm:^2.0.0": + version: 2.0.0 + resolution: "prepend-http@npm:2.0.0" + checksum: 7694a9525405447662c1ffd352fcb41b6410c705b739b6f4e3a3e21cf5fdede8377890088e8934436b8b17ba55365a615f153960f30877bf0d0392f9e93503ea + languageName: node + linkType: hard + "prettier@npm:^2.2.1, prettier@npm:^2.7.1": version: 2.8.8 resolution: "prettier@npm:2.8.8" @@ -35862,14 +38674,14 @@ __metadata: languageName: node linkType: hard -"pretty-format@npm:^29.0.0, pretty-format@npm:^29.4.3, pretty-format@npm:^29.6.3": - version: 29.6.3 - resolution: "pretty-format@npm:29.6.3" +"pretty-format@npm:^29.0.0, pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" dependencies: "@jest/schemas": ^29.6.3 ansi-styles: ^5.0.0 react-is: ^18.0.0 - checksum: 4e1c0db48e65571c22e80ff92123925ff8b3a2a89b71c3a1683cfde711004d492de32fe60c6bc10eea8bf6c678e5cbe544ac6c56cb8096e1eb7caf856928b1c4 + checksum: 032c1602383e71e9c0c02a01bbd25d6759d60e9c7cf21937dde8357aa753da348fcec5def5d1002c9678a8524d5fe099ad98861286550ef44de8808cc61e43b6 languageName: node linkType: hard @@ -36174,7 +38986,7 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^1.2.4, punycode@npm:^1.3.2": +"punycode@npm:^1.2.4, punycode@npm:^1.3.2, punycode@npm:^1.4.1": version: 1.4.1 resolution: "punycode@npm:1.4.1" checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 @@ -36188,6 +39000,15 @@ __metadata: languageName: node linkType: hard +"pupa@npm:^2.1.1": + version: 2.1.1 + resolution: "pupa@npm:2.1.1" + dependencies: + escape-goat: ^2.0.0 + checksum: 49529e50372ffdb0cccf0efa0f3b3cb0a2c77805d0d9cc2725bd2a0f6bb414631e61c93a38561b26be1259550b7bb6c2cb92315aa09c8bf93f3bdcb49f2b2fb7 + languageName: node + linkType: hard + "pure-color@npm:^1.2.0": version: 1.3.0 resolution: "pure-color@npm:1.3.0" @@ -36195,6 +39016,13 @@ __metadata: languageName: node linkType: hard +"pure-rand@npm:^6.0.0": + version: 6.0.4 + resolution: "pure-rand@npm:6.0.4" + checksum: e1c4e69f8bf7303e5252756d67c3c7551385cd34d94a1f511fe099727ccbab74c898c03a06d4c4a24a89b51858781057b83ebbfe740d984240cdc04fead36068 + languageName: node + linkType: hard + "pvtsutils@npm:^1.3.2": version: 1.3.2 resolution: "pvtsutils@npm:1.3.2" @@ -36255,7 +39083,7 @@ __metadata: languageName: node linkType: hard -"querystring-es3@npm:^0.2.0": +"querystring-es3@npm:^0.2.0, querystring-es3@npm:^0.2.1": version: 0.2.1 resolution: "querystring-es3@npm:0.2.1" checksum: 691e8d6b8b157e7cd49ae8e83fcf86de39ab3ba948c25abaa94fba84c0986c641aa2f597770848c64abce290ed17a39c9df6df737dfa7e87c3b63acc7d225d61 @@ -36453,7 +39281,7 @@ __metadata: languageName: node linkType: hard -"rc@npm:^1.2.7": +"rc@npm:1.2.8, rc@npm:^1.2.7, rc@npm:^1.2.8": version: 1.2.8 resolution: "rc@npm:1.2.8" dependencies: @@ -36588,7 +39416,7 @@ __metadata: languageName: node linkType: hard -"react-draggable@npm:^4.0.3, react-draggable@npm:^4.4.5": +"react-draggable@npm:^4.0.0, react-draggable@npm:^4.0.3": version: 4.4.6 resolution: "react-draggable@npm:4.4.6" dependencies: @@ -36656,20 +39484,19 @@ __metadata: languageName: node linkType: hard -"react-grid-layout@npm:^1.3.4": - version: 1.4.2 - resolution: "react-grid-layout@npm:1.4.2" +"react-grid-layout@npm:1.3.4": + version: 1.3.4 + resolution: "react-grid-layout@npm:1.3.4" dependencies: - clsx: ^2.0.0 - fast-equals: ^4.0.3 + clsx: ^1.1.1 + lodash.isequal: ^4.0.0 prop-types: ^15.8.1 - react-draggable: ^4.4.5 - react-resizable: ^3.0.5 - resize-observer-polyfill: ^1.5.1 + react-draggable: ^4.0.0 + react-resizable: ^3.0.4 peerDependencies: react: ">= 16.3.0" react-dom: ">= 16.3.0" - checksum: a052d38c290b18e1c513a3b939757c239887009b7f175814b472d007708962870960d3b20d4a15dbdeb955ac55434ce6c5e5e5f8c850cfbdcc90f75c318f1d58 + checksum: f56c8c452acd9588edf1dc6996a4ea14d9f669d77f6b2ebd50146eaeeb9325c83f5ca44b66bac2b8c24f9cb2ec7ed49396350435991255c3b31e21b8a2e3d243 languageName: node linkType: hard @@ -36866,7 +39693,42 @@ __metadata: languageName: node linkType: hard -"react-resizable@npm:^3.0.4, react-resizable@npm:^3.0.5": +"react-remove-scroll-bar@npm:^2.3.3": + version: 2.3.4 + resolution: "react-remove-scroll-bar@npm:2.3.4" + dependencies: + react-style-singleton: ^2.2.1 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: b5ce5f2f98d65c97a3e975823ae4043a4ba2a3b63b5ba284b887e7853f051b5cd6afb74abde6d57b421931c52f2e1fdbb625dc858b1cb5a32c27c14ab85649d4 + languageName: node + linkType: hard + +"react-remove-scroll@npm:2.5.5": + version: 2.5.5 + resolution: "react-remove-scroll@npm:2.5.5" + dependencies: + react-remove-scroll-bar: ^2.3.3 + react-style-singleton: ^2.2.1 + tslib: ^2.1.0 + use-callback-ref: ^1.3.0 + use-sidecar: ^1.1.2 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 2c7fe9cbd766f5e54beb4bec2e2efb2de3583037b23fef8fa511ab426ed7f1ae992382db5acd8ab5bfb030a4b93a06a2ebca41377d6eeaf0e6791bb0a59616a4 + languageName: node + linkType: hard + +"react-resizable@npm:^3.0.4": version: 3.0.5 resolution: "react-resizable@npm:3.0.5" dependencies: @@ -36999,6 +39861,23 @@ __metadata: languageName: node linkType: hard +"react-style-singleton@npm:^2.2.1": + version: 2.2.1 + resolution: "react-style-singleton@npm:2.2.1" + dependencies: + get-nonce: ^1.0.0 + invariant: ^2.2.4 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7ee8ef3aab74c7ae1d70ff34a27643d11ba1a8d62d072c767827d9ff9a520905223e567002e0bf6c772929d8ea1c781a3ba0cc4a563e92b1e3dc2eaa817ecbe8 + languageName: node + linkType: hard + "react-syntax-highlighter@npm:^15.4.5, react-syntax-highlighter@npm:^15.5.0": version: 15.5.0 resolution: "react-syntax-highlighter@npm:15.5.0" @@ -37190,6 +40069,15 @@ __metadata: languageName: node linkType: hard +"read-tls-client-hello@npm:^1.0.0": + version: 1.0.1 + resolution: "read-tls-client-hello@npm:1.0.1" + dependencies: + "@types/node": "*" + checksum: 532c1c32ef049c245b59473ad7a06ad5db61bd22258ccfb54923be24173e8cafbb1a6a17bcc783884dce9b98db15db76a9569ea9c95b2b9b729be990439b931b + languageName: node + linkType: hard + "read-yaml-file@npm:^1.1.0": version: 1.1.0 resolution: "read-yaml-file@npm:1.1.0" @@ -37535,6 +40423,24 @@ __metadata: languageName: node linkType: hard +"registry-auth-token@npm:^4.0.0": + version: 4.2.2 + resolution: "registry-auth-token@npm:4.2.2" + dependencies: + rc: 1.2.8 + checksum: c5030198546ecfdcbcb0722cbc3e260c4f5f174d8d07bdfedd4620e79bfdf17a2db735aa230d600bd388fce6edd26c0a9ed2eb7e9b4641ec15213a28a806688b + languageName: node + linkType: hard + +"registry-url@npm:^5.0.0": + version: 5.1.0 + resolution: "registry-url@npm:5.1.0" + dependencies: + rc: ^1.2.8 + checksum: bcea86c84a0dbb66467b53187fadebfea79017cddfb4a45cf27530d7275e49082fe9f44301976eb0164c438e395684bcf3dae4819b36ff9d1640d8cc60c73df9 + languageName: node + linkType: hard + "regjsparser@npm:^0.9.1": version: 0.9.1 resolution: "regjsparser@npm:0.9.1" @@ -37632,6 +40538,13 @@ __metadata: languageName: node linkType: hard +"remove-trailing-slash@npm:^0.1.0": + version: 0.1.1 + resolution: "remove-trailing-slash@npm:0.1.1" + checksum: dd200c6b7d6f2b49d12b3eff3abc7089917e8a268cefcd5bf67ff23f8c2ad9f866fbe2f3566e1a8dbdc4f4b1171e2941f7dd00852f8de549bb73c3df53b09d96 + languageName: node + linkType: hard + "remove-trailing-spaces@npm:^1.0.6": version: 1.0.8 resolution: "remove-trailing-spaces@npm:1.0.8" @@ -37758,10 +40671,10 @@ __metadata: languageName: node linkType: hard -"resolve-alpn@npm:^1.0.0": - version: 1.0.0 - resolution: "resolve-alpn@npm:1.0.0" - checksum: 146b739f14fad759bc137e7642b4859a7d7a290b61e00db37a468f159932ccefe610006604e6aa7ba2a2991cbd627246f0c74717e8942b7c805c346da35c3f3f +"resolve-alpn@npm:^1.0.0, resolve-alpn@npm:^1.2.0": + version: 1.2.1 + resolution: "resolve-alpn@npm:1.2.1" + checksum: f558071fcb2c60b04054c99aebd572a2af97ef64128d59bef7ab73bd50d896a222a056de40ffc545b633d99b304c259ea9d0c06830d5c867c34f0bfa60b8eae0 languageName: node linkType: hard @@ -37802,16 +40715,16 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.19.0, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:~1.22.1": - version: 1.22.4 - resolution: "resolve@npm:1.22.4" +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, 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.1": + version: 1.22.6 + resolution: "resolve@npm:1.22.6" dependencies: is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 23f25174c2736ce24c6d918910e0d1f89b6b38fefa07a995dff864acd7863d59a7f049e691f93b4b2ee29696303390d921552b6d1b841ed4a8101f517e1d0124 + checksum: d13bf66d4e2ee30d291491f16f2fa44edd4e0cefb85d53249dd6f93e70b2b8c20ec62f01b18662e3cd40e50a7528f18c4087a99490048992a3bb954cf3201a5b languageName: node linkType: hard @@ -37838,16 +40751,16 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": - version: 1.22.4 - resolution: "resolve@patch:resolve@npm%3A1.22.4#~builtin::version=1.22.4&hash=07638b" +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.17.0#~builtin, resolve@patch:resolve@^1.19.0#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": + version: 1.22.6 + resolution: "resolve@patch:resolve@npm%3A1.22.6#~builtin::version=1.22.6&hash=07638b" dependencies: is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: c45f2545fdc4d21883861b032789e20aa67a2f2692f68da320cc84d5724cd02f2923766c5354b3210897e88f1a7b3d6d2c7c22faeead8eed7078e4c783a444bc + checksum: 9d3b3c67aefd12cecbe5f10ca4d1f51ea190891096497c43f301b086883b426466918c3a64f1bbf1788fabb52b579d58809614006c5d0b49186702b3b8fb746a languageName: node linkType: hard @@ -37881,6 +40794,15 @@ __metadata: languageName: node linkType: hard +"responselike@npm:^1.0.2": + version: 1.0.2 + resolution: "responselike@npm:1.0.2" + dependencies: + lowercase-keys: ^1.0.0 + checksum: 2e9e70f1dcca3da621a80ce71f2f9a9cad12c047145c6ece20df22f0743f051cf7c73505e109814915f23f9e34fb0d358e22827723ee3d56b623533cab8eafcd + languageName: node + linkType: hard + "responselike@npm:^2.0.0": version: 2.0.0 resolution: "responselike@npm:2.0.0" @@ -38154,6 +41076,20 @@ __metadata: languageName: node linkType: hard +"rollup@npm:^3.27.1": + version: 3.29.4 + resolution: "rollup@npm:3.29.4" + dependencies: + fsevents: ~2.3.2 + dependenciesMeta: + fsevents: + optional: true + bin: + rollup: dist/bin/rollup + checksum: 8bb20a39c8d91130825159c3823eccf4dc2295c9a0a5c4ed851a5bf2167dbf24d9a29f23461a54c955e5506395e6cc188eafc8ab0e20399d7489fb33793b184e + languageName: node + linkType: hard + "root@workspace:.": version: 0.0.0-use.local resolution: "root@workspace:." @@ -38173,6 +41109,7 @@ __metadata: "@techdocs/cli": "workspace:*" "@types/node": ^18.17.8 "@types/webpack": ^5.28.0 + "@useoptic/optic": ^0.50.10 command-exists: ^1.2.9 concurrently: ^8.0.0 cross-env: ^7.0.0 @@ -38513,6 +41450,15 @@ __metadata: languageName: node linkType: hard +"semver-diff@npm:^3.1.1": + version: 3.1.1 + resolution: "semver-diff@npm:3.1.1" + dependencies: + semver: ^6.3.0 + checksum: 8bbe5a5d7add2d5e51b72314a9215cd294d71f41cdc2bf6bd59ee76411f3610b576172896f1d191d0d7294cb9f2f847438d2ee158adacc0c224dca79052812fe + languageName: node + linkType: hard + "semver-store@npm:^0.3.0": version: 0.3.0 resolution: "semver-store@npm:0.3.0" @@ -38520,7 +41466,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.6.0": +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.5.0, semver@npm:^5.6.0": version: 5.7.2 resolution: "semver@npm:5.7.2" bin: @@ -38529,16 +41475,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^6.0.0, semver@npm:^6.3.0, semver@npm:^6.3.1": - version: 6.3.1 - resolution: "semver@npm:6.3.1" - bin: - semver: bin/semver.js - checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 - languageName: node - linkType: hard - -"semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:~7.5.4": +"semver@npm:7.5.4, semver@npm:^7.1.1, semver@npm:^7.1.2, semver@npm:^7.1.3, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:~7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -38549,6 +41486,15 @@ __metadata: languageName: node linkType: hard +"semver@npm:^6.0.0, semver@npm:^6.2.0, semver@npm:^6.3.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2 + languageName: node + linkType: hard + "send@npm:0.18.0": version: 0.18.0 resolution: "send@npm:0.18.0" @@ -39056,13 +42002,24 @@ __metadata: languageName: node linkType: hard -"socks@npm:^2.6.1, socks@npm:^2.6.2": - version: 2.6.2 - resolution: "socks@npm:2.6.2" +"socks-proxy-agent@npm:^8.0.2": + version: 8.0.2 + resolution: "socks-proxy-agent@npm:8.0.2" dependencies: - ip: ^1.1.5 + agent-base: ^7.0.2 + debug: ^4.3.4 + socks: ^2.7.1 + checksum: 4fb165df08f1f380881dcd887b3cdfdc1aba3797c76c1e9f51d29048be6e494c5b06d68e7aea2e23df4572428f27a3ec22b3d7c75c570c5346507433899a4b6d + languageName: node + linkType: hard + +"socks@npm:^2.6.1, socks@npm:^2.6.2, socks@npm:^2.7.1": + version: 2.7.1 + resolution: "socks@npm:2.7.1" + dependencies: + ip: ^2.0.0 smart-buffer: ^4.2.0 - checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e + checksum: 259d9e3e8e1c9809a7f5c32238c3d4d2a36b39b83851d0f573bfde5f21c4b1288417ce1af06af1452569cd1eb0841169afd4998f0e04ba04656f6b7f0e46d748 languageName: node linkType: hard @@ -39292,6 +42249,15 @@ __metadata: languageName: node linkType: hard +"sprintf-kit@npm:^2.0.1": + version: 2.0.1 + resolution: "sprintf-kit@npm:2.0.1" + dependencies: + es5-ext: ^0.10.53 + checksum: e867136dc67419920da065ff57a75786b884cd66bb08c7b7978d0368ec6169ecbe90acd3fbc53435753a9b053d55075d196fe5e35ec17919c8251c6e909e41ec + languageName: node + linkType: hard + "sqlstring@npm:^2.3.2": version: 2.3.2 resolution: "sqlstring@npm:2.3.2" @@ -39464,7 +42430,7 @@ __metadata: languageName: node linkType: hard -"statuses@npm:>= 1.4.0 < 2": +"statuses@npm:>= 1.4.0 < 2, statuses@npm:~1.5.0": version: 1.5.0 resolution: "statuses@npm:1.5.0" checksum: c469b9519de16a4bb19600205cffb39ee471a5f17b82589757ca7bd40a8d92ebb6ed9f98b5a540c5d302ccbc78f15dc03cc0280dd6e00df1335568a5d5758a5c @@ -39478,7 +42444,7 @@ __metadata: languageName: node linkType: hard -"stream-browserify@npm:3.0.0": +"stream-browserify@npm:3.0.0, stream-browserify@npm:^3.0.0": version: 3.0.0 resolution: "stream-browserify@npm:3.0.0" dependencies: @@ -39505,6 +42471,13 @@ __metadata: languageName: node linkType: hard +"stream-chain@npm:^2.2.5": + version: 2.2.5 + resolution: "stream-chain@npm:2.2.5" + checksum: c83cbf504bd11e2bcbe761a92801295b3decac7ffa4092ceffca2eb1b5d0763bcc511fa22cd8044e8a18c21ca66794fd10c8d9cd1292a3e6c0d83a4194c6b8ed + languageName: node + linkType: hard + "stream-events@npm:^1.0.5": version: 1.0.5 resolution: "stream-events@npm:1.0.5" @@ -39527,6 +42500,27 @@ __metadata: languageName: node linkType: hard +"stream-http@npm:^3.2.0": + version: 3.2.0 + resolution: "stream-http@npm:3.2.0" + dependencies: + builtin-status-codes: ^3.0.0 + inherits: ^2.0.4 + readable-stream: ^3.6.0 + xtend: ^4.0.2 + checksum: c9b78453aeb0c84fcc59555518ac62bacab9fa98e323e7b7666e5f9f58af8f3155e34481078509b02929bd1268427f664d186604cdccee95abc446099b339f83 + languageName: node + linkType: hard + +"stream-json@npm:^1.7.4": + version: 1.8.0 + resolution: "stream-json@npm:1.8.0" + dependencies: + stream-chain: ^2.2.5 + checksum: c17ac72228815850fc5226d8c0a80afd6c2ffbfa71c572ad99ad2eac145dc836a3fc6f62a298b3df716f1726cc1ed8a448892ed9fb6123f46abf2f89c908749f + languageName: node + linkType: hard + "stream-shift@npm:^1.0.0": version: 1.0.1 resolution: "stream-shift@npm:1.0.1" @@ -39604,7 +42598,7 @@ __metadata: languageName: node linkType: hard -"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -39769,6 +42763,13 @@ __metadata: languageName: node linkType: hard +"strip-eof@npm:^1.0.0": + version: 1.0.0 + resolution: "strip-eof@npm:1.0.0" + checksum: 40bc8ddd7e072f8ba0c2d6d05267b4e0a4800898c3435b5fb5f5a21e6e47dfaff18467e7aa0d1844bb5d6274c3097246595841fbfeb317e541974ee992cac506 + languageName: node + linkType: hard + "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -39862,6 +42863,16 @@ __metadata: languageName: node linkType: hard +"style-value-types@npm:5.0.0": + version: 5.0.0 + resolution: "style-value-types@npm:5.0.0" + dependencies: + hey-listen: ^1.0.8 + tslib: ^2.1.0 + checksum: 16d198302cd102edf9dba94e7752a2364c93b1eaa5cc7c32b42b28eef4af4ccb5149a3f16bc2a256adc02616a2404f4612bd15f3081c1e8ca06132cae78be6c0 + languageName: node + linkType: hard + "styled-components@npm:^5.3.3": version: 5.3.11 resolution: "styled-components@npm:5.3.11" @@ -39973,6 +42984,15 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:^6.1.0": + version: 6.1.0 + resolution: "supports-color@npm:6.1.0" + dependencies: + has-flag: ^3.0.0 + checksum: 74358f9535c83ee113fbaac354b11e808060f6e7d8722082ee43af3578469134e89d00026dce2a6b93ce4e5b89d0e9a10f638b2b9f64c7838c2fb2883a47b3d5 + languageName: node + linkType: hard + "supports-color@npm:^7.1.0, supports-color@npm:^7.2.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" @@ -40458,6 +43478,16 @@ __metadata: languageName: node linkType: hard +"timers-ext@npm:^0.1.7": + version: 0.1.7 + resolution: "timers-ext@npm:0.1.7" + dependencies: + es5-ext: ~0.10.46 + next-tick: 1 + checksum: ef3f27a0702a88d885bcbb0317c3e3ecd094ce644da52e7f7d362394a125d9e3578292a8f8966071a980d8abbc3395725333b1856f3ae93835b46589f700d938 + languageName: node + linkType: hard + "timsort@npm:^0.3.0": version: 0.3.0 resolution: "timsort@npm:0.3.0" @@ -40541,6 +43571,13 @@ __metadata: languageName: node linkType: hard +"to-readable-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "to-readable-stream@npm:1.0.0" + checksum: 2bd7778490b6214a2c40276065dd88949f4cf7037ce3964c76838b8cb212893aeb9cceaaf4352a4c486e3336214c350270f3263e1ce7a0c38863a715a4d9aeb5 + languageName: node + linkType: hard + "to-regex-range@npm:^5.0.1": version: 5.0.1 resolution: "to-regex-range@npm:5.0.1" @@ -40767,6 +43804,22 @@ __metadata: languageName: node linkType: hard +"ts-invariant@npm:^0.9.3, ts-invariant@npm:^0.9.4": + version: 0.9.4 + resolution: "ts-invariant@npm:0.9.4" + dependencies: + tslib: ^2.1.0 + checksum: c9e5726361fa266916966b2070605f8664b6dd1d8b0ef7565dbf056abb6a87be26195985ef62dd97aeb0894cf2f4ad5b7f0d89dadadc197eaa38e99222afa29c + languageName: node + linkType: hard + +"ts-is-present@npm:^1.1.1": + version: 1.2.2 + resolution: "ts-is-present@npm:1.2.2" + checksum: 3620ecf48219d0dd108e493260a207f4733d8e39a18dffec23c7ed2b1ef2aba7158d0dfafe36f3f27d0092472535a5e474ce04ade54e972e64b2b6329d20ab0b + languageName: node + linkType: hard + "ts-log@npm:^2.2.3": version: 2.2.5 resolution: "ts-log@npm:2.2.5" @@ -40822,6 +43875,13 @@ __metadata: languageName: node linkType: hard +"ts-results@npm:^3.3.0": + version: 3.3.0 + resolution: "ts-results@npm:3.3.0" + checksum: 426c272901d7a0cf8e9539ed90739d03f8e99245fb669beabba8c6729b92da6574698018f9dc8d5d92e8a3e833a40899f855192208ff8dd7e4a68178f7740132 + languageName: node + linkType: hard + "ts-toolbelt@npm:^9.6.0": version: 9.6.0 resolution: "ts-toolbelt@npm:9.6.0" @@ -40848,7 +43908,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:2.5.0, tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.4.1, tslib@npm:^2.5.0, tslib@npm:~2.5.0": +"tslib@npm:2.5.0, tslib@npm:~2.5.0": version: 2.5.0 resolution: "tslib@npm:2.5.0" checksum: ae3ed5f9ce29932d049908ebfdf21b3a003a85653a9a140d614da6b767a93ef94f460e52c3d787f0e4f383546981713f165037dc2274df212ea9f8a4541004e1 @@ -40862,6 +43922,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.0.0, tslib@npm:^2.0.1, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.2.0, tslib@npm:^2.3.0, tslib@npm:^2.3.1, tslib@npm:^2.4.0, tslib@npm:^2.4.1, tslib@npm:^2.4.1 || ^1.9.3, tslib@npm:^2.5.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad + languageName: node + linkType: hard + "tslib@npm:~2.4.0": version: 2.4.1 resolution: "tslib@npm:2.4.1" @@ -40880,6 +43947,23 @@ __metadata: languageName: node linkType: hard +"tsx@npm:^3.14.0": + version: 3.14.0 + resolution: "tsx@npm:3.14.0" + dependencies: + esbuild: ~0.18.20 + fsevents: ~2.3.3 + get-tsconfig: ^4.7.2 + source-map-support: ^0.5.21 + dependenciesMeta: + fsevents: + optional: true + bin: + tsx: dist/cli.mjs + checksum: afcef5d9b90b5800cf1ffb749e943f63042d78a4c0d9eef6e13e43f4ecab465d45e2c9812a2c515cbdc2ee913ff1cd01bf5c606a48013dd3ce2214a631b45557 + languageName: node + linkType: hard + "tty-browserify@npm:0.0.0": version: 0.0.0 resolution: "tty-browserify@npm:0.0.0" @@ -40887,6 +43971,13 @@ __metadata: languageName: node linkType: hard +"tty-browserify@npm:0.0.1": + version: 0.0.1 + resolution: "tty-browserify@npm:0.0.1" + checksum: 93b745d43fa5a7d2b948fa23be8d313576d1d884b48acd957c07710bac1c0d8ac34c0556ad4c57c73d36e11741763ef66b3fb4fb97b06b7e4d525315a3cd45f5 + languageName: node + linkType: hard + "tty-table@npm:^4.1.5": version: 4.1.6 resolution: "tty-table@npm:4.1.6" @@ -41011,6 +44102,20 @@ __metadata: languageName: node linkType: hard +"type@npm:^1.0.1": + version: 1.2.0 + resolution: "type@npm:1.2.0" + checksum: dae8c64f82c648b985caf321e9dd6e8b7f4f2e2d4f846fc6fd2c8e9dc7769382d8a52369ddbaccd59aeeceb0df7f52fb339c465be5f2e543e81e810e413451ee + languageName: node + linkType: hard + +"type@npm:^2.5.0, type@npm:^2.7.2": + version: 2.7.2 + resolution: "type@npm:2.7.2" + checksum: 0f42379a8adb67fe529add238a3e3d16699d95b42d01adfe7b9a7c5da297f5c1ba93de39265ba30ffeb37dfd0afb3fb66ae09f58d6515da442219c086219f6f4 + languageName: node + linkType: hard + "typed-array-buffer@npm:^1.0.0": version: 1.0.0 resolution: "typed-array-buffer@npm:1.0.0" @@ -41058,6 +44163,13 @@ __metadata: languageName: node linkType: hard +"typed-error@npm:^3.0.2": + version: 3.2.2 + resolution: "typed-error@npm:3.2.2" + checksum: 90d0d2ebef72a3655153d7d4ffe8607ebb38a39e38f9f19642a55542c0459afc887862ff5353d57ee77502c5c438341843b21309ecd0cf2b19a344034c9fedef + languageName: node + linkType: hard + "typed-rest-client@npm:^1.8.4": version: 1.8.4 resolution: "typed-rest-client@npm:1.8.4" @@ -41069,6 +44181,15 @@ __metadata: languageName: node linkType: hard +"typedarray-to-buffer@npm:^3.1.5": + version: 3.1.5 + resolution: "typedarray-to-buffer@npm:3.1.5" + dependencies: + is-typedarray: ^1.0.0 + checksum: 99c11aaa8f45189fcfba6b8a4825fd684a321caa9bd7a76a27cf0c7732c174d198b99f449c52c3818107430b5f41c0ccbbfb75cb2ee3ca4a9451710986d61a60 + languageName: node + linkType: hard + "typedarray@npm:^0.0.6": version: 0.0.6 resolution: "typedarray@npm:0.0.6" @@ -41254,6 +44375,15 @@ __metadata: languageName: node linkType: hard +"uni-global@npm:^1.0.0": + version: 1.0.0 + resolution: "uni-global@npm:1.0.0" + dependencies: + type: ^2.5.0 + checksum: 80550f304b350424381189989715888615a8ba9b0552ca771af63c59714fb0f1358d35dfe679ecff60a872da06be0c96f8a440d5f3c5cedfed4b29d7a2d3932c + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -41336,6 +44466,15 @@ __metadata: languageName: node linkType: hard +"unique-string@npm:^2.0.0": + version: 2.0.0 + resolution: "unique-string@npm:2.0.0" + dependencies: + crypto-random-string: ^2.0.0 + checksum: ef68f639136bcfe040cf7e3cd7a8dff076a665288122855148a6f7134092e6ed33bf83a7f3a9185e46c98dddc445a0da6ac25612afa1a7c38b8b654d6c02498e + languageName: node + linkType: hard + "unist-builder@npm:^3.0.0": version: 3.0.0 resolution: "unist-builder@npm:3.0.0" @@ -41485,6 +44624,13 @@ __metadata: languageName: node linkType: hard +"upath@npm:^2.0.1": + version: 2.0.1 + resolution: "upath@npm:2.0.1" + checksum: 2db04f24a03ef72204c7b969d6991abec9e2cb06fb4c13a1fd1c59bc33b46526b16c3325e55930a11ff86a77a8cbbcda8f6399bf914087028c5beae21ecdb33c + languageName: node + linkType: hard + "update-browserslist-db@npm:^1.0.11": version: 1.0.11 resolution: "update-browserslist-db@npm:1.0.11" @@ -41499,6 +44645,28 @@ __metadata: languageName: node linkType: hard +"update-notifier@npm:^5.1.0": + version: 5.1.0 + resolution: "update-notifier@npm:5.1.0" + dependencies: + boxen: ^5.0.0 + chalk: ^4.1.0 + configstore: ^5.0.1 + has-yarn: ^2.1.0 + import-lazy: ^2.1.0 + is-ci: ^2.0.0 + is-installed-globally: ^0.4.0 + is-npm: ^5.0.0 + is-yarn-global: ^0.3.0 + latest-version: ^5.1.0 + pupa: ^2.1.1 + semver: ^7.3.4 + semver-diff: ^3.1.1 + xdg-basedir: ^4.0.0 + checksum: 461e5e5b002419296d3868ee2abe0f9ab3e1846d9db642936d0c46f838872ec56069eddfe662c45ce1af0a8d6d5026353728de2e0a95ab2e3546a22ea077caf1 + languageName: node + linkType: hard + "upper-case-first@npm:^2.0.2": version: 2.0.2 resolution: "upper-case-first@npm:2.0.2" @@ -41526,13 +44694,29 @@ __metadata: languageName: node linkType: hard -"urijs@npm:^1.19.11": +"urijs@npm:^1.19.10, urijs@npm:^1.19.11": version: 1.19.11 resolution: "urijs@npm:1.19.11" checksum: f9b95004560754d30fd7dbee44b47414d662dc9863f1cf5632a7c7983648df11d23c0be73b9b4f9554463b61d5b0a520b70df9e1ee963ebb4af02e6da2cc80f3 languageName: node linkType: hard +"url-join@npm:^4.0.1": + version: 4.0.1 + resolution: "url-join@npm:4.0.1" + checksum: f74e868bf25dbc8be6a8d7237d4c36bb5b6c62c72e594d5ab1347fe91d6af7ccd9eb5d621e30152e4da45c2e9a26bec21390e911ab54a62d4d82e76028374ee5 + languageName: node + linkType: hard + +"url-parse-lax@npm:^3.0.0": + version: 3.0.0 + resolution: "url-parse-lax@npm:3.0.0" + dependencies: + prepend-http: ^2.0.0 + checksum: 1040e357750451173132228036aff1fd04abbd43eac1fb3e4fca7495a078bcb8d33cb765fe71ad7e473d9c94d98fd67adca63bd2716c815a2da066198dd37217 + languageName: node + linkType: hard + "url-parse@npm:^1.5.10, url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10" @@ -41569,6 +44753,21 @@ __metadata: languageName: node linkType: hard +"use-callback-ref@npm:^1.3.0": + version: 1.3.0 + resolution: "use-callback-ref@npm:1.3.0" + dependencies: + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 7913df383a5a6fcb399212eedefaac2e0c6f843555202d4e3010bac3848afe38ecaa3d0d6500ad1d936fbeffd637e6c517e68edb024af5e6beca7f27f3ce7b21 + languageName: node + linkType: hard + "use-composed-ref@npm:^1.3.0": version: 1.3.0 resolution: "use-composed-ref@npm:1.3.0" @@ -41647,6 +44846,22 @@ __metadata: languageName: node linkType: hard +"use-sidecar@npm:^1.1.2": + version: 1.1.2 + resolution: "use-sidecar@npm:1.1.2" + dependencies: + detect-node-es: ^1.1.0 + tslib: ^2.0.0 + peerDependencies: + "@types/react": ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 925d1922f9853e516eaad526b6fed1be38008073067274f0ecc3f56b17bb8ab63480140dd7c271f94150027c996cea4efe83d3e3525e8f3eda22055f6a39220b + languageName: node + linkType: hard + "use-sync-external-store@npm:^1.0.0, use-sync-external-store@npm:^1.2.0": version: 1.2.0 resolution: "use-sync-external-store@npm:1.2.0" @@ -41681,7 +44896,7 @@ __metadata: languageName: node linkType: hard -"util@npm:^0.12.0, util@npm:^0.12.3": +"util@npm:^0.12.0, util@npm:^0.12.3, util@npm:^0.12.4": version: 0.12.5 resolution: "util@npm:0.12.5" dependencies: @@ -41715,6 +44930,15 @@ __metadata: languageName: node linkType: hard +"uuid@npm:8.3.2, uuid@npm:^8.0.0, uuid@npm:^8.2.0, uuid@npm:^8.3.0, uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + "uuid@npm:^3.3.2": version: 3.4.0 resolution: "uuid@npm:3.4.0" @@ -41724,15 +44948,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:^8.0.0, uuid@npm:^8.2.0, uuid@npm:^8.3.0, uuid@npm:^8.3.2": - version: 8.3.2 - resolution: "uuid@npm:8.3.2" - bin: - uuid: dist/bin/uuid - checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df - languageName: node - linkType: hard - "uuid@npm:^9.0.0": version: 9.0.1 resolution: "uuid@npm:9.0.1" @@ -41959,6 +45174,82 @@ __metadata: languageName: node linkType: hard +"vite-plugin-html@npm:^3.2.0": + version: 3.2.0 + resolution: "vite-plugin-html@npm:3.2.0" + dependencies: + "@rollup/pluginutils": ^4.2.0 + colorette: ^2.0.16 + connect-history-api-fallback: ^1.6.0 + consola: ^2.15.3 + dotenv: ^16.0.0 + dotenv-expand: ^8.0.2 + ejs: ^3.1.6 + fast-glob: ^3.2.11 + fs-extra: ^10.0.1 + html-minifier-terser: ^6.1.0 + node-html-parser: ^5.3.3 + pathe: ^0.2.0 + peerDependencies: + vite: ">=2.0.0" + checksum: f5222247b65da1c36215f0b2f509fd3975a7426b8d44546beb49f3ba51ee87b3a6b6e6afc9e7567a0d8bd1016631f2db3f934808f62a7c8f7f83fa83d8561d2d + languageName: node + linkType: hard + +"vite-plugin-node-polyfills@npm:^0.14.1": + version: 0.14.1 + resolution: "vite-plugin-node-polyfills@npm:0.14.1" + dependencies: + "@rollup/plugin-inject": ^5.0.3 + buffer-polyfill: "npm:buffer@^6.0.3" + node-stdlib-browser: ^1.2.0 + process: ^0.11.10 + peerDependencies: + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 + checksum: 1dd6e4ff500a69ff78c8d7d967e74790b704e3ccb117fc4f8841f2e5d0e339422a15d9c6c0607d1fed4df5a7a77c56d245769b22572d4e905771b37ff8801915 + languageName: node + linkType: hard + +"vite@npm:^4.4.9": + version: 4.4.11 + resolution: "vite@npm:4.4.11" + dependencies: + esbuild: ^0.18.10 + fsevents: ~2.3.2 + postcss: ^8.4.27 + rollup: ^3.27.1 + peerDependencies: + "@types/node": ">= 14" + less: "*" + lightningcss: ^1.21.0 + sass: "*" + stylus: "*" + sugarss: "*" + terser: ^5.4.0 + dependenciesMeta: + fsevents: + optional: true + peerDependenciesMeta: + "@types/node": + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + bin: + vite: bin/vite.js + checksum: c22145c8385343a629cd546054b9da6eee60327540102bdfd1ad897fd2e78e0763ce6a18a9d84fdefde9da8fd2427d3bec9eb2697b47cf4068c7b4b52f7e3e6a + languageName: node + linkType: hard + "viz.js@npm:2.1.2": version: 2.1.2 resolution: "viz.js@npm:2.1.2" @@ -41973,10 +45264,10 @@ __metadata: languageName: node linkType: hard -"vscode-languageserver-types@npm:^3.15.1": - version: 3.15.1 - resolution: "vscode-languageserver-types@npm:3.15.1" - checksum: 28c1cb0d5b7ad7c719015a6eb5f774c000fe68c41bbd4a3fd0cbe6d862f27eec075d6bfb14c9d86c22d0e8711c2df8194295bf1e7d6ac0c359cab348c5e14887 +"vscode-languageserver-types@npm:^3.17.1": + version: 3.17.5 + resolution: "vscode-languageserver-types@npm:3.17.5" + checksum: 79b420e7576398d396579ca3a461c9ed70e78db4403cd28bbdf4d3ed2b66a2b4114031172e51fad49f0baa60a2180132d7cb2ea35aa3157d7af3c325528210ac languageName: node linkType: hard @@ -42482,6 +45773,15 @@ __metadata: languageName: node linkType: hard +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" + dependencies: + string-width: ^4.0.0 + checksum: 03db6c9d0af9329c37d74378ff1d91972b12553c7d72a6f4e8525fe61563fa7adb0b9d6e8d546b7e059688712ea874edd5ded475999abdeedf708de9849310e0 + languageName: node + linkType: hard + "winston-transport@npm:^4.5.0": version: 4.5.0 resolution: "winston-transport@npm:4.5.0" @@ -42577,6 +45877,18 @@ __metadata: languageName: node linkType: hard +"write-file-atomic@npm:^3.0.0": + version: 3.0.3 + resolution: "write-file-atomic@npm:3.0.3" + dependencies: + imurmurhash: ^0.1.4 + is-typedarray: ^1.0.0 + signal-exit: ^3.0.2 + typedarray-to-buffer: ^3.1.5 + checksum: c55b24617cc61c3a4379f425fc62a386cc51916a9b9d993f39734d005a09d5a4bb748bc251f1304e7abd71d0a26d339996c275955f527a131b1dcded67878280 + languageName: node + linkType: hard + "write-file-atomic@npm:^4.0.0, write-file-atomic@npm:^4.0.2": version: 4.0.2 resolution: "write-file-atomic@npm:4.0.2" @@ -42587,6 +45899,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:*, ws@npm:^8.11.0, ws@npm:^8.13.0, ws@npm:^8.8.0": + version: 8.14.2 + resolution: "ws@npm:8.14.2" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b + languageName: node + linkType: hard + "ws@npm:8.11.0": version: 8.11.0 resolution: "ws@npm:8.11.0" @@ -42617,21 +45944,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.11.0, ws@npm:^8.13.0, ws@npm:^8.8.0": - version: 8.14.2 - resolution: "ws@npm:8.14.2" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 3ca0dad26e8cc6515ff392b622a1467430814c463b3368b0258e33696b1d4bed7510bc7030f7b72838b9fdeb8dbd8839cbf808367d6aae2e1d668ce741d4308b - languageName: node - linkType: hard - "xcase@npm:^2.0.1": version: 2.0.1 resolution: "xcase@npm:2.0.1" @@ -42639,6 +45951,13 @@ __metadata: languageName: node linkType: hard +"xdg-basedir@npm:^4.0.0": + version: 4.0.0 + resolution: "xdg-basedir@npm:4.0.0" + checksum: 0073d5b59a37224ed3a5ac0dd2ec1d36f09c49f0afd769008a6e9cd3cd666bd6317bd1c7ce2eab47e1de285a286bad11a9b038196413cd753b79770361855f3c + languageName: node + linkType: hard + "xml-but-prettier@npm:^1.0.1": version: 1.0.1 resolution: "xml-but-prettier@npm:1.0.1" @@ -42755,7 +46074,7 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0, xtend@npm:~4.0.1": +"xtend@npm:^4.0.0, xtend@npm:^4.0.2, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a @@ -42858,10 +46177,10 @@ __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": - version: 2.3.2 - resolution: "yaml@npm:2.3.2" - checksum: acd80cc24df12c808c6dec8a0176d404ef9e6f08ad8786f746ecc9d8974968c53c6e8a67fdfabcc5f99f3dc59b6bb0994b95646ff03d18e9b1dcd59eccc02146 +"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": + version: 2.3.3 + resolution: "yaml@npm:2.3.3" + checksum: cdfd132e7e0259f948929efe8835923df05c013c273c02bb7a2de9b46ac3af53c2778a35b32c7c0f877cc355dc9340ed564018c0242bfbb1278c2a3e53a0e99e languageName: node linkType: hard @@ -43136,6 +46455,13 @@ __metadata: languageName: node linkType: hard +"zstd-codec@npm:^0.1.4": + version: 0.1.4 + resolution: "zstd-codec@npm:0.1.4" + checksum: 8689bc0defc4f387d1be990b8b8ca8ca56690d17dfc8dd4703db798465b92a21e64e54e886acfaa376147d9d07d879a68627b09fddc34a0c93f0dc5c610a790c + languageName: node + linkType: hard + "zustand@npm:3.6.9": version: 3.6.9 resolution: "zustand@npm:3.6.9"