diff --git a/.changeset/angry-ghosts-report.md b/.changeset/angry-ghosts-report.md new file mode 100644 index 0000000000..dfa83663a9 --- /dev/null +++ b/.changeset/angry-ghosts-report.md @@ -0,0 +1,16 @@ +--- +'@backstage/backend-common': patch +--- + +Support a `ensureExists` config option to skip ensuring a configured database exists. This allows deployment scenarios where +limited permissions are given for provisioned databases without privileges to create new databases. If set to `false`, the +database connection will not be validated prior to use which means the backend will not attempt to create the database if it +doesn't exist. You can configure this in your app-config.yaml: + +```yaml +backend: + database: + ensureExists: false +``` + +This defaults to `true` if unspecified. You can also configure this per plugin connection and will override the base option. diff --git a/.changeset/angry-hounds-bow.md b/.changeset/angry-hounds-bow.md deleted file mode 100644 index 4bbfffd4f1..0000000000 --- a/.changeset/angry-hounds-bow.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': patch ---- - -Do not throw in `ScmIntegration` `byUrl` for invalid URLs diff --git a/.changeset/angry-rules-fail.md b/.changeset/angry-rules-fail.md new file mode 100644 index 0000000000..cc52d4478e --- /dev/null +++ b/.changeset/angry-rules-fail.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog': patch +--- + +Add unstable prop for disabling unregister entity menu diff --git a/.changeset/beige-ladybugs-sip.md b/.changeset/beige-ladybugs-sip.md deleted file mode 100644 index f1ef84e233..0000000000 --- a/.changeset/beige-ladybugs-sip.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Improve UX of the Sidebar by adding SidebarScrollWrapper component allowing the user to scroll through Plugins & Shortcuts on smaller screens. Prevent the Sidebar from opening on click on small devices diff --git a/.changeset/breezy-kings-applaud.md b/.changeset/breezy-kings-applaud.md deleted file mode 100644 index 867d135739..0000000000 --- a/.changeset/breezy-kings-applaud.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-pagerduty': patch ---- - -Update README diff --git a/.changeset/brown-olives-hang.md b/.changeset/brown-olives-hang.md deleted file mode 100644 index 64ce4e25d5..0000000000 --- a/.changeset/brown-olives-hang.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -add default branch property for publish GitLab, Bitbucket and Azure actions diff --git a/.changeset/curly-badgers-sit.md b/.changeset/curly-badgers-sit.md new file mode 100644 index 0000000000..ca740e907a --- /dev/null +++ b/.changeset/curly-badgers-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Store filter values set in `EntityListProvider` in query parameters. This allows selected filters to be restored when returning to pages that list catalog entities. diff --git a/.changeset/eight-pots-remember.md b/.changeset/eight-pots-remember.md deleted file mode 100644 index 34c6d018b5..0000000000 --- a/.changeset/eight-pots-remember.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Added filesystem remove/rename built-in actions diff --git a/.changeset/eighty-deers-jam.md b/.changeset/eighty-deers-jam.md deleted file mode 100644 index df8b84aff1..0000000000 --- a/.changeset/eighty-deers-jam.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-import': patch ---- - -More helpful error message when trying to import by folder from non-github diff --git a/.changeset/fair-falcons-scream.md b/.changeset/fair-falcons-scream.md deleted file mode 100644 index 1f2f095c81..0000000000 --- a/.changeset/fair-falcons-scream.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-explore': patch ---- - -- Enhanced core `Button` component to open external links in new tab. -- Replaced the use of `Button` component from material by `core-components` in tools card. diff --git a/.changeset/flat-donkeys-rhyme.md b/.changeset/flat-donkeys-rhyme.md deleted file mode 100644 index c98f4aedc5..0000000000 --- a/.changeset/flat-donkeys-rhyme.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/catalog-model': minor ---- - -Changed the regex to validate names following the Kubernetes validation rule, this allow to be more permissive validating the name of the object in Backstage. diff --git a/.changeset/fresh-elephants-tease.md b/.changeset/fresh-elephants-tease.md deleted file mode 100644 index 5141f96be7..0000000000 --- a/.changeset/fresh-elephants-tease.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -'@backstage/catalog-model': minor -'@backstage/plugin-catalog-backend': minor -'@backstage/plugin-scaffolder': minor -'@backstage/plugin-scaffolder-backend': minor -'@backstage/create-app': patch ---- - -Support for `Template` kinds with version `backstage.io/v1alpha1` has now been removed. This means that the old method of running templates with `Preparers`, `Templaters` and `Publishers` has also been removed. If you had any logic in these abstractions, they should now be moved to `actions` instead, and you can find out more about those in the [documentation](https://backstage.io/docs/features/software-templates/writing-custom-actions) - -If you need any help migrating existing templates, there's a [migration guide](https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2). Reach out to us on Discord in the #support channel if you're having problems. - -The `scaffolder-backend` now no longer requires these `Preparers`, `Templaters`, and `Publishers` to be passed in, now all it needs is the `containerRunner`. - -Please update your `packages/backend/src/plugins/scaffolder.ts` like the following - -```diff -- import { -- DockerContainerRunner, -- SingleHostDiscovery, -- } from '@backstage/backend-common'; -+ import { DockerContainerRunner } from '@backstage/backend-common'; - import { CatalogClient } from '@backstage/catalog-client'; -- import { -- CookieCutter, -- CreateReactAppTemplater, -- createRouter, -- Preparers, -- Publishers, -- Templaters, -- } from '@backstage/plugin-scaffolder-backend'; -+ import { createRouter } from '@backstage/plugin-scaffolder-backend'; - import Docker from 'dockerode'; - import { Router } from 'express'; - import type { PluginEnvironment } from '../types'; - - export default async function createPlugin({ - config, - database, - reader, -+ discovery, - }: PluginEnvironment): Promise { - const dockerClient = new Docker(); - const containerRunner = new DockerContainerRunner({ dockerClient }); - -- const cookiecutterTemplater = new CookieCutter({ containerRunner }); -- const craTemplater = new CreateReactAppTemplater({ containerRunner }); -- const templaters = new Templaters(); - -- templaters.register('cookiecutter', cookiecutterTemplater); -- templaters.register('cra', craTemplater); -- -- const preparers = await Preparers.fromConfig(config, { logger }); -- const publishers = await Publishers.fromConfig(config, { logger }); - -- const discovery = SingleHostDiscovery.fromConfig(config); - const catalogClient = new CatalogClient({ discoveryApi: discovery }); - - return await createRouter({ -- preparers, -- templaters, -- publishers, -+ containerRunner, - logger, - config, - database, - -``` diff --git a/.changeset/gorgeous-timers-cover.md b/.changeset/gorgeous-timers-cover.md deleted file mode 100644 index de71d740c0..0000000000 --- a/.changeset/gorgeous-timers-cover.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/techdocs-common': patch ---- - -Fix openStack swift publisher encoding issue. Remove utf8 forced encoding on binary files diff --git a/.changeset/great-bears-work.md b/.changeset/great-bears-work.md deleted file mode 100644 index 572cf1538d..0000000000 --- a/.changeset/great-bears-work.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Added a `context` parameter to validator functions, letting them have access to -the API holder. - -If you have implemented custom validators and use `createScaffolderFieldExtension`, -your `validation` function can now optionally accept a third parameter, -`context: { apiHolder: ApiHolder }`. diff --git a/.changeset/green-vans-peel.md b/.changeset/green-vans-peel.md new file mode 100644 index 0000000000..cd4d30139a --- /dev/null +++ b/.changeset/green-vans-peel.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Fix `EntityTypeFilter` so it produces unique case-insensitive set of available types diff --git a/.changeset/grumpy-dolls-call.md b/.changeset/grumpy-dolls-call.md new file mode 100644 index 0000000000..13d96ecb1f --- /dev/null +++ b/.changeset/grumpy-dolls-call.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-catalog-react': minor +'@backstage/plugin-scaffolder': patch +--- + +Updated the software templates list page (`ScaffolderPage`) to use the `useEntityListProvider` hook from #5643. This reduces the code footprint, making it easier to customize the display of this page, and consolidates duplicate approaches to querying the catalog with filters. + +- The `useEntityTypeFilter` hook has been updated along with the underlying `EntityTypeFilter` to work with multiple values, to allow more flexibility for different user interfaces. It's unlikely that this change affects you; however, if you're using either of these directly, you'll need to update your usage. +- `SearchToolbar` was renamed to `EntitySearchBar` and moved to `catalog-react` to be usable by other entity list pages +- `UserListPicker` now has an `availableTypes` prop to restrict which user-related options to present diff --git a/.changeset/happy-pugs-notice.md b/.changeset/happy-pugs-notice.md deleted file mode 100644 index 4120018da9..0000000000 --- a/.changeset/happy-pugs-notice.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog': patch ---- - -Change catalog page layout to use Grid components to improve responsiveness diff --git a/.changeset/healthy-colts-watch.md b/.changeset/healthy-colts-watch.md deleted file mode 100644 index 2b24621f20..0000000000 --- a/.changeset/healthy-colts-watch.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -'@backstage/backend-test-utils': patch -'@backstage/create-app': patch -'@backstage/plugin-catalog-backend': patch ---- - -bump sqlite3 to 5.0.1 diff --git a/.changeset/honest-cars-glow.md b/.changeset/honest-cars-glow.md deleted file mode 100644 index 3fdc6e335a..0000000000 --- a/.changeset/honest-cars-glow.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -'@backstage/backend-common': patch ---- - -Added a `readUrl` method to the `UrlReader` interface that allows for complex response objects and is intended to replace the `read` method. This new method is currently optional to implement which allows for a soft migration to `readUrl` instead of `read` in the future. - -The main use case for `readUrl` returning an object instead of solely a read buffer is to allow for additional metadata such as ETag, which is a requirement for more efficient catalog processing. - -The `GithubUrlReader` and `GitlabUrlReader` readers fully implement `readUrl`. The other existing readers implement the new method but do not propagate or return ETags. - -While the `readUrl` method is not yet required, it will be in the future, and we already log deprecation warnings when custom `UrlReader` implementations that do not implement `readUrl` are used. We therefore recommend that any existing custom implementations are migrated to implement `readUrl`. - -The old `read` and the new `readUrl` methods can easily be implemented using one another, but we recommend moving the chunk of the implementation to the new `readUrl` method as `read` is being removed, for example this: - -```ts -class CustomUrlReader implements UrlReader { - read(url: string): Promise { - const res = await fetch(url); - - if (!res.ok) { - // error handling ... - } - - return Buffer.from(await res.text()); - } -} -``` - -Can be migrated to something like this: - -```ts -class CustomUrlReader implements UrlReader { - read(url: string): Promise { - const res = await this.readUrl(url); - return res.buffer(); - } - - async readUrl( - url: string, - _options?: ReadUrlOptions, - ): Promise { - const res = await fetch(url); - - if (!res.ok) { - // error handling ... - } - - const buffer = Buffer.from(await res.text()); - return { buffer: async () => buffer }; - } -} -``` - -While there is no usage of the ETag capability yet in the main Backstage packages, you can already add it to your custom implementations. To do so, refer to the documentation of the `readUrl` method and surrounding types, and the existing implementation in `packages/backend-common/src/reading/GithubUrlReader.ts`. diff --git a/.changeset/large-mice-sin.md b/.changeset/large-mice-sin.md deleted file mode 100644 index 77aba8d3c5..0000000000 --- a/.changeset/large-mice-sin.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/plugin-catalog-backend-module-ldap': minor ---- - -Add extension points to the `LdapOrgReaderProcessor` to make it possible to do more advanced modifications -of the ingested users and groups. diff --git a/.changeset/late-pants-itch.md b/.changeset/late-pants-itch.md deleted file mode 100644 index 7bd3087316..0000000000 --- a/.changeset/late-pants-itch.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Show scroll bar of the sidebar wrapper only on hover diff --git a/.changeset/lemon-crabs-confess.md b/.changeset/lemon-crabs-confess.md new file mode 100644 index 0000000000..0d87843df5 --- /dev/null +++ b/.changeset/lemon-crabs-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Updating fs-extra to 10.0.0 to handle broken symbolic links correctly diff --git a/.changeset/lemon-dancers-taste.md b/.changeset/lemon-dancers-taste.md new file mode 100644 index 0000000000..0542fe4a54 --- /dev/null +++ b/.changeset/lemon-dancers-taste.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +The codeowners processor extracts the username of the primary owner and uses this as the owner field. +Given the kind isn't specified this is assumed to be a group and so the link to the owner in the about card +doesn't work. This change specifies the kind where the entity is a user. e.g: + +`@iain-b` -> `user:iain-b` diff --git a/.changeset/moody-garlics-whisper.md b/.changeset/moody-garlics-whisper.md deleted file mode 100644 index b9683e4637..0000000000 --- a/.changeset/moody-garlics-whisper.md +++ /dev/null @@ -1,24 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': minor ---- - -Adding `config: Config` as a required argument to `createBuiltinActions` and downstream methods in order to support configuration of the default git author used for Scaffolder commits. - -The affected methods are: - -- `createBuiltinActions` -- `createPublishGithubAction` -- `createPublishGitlabAction` -- `createPublishBitbucketAction` -- `createPublishAzureAction` - -Call sites to these methods will need to be migrated to include the new `config` argument. See `createRouter` in `plugins/scaffolder-backend/src/service/router.ts` for an example of adding this new argument. - -To configure the default git author, use the `defaultAuthor` key under `scaffolder` in `app-config.yaml`: - -```yaml -scaffolder: - defaultAuthor: - name: Example - email: example@example.com -``` diff --git a/.changeset/nice-bugs-beg.md b/.changeset/nice-bugs-beg.md new file mode 100644 index 0000000000..c41b54b220 --- /dev/null +++ b/.changeset/nice-bugs-beg.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-user-settings': minor +--- + +Exported and renamed components from the `@backstage/plugin-user-settings` plugin , to be able to use it in the consumer side and customize the `SettingPage` diff --git a/.changeset/polite-spies-judge.md b/.changeset/polite-spies-judge.md new file mode 100644 index 0000000000..075bcf1598 --- /dev/null +++ b/.changeset/polite-spies-judge.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend-module-rails': patch +--- + +updated paths to consider differences between platform (windows corrected) diff --git a/.changeset/poor-otters-buy.md b/.changeset/poor-otters-buy.md new file mode 100644 index 0000000000..b60cbddc1b --- /dev/null +++ b/.changeset/poor-otters-buy.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Add new `fetch:template` action which handles the same responsibilities as `fetch:cookiecutter` without the external dependency on `cookiecutter`. For information on migrating from `fetch:cookiecutter` to `fetch:template`, see the [migration guide](https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template) in the docs. diff --git a/.changeset/pretty-drinks-serve.md b/.changeset/pretty-drinks-serve.md new file mode 100644 index 0000000000..a012aba11f --- /dev/null +++ b/.changeset/pretty-drinks-serve.md @@ -0,0 +1,12 @@ +--- +'@backstage/plugin-scaffolder-backend': patch +--- + +Scaffolder: Added an 'eq' handlebars helper for use in software template YAML files. This can be used to execute a step depending on the value of an input, e.g.: + +```yaml +steps: + id: 'conditional-step' + action: 'custom-action' + if: '{{ eq parameters.myvalue "custom" }}', +``` diff --git a/.changeset/real-plums-vanish.md b/.changeset/real-plums-vanish.md new file mode 100644 index 0000000000..a653a6bdf2 --- /dev/null +++ b/.changeset/real-plums-vanish.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Add Sign In and Handler resolver for Microsoft provider diff --git a/.changeset/red-ladybugs-promise.md b/.changeset/red-ladybugs-promise.md deleted file mode 100644 index dbfbf2bbd4..0000000000 --- a/.changeset/red-ladybugs-promise.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -add support for uiSchema on dependent form fields diff --git a/.changeset/rotten-cooks-try.md b/.changeset/rotten-cooks-try.md deleted file mode 100644 index ccbd315ebe..0000000000 --- a/.changeset/rotten-cooks-try.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-ilert': patch ---- - -chore: bump `@date-io/luxon` from 1.3.13 to 2.10.11 diff --git a/.changeset/rotten-walls-cross.md b/.changeset/rotten-walls-cross.md deleted file mode 100644 index 498c10b203..0000000000 --- a/.changeset/rotten-walls-cross.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-catalog-backend': patch ---- - -Make use of the new `readUrl` method on `UrlReader` from `@backstage/backend-common`. diff --git a/.changeset/search-mighty-mice-collect.md b/.changeset/search-mighty-mice-collect.md new file mode 100644 index 0000000000..bad91ff853 --- /dev/null +++ b/.changeset/search-mighty-mice-collect.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-search-backend-node': minor +--- + +Change return value of `SearchEngine.index` to `Promise` to support +implementation of external search engines. diff --git a/.changeset/serious-falcons-greet.md b/.changeset/serious-falcons-greet.md deleted file mode 100644 index f793ed26d4..0000000000 --- a/.changeset/serious-falcons-greet.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/integration': patch ---- - -Fix downloads from repositories located at bitbucket.org diff --git a/.changeset/shaggy-ties-carry.md b/.changeset/shaggy-ties-carry.md deleted file mode 100644 index 83b7782cd0..0000000000 --- a/.changeset/shaggy-ties-carry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Handle empty code blocks in markdown files so they don't fail rendering diff --git a/.changeset/short-eggs-confess.md b/.changeset/short-eggs-confess.md new file mode 100644 index 0000000000..3fa240a347 --- /dev/null +++ b/.changeset/short-eggs-confess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend-module-ldap': patch +--- + +Expose missing types used by the custom transformers diff --git a/.changeset/shy-garlics-tap.md b/.changeset/shy-garlics-tap.md deleted file mode 100644 index 58c660e864..0000000000 --- a/.changeset/shy-garlics-tap.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/create-app': patch ---- - -Use SidebarScrollWrapper to improve responsiveness of the current sidebar. Change: Wrap a section of SidebarItems with this component to enable scroll for smaller screens. It can also be used in sidebar plugins (see shortcuts plugin for an example). diff --git a/.changeset/shy-rules-design.md b/.changeset/shy-rules-design.md new file mode 100644 index 0000000000..4f009e5aa8 --- /dev/null +++ b/.changeset/shy-rules-design.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-scaffolder-backend': minor +--- + +Updated inputs for the `publish:github:pull-request` action. + +Now requires a `repoUrl` instead of separate `owner` and `repo` inputs. This aligns with the output of the `RepoUrlPicker` ui field used by the pull-request sample template. diff --git a/.changeset/silent-horses-fry.md b/.changeset/silent-horses-fry.md deleted file mode 100644 index 2b7c562607..0000000000 --- a/.changeset/silent-horses-fry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Fix error in error panel, and console warnings about DOM nesting pre inside p diff --git a/.changeset/six-birds-approve.md b/.changeset/six-birds-approve.md deleted file mode 100644 index ce666dd09c..0000000000 --- a/.changeset/six-birds-approve.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-org': patch ---- - -Add edit button to Group Profile Card diff --git a/.changeset/sweet-terms-approve.md b/.changeset/sweet-terms-approve.md deleted file mode 100644 index e8daa06673..0000000000 --- a/.changeset/sweet-terms-approve.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -bump azure-devops-node to 10.2.2 diff --git a/.changeset/tasty-chairs-flash.md b/.changeset/tasty-chairs-flash.md deleted file mode 100644 index 8be48d92b3..0000000000 --- a/.changeset/tasty-chairs-flash.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder': patch ---- - -Pass through the `idToken` in `Authorization` Header for `listActions` request diff --git a/.changeset/tasty-ghosts-raise.md b/.changeset/tasty-ghosts-raise.md deleted file mode 100644 index 4e9de7043a..0000000000 --- a/.changeset/tasty-ghosts-raise.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/plugin-search-backend-node': patch ---- - -Change search scheduler from starting indexing in a fixed interval (for example -every 60 seconds), to wait a fixed time between index runs. -This makes sure that no second index process for the same document type is -started when the previous one is still running. diff --git a/.changeset/techdocs-typescript-isnt-fun.md b/.changeset/techdocs-typescript-isnt-fun.md new file mode 100644 index 0000000000..9341c1c14a --- /dev/null +++ b/.changeset/techdocs-typescript-isnt-fun.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-techdocs-backend': patch +--- + +It is no longer required to provide a generator and a preparer to the TechDocs +router factory when running TechDocs in the "recommended" (e.g. externally +prepared and generated docs) configuration. diff --git a/.changeset/twelve-deers-explode.md b/.changeset/twelve-deers-explode.md deleted file mode 100644 index 0d34392eab..0000000000 --- a/.changeset/twelve-deers-explode.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -Export the `fetchContents` from scaffolder-backend diff --git a/.changeset/violet-maps-smell.md b/.changeset/violet-maps-smell.md deleted file mode 100644 index fdb4c6b46d..0000000000 --- a/.changeset/violet-maps-smell.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -'@backstage/backend-common': patch -'@backstage/plugin-scaffolder-backend': patch ---- - -add defaultBranch property for publish GitHub action diff --git a/.changeset/wet-queens-deny.md b/.changeset/wet-queens-deny.md deleted file mode 100644 index 1e446c619c..0000000000 --- a/.changeset/wet-queens-deny.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/plugin-scaffolder-backend': patch ---- - -removing mandatory of protection for the default branch, that could be handled by the GitHub automation in async manner, thus throwing floating errors diff --git a/.changeset/wise-rockets-smoke.md b/.changeset/wise-rockets-smoke.md new file mode 100644 index 0000000000..c81d20519d --- /dev/null +++ b/.changeset/wise-rockets-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Document the default behavior of `statusCheck` option in `createStatusCheckRouter`. diff --git a/.changeset/young-pigs-beg.md b/.changeset/young-pigs-beg.md deleted file mode 100644 index bfdf726911..0000000000 --- a/.changeset/young-pigs-beg.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/core-components': patch ---- - -Add custom styles to scroll bar of the sidebar wrapper to fix flaky behaviour diff --git a/.changeset/young-tables-reply.md b/.changeset/young-tables-reply.md new file mode 100644 index 0000000000..e08a875c14 --- /dev/null +++ b/.changeset/young-tables-reply.md @@ -0,0 +1,16 @@ +--- +'@backstage/backend-common': patch +'@backstage/cli': patch +'@backstage/config-loader': patch +'@backstage/create-app': patch +'@backstage/techdocs-common': patch +'@backstage/plugin-app-backend': patch +'@backstage/plugin-auth-backend': patch +'@backstage/plugin-catalog-backend': patch +'@backstage/plugin-kubernetes-backend': patch +'@backstage/plugin-rollbar-backend': patch +'@backstage/plugin-scaffolder-backend': patch +'@backstage/plugin-techdocs-backend': patch +--- + +Revert the upgrade to `fs-extra@10.0.0` as that seemed to have broken all installs inexplicably. diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index f6826cbefe..e6af881f1e 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -169,6 +169,7 @@ nohoist nonces noop npm +nunjucks nvarchar nvm OAuth @@ -274,6 +275,7 @@ touchpoints transpilation transpiled truthy +typeahead ui unbreak unmanaged diff --git a/README.md b/README.md index 895106c967..98da194c1b 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,6 @@ Check out [the documentation](https://backstage.io/docs/getting-started) on how ## License -Copyright 2020-2021 © Backstage Project Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage +Copyright 2020-2021 © The Backstage Authors. All rights reserved. The Linux Foundation has registered trademarks and uses trademarks. For a list of trademarks of The Linux Foundation, please see our Trademark Usage page: https://www.linuxfoundation.org/trademark-usage Licensed under the Apache License, Version 2.0: http://www.apache.org/licenses/LICENSE-2.0 diff --git a/docs/features/search/getting-started.md b/docs/features/search/getting-started.md index c412701499..2bd26faf8b 100644 --- a/docs/features/search/getting-started.md +++ b/docs/features/search/getting-started.md @@ -142,7 +142,6 @@ export default async function createPlugin({ const indexBuilder = new IndexBuilder({ logger, searchEngine }); indexBuilder.addCollator({ - type: 'software-catalog', defaultRefreshIntervalSeconds: 600, collator: new DefaultCatalogCollator({ discovery }), }); @@ -262,21 +261,21 @@ const indexBuilder = new IndexBuilder({ logger, searchEngine }); ``` Backstage Search can be used to power search of anything! Plugins like the -Catalog offer default [collators](./concepts.md#collators) which are responsible -for providing documents [to be indexed](./concepts.md#documents-and-indices). -You can register any number of collators with the `IndexBuilder` like this: +Catalog offer default [collators](./concepts.md#collators) (e.g. +[DefaultCatalogCollator](https://github.com/backstage/backstage/blob/df12cc25aa4934a98bc42ed03c07f64a1a0a9d72/plugins/catalog-backend/src/search/DefaultCatalogCollator.ts)) +which are responsible for providing documents +[to be indexed](./concepts.md#documents-and-indices). You can register any +number of collators with the `IndexBuilder` like this: ```typescript const indexBuilder = new IndexBuilder({ logger, searchEngine }); indexBuilder.addCollator({ - type: 'software-catalog', defaultRefreshIntervalSeconds: 600, collator: new DefaultCatalogCollator({ discovery }), }); indexBuilder.addCollator({ - type: 'my-custom-stuff', defaultRefreshIntervalSeconds: 3600, collator: new MyCustomCollator(), }); @@ -290,7 +289,6 @@ its `defaultRefreshIntervalSeconds` value, like this: ```typescript {3} indexBuilder.addCollator({ - type: 'software-catalog', defaultRefreshIntervalSeconds: 600, collator: new DefaultCatalogCollator({ discovery }), }); diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 988af89e47..fb17f4a434 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -654,7 +654,7 @@ spec: steps: - id: fetch-base name: Fetch Base - action: fetch:cookiecutter + action: fetch:template input: url: ./template values: diff --git a/docs/features/software-templates/adding-templates.md b/docs/features/software-templates/adding-templates.md index d185558949..2cdf6f1b35 100644 --- a/docs/features/software-templates/adding-templates.md +++ b/docs/features/software-templates/adding-templates.md @@ -51,7 +51,7 @@ spec: steps: - id: fetch-base name: Fetch Base - action: fetch:cookiecutter + action: fetch:template input: url: ./template values: diff --git a/docs/features/software-templates/builtin-actions.md b/docs/features/software-templates/builtin-actions.md index 14dc7ef861..af3ae97b21 100644 --- a/docs/features/software-templates/builtin-actions.md +++ b/docs/features/software-templates/builtin-actions.md @@ -14,3 +14,54 @@ Azure, GitLab and Bitbucket. A list of all registered actions can be found under `/create/actions`. For local development you should be able to reach them at `http://localhost:3000/create/actions`. + +### Migrating from `fetch:cookiecutter` to `fetch:template` + +The `fetch:template` action is a new action with a similar API to +`fetch:cookiecutter` but no dependency on `cookiecutter`. There are two options +for migrating templates that use `fetch:cookiecutter` to use `fetch:template`: + +#### Using `cookiecutterCompat` mode + +The new `fetch:template` action has a `cookiecutterCompat` flag which should +allow most templates built for `fetch:cookiecutter` to work without any changes. + +1. Update action name in `template.yaml`. The name should be changed from + `fetch:cookiecutter` to `fetch:template`. +2. Set `cookiecutterCompat` to `true` in the `fetch:template` step input in + `template.yaml`. + +```diff + steps: + - id: fetch-base + name: Fetch Base +- action: fetch:cookiecutter ++ action: fetch:template + input: + url: ./skeleton ++ cookiecutterCompat: true + values: +``` + +#### Manual migration + +If you prefer, you can manually migrate your templates to avoid the need for +enabling cookiecutter compatibility mode, which will result in slightly less +verbose template variables expressions. + +1. Update action name in `template.yaml`. The name should be changed from + `fetch:cookiecutter` to `fetch:template`. +2. Update variable syntax in file names and content. `fetch:cookiecutter` + expects variables to be enclosed in `{{` `}}` and prefixed with + `cookiecutter.`, while `fetch:template` expects variables to be enclosed in + `${{` `}}` and prefixed with `values.`. For example, a reference to variable + `myInputVariable` would need to be migrated from + `{{ cookiecutter.myInputVariable }}` to `${{ values.myInputVariable }}`. +3. Replace uses of `jsonify` with `dump`. The + [`jsonify` filter](https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html#jsonify-extension) + is built in to `cookiecutter`, and is not available by default when using + `fetch:template`. The + [`dump` filter](https://mozilla.github.io/nunjucks/templating.html#dump) is + the equivalent filter in nunjucks, so an expression like + `{{ cookiecutter.myAwesomeList | jsonify }}` should be migrated to + `${{ values.myAwesomeList | dump }}`. diff --git a/docs/features/software-templates/writing-custom-actions.md b/docs/features/software-templates/writing-custom-actions.md index 90dd603c8f..53a90e2954 100644 --- a/docs/features/software-templates/writing-custom-actions.md +++ b/docs/features/software-templates/writing-custom-actions.md @@ -101,9 +101,7 @@ should have something similar to the below in ```ts return await createRouter({ - preparers, - templaters, - publishers, + containerRunner, logger, config, database, @@ -118,9 +116,7 @@ will set the available actions that the scaffolder has access to. ```ts const actions = [createNewFileAction()]; return await createRouter({ - preparers, - templaters, - publishers, + containerRunner, logger, config, database, @@ -137,18 +133,17 @@ want to have those as well as your new one, you'll need to do the following: import { createBuiltinActions } from '@backstage/plugin-scaffolder-backend'; const builtInActions = createBuiltinActions({ + containerRunner, integrations, + config, catalogClient, - templaters, reader, }); const actions = [...builtInActions, createNewFileAction()]; return await createRouter({ - preparers, - templaters, - publishers, + containerRunner, logger, config, database, diff --git a/docs/features/software-templates/writing-templates.md b/docs/features/software-templates/writing-templates.md index 529376eb89..7b475f2fa1 100644 --- a/docs/features/software-templates/writing-templates.md +++ b/docs/features/software-templates/writing-templates.md @@ -62,7 +62,7 @@ spec: steps: - id: fetch-base name: Fetch Base - action: fetch:cookiecutter + action: fetch:template input: url: ./template values: @@ -289,8 +289,8 @@ template. These follow the same standard format: - id: fetch-base # A unique id for the step name: Fetch Base # A title displayed in the frontend if: '{{ parameters.name }}' # Optional condition, skip the step if not truthy - action: fetch:cookiecutter # an action to call - input: # input that is passed as arguments to the action handler + action: fetch:template # An action to call + input: # Input that is passed as arguments to the action handler url: ./template values: name: '{{ parameters.name }}' @@ -317,20 +317,20 @@ output: ### The templating syntax -You might have noticed in the examples that there are `{{ }}`, and these are a -`handlebars` templates for linking and glueing all these different parts of -`yaml` together. All the form inputs from the `parameters` section, when passed -to the steps will be available by using the template syntax -`{{ parameters.something }}`. This is great for passing the values from the form -into different steps and reusing these input variables. To pass arrays or -objects use the syntax `{{ json paramaters.something }}` where -`paramaters.something` is of type `object` or `array` in the `jsonSchema`, such -as the `nicknames` parameter in the previous example. +You might have noticed variables wrapped in `{{ }}` in the examples. These are +`handlebars` template strings for linking and gluing the different parts of the +template together. All the form inputs from the `parameters` section will be +available by using this template syntax (for example, +`{{ parameters.firstName }}` inserts the value of `firstName` from the +parameters). This is great for passing the values from the form into different +steps and reusing these input variables. To pass arrays or objects use the +`json` custom [helper](https://handlebarsjs.com/guide/expressions.html#helpers). +For example, `{{ json parameters.nicknames }}` will insert the result of calling +`JSON.stringify` on the value of the `nicknames` parameter. As you can see above in the `Outputs` section, `actions` and `steps` can also -output things. So you can grab that output by using -`steps.$stepId.output.$property`. +output things. You can grab that output using `steps.$stepId.output.$property`. You can read more about all the `inputs` and `outputs` defined in the actions in -code part of the `JSONSchema` or you can read more about our built in ones +code part of the `JSONSchema`, or you can read more about our built in ones [here](./builtin-actions.md). diff --git a/docs/overview/stability-index.md b/docs/overview/stability-index.md index 1cdde89a78..2b730024fd 100644 --- a/docs/overview/stability-index.md +++ b/docs/overview/stability-index.md @@ -332,8 +332,7 @@ Stability: `1` The backend scaffolder plugin that provides an implementation for templates in the catalog. -Stability: `1`. There is planned work to rework the scaffolder in -https://github.com/backstage/backstage/issues/2771. +Stability: `2`. ### `tech-radar` [GitHub](https://github.com/backstage/backstage/tree/master/plugins/tech-radar/) diff --git a/microsite/blog/2021-05-20-adopting-backstage.md b/microsite/blog/2021-05-20-adopting-backstage.md index 191422004b..290436c875 100644 --- a/microsite/blog/2021-05-20-adopting-backstage.md +++ b/microsite/blog/2021-05-20-adopting-backstage.md @@ -132,4 +132,4 @@ Integrating infrastructure of this size and complexity can seem overwhelming. It ## More questions about adopting Backstage? -[Contact the Backstage team at Spotify.](https://calendly.com/spotify-backstage) We’ll share more about what we’ve learned from our experience here at Spotify — and from other companies who are already using Backstage to transform their developer experience. +[Contact the Backstage team at Spotify.](https://backstage.spotify.com/) We’ll share more about what we’ve learned from our experience here at Spotify — and from other companies who are already using Backstage to transform their developer experience. diff --git a/microsite/static/img/backstage-k8s.svg b/microsite/static/img/backstage-k8s.svg index 9a6270f480..2796a396e7 100644 --- a/microsite/static/img/backstage-k8s.svg +++ b/microsite/static/img/backstage-k8s.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-software-catalog.svg b/microsite/static/img/backstage-software-catalog.svg index 188b52a43b..101439497b 100644 --- a/microsite/static/img/backstage-software-catalog.svg +++ b/microsite/static/img/backstage-software-catalog.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-software-templates.svg b/microsite/static/img/backstage-software-templates.svg index 41d51ff8e1..3984cd2870 100644 --- a/microsite/static/img/backstage-software-templates.svg +++ b/microsite/static/img/backstage-software-templates.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/microsite/static/img/backstage-techdocs.svg b/microsite/static/img/backstage-techdocs.svg index 48085df83c..aa9362b1bc 100644 --- a/microsite/static/img/backstage-techdocs.svg +++ b/microsite/static/img/backstage-techdocs.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/package.json b/package.json index 09dc55617b..93517d3374 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "start": "yarn workspace example-app start", "start-backend": "yarn workspace example-backend start", "build": "lerna run build", - "build:api-reports": "tsc && yarn build:api-reports:only", + "build:api-reports": "yarn tsc:full && yarn build:api-reports:only", "build:api-reports:only": "ts-node -T -P scripts/tsconfig.json scripts/api-extractor.ts", "build:api-docs": "yarn build:api-reports --docs", "tsc": "tsc", @@ -44,15 +44,14 @@ "**/@roadiehq/**/@backstage/core": "*", "**/@roadiehq/**/@backstage/plugin-catalog": "*", "**/@roadiehq/**/@backstage/catalog-model": "*", - "**/@microsoft/api-extractor/typescript": "^4.0.3", "graphql-language-service-interface": "2.8.2", "graphql-language-service-parser": "1.9.0" }, "version": "1.0.0", "dependencies": { - "@microsoft/api-documenter": "^7.12.16", - "@microsoft/api-extractor": "7.13.2-pr1916.0", - "@microsoft/api-extractor-model": "^7.12.5" + "@microsoft/api-documenter": "^7.13.30", + "@microsoft/api-extractor": "^7.18.1", + "@microsoft/api-extractor-model": "^7.13.3" }, "devDependencies": { "@changesets/cli": "^2.14.0", @@ -61,7 +60,7 @@ "command-exists": "^1.2.9", "concurrently": "^6.0.0", "eslint-plugin-notice": "^0.9.10", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "husky": "^6.0.0", "lerna": "^4.0.0", "lint-staged": "^10.1.0", diff --git a/packages/app/CHANGELOG.md b/packages/app/CHANGELOG.md index ca623d0ba1..cc67b78f44 100644 --- a/packages/app/CHANGELOG.md +++ b/packages/app/CHANGELOG.md @@ -1,5 +1,35 @@ # example-app +## 0.2.36 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/plugin-pagerduty@0.3.7 + - @backstage/plugin-catalog-import@0.5.12 + - @backstage/plugin-explore@0.3.9 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-scaffolder@0.10.0 + - @backstage/plugin-catalog@0.6.6 + - @backstage/plugin-org@0.3.16 + - @backstage/plugin-techdocs@0.9.9 + - @backstage/plugin-api-docs@0.6.1 + - @backstage/plugin-badges@0.2.4 + - @backstage/plugin-catalog-react@0.2.6 + - @backstage/plugin-circleci@0.2.18 + - @backstage/plugin-cloudbuild@0.2.18 + - @backstage/plugin-code-coverage@0.1.6 + - @backstage/plugin-github-actions@0.4.12 + - @backstage/plugin-jenkins@0.4.7 + - @backstage/plugin-kafka@0.2.10 + - @backstage/plugin-kubernetes@0.4.7 + - @backstage/plugin-lighthouse@0.2.19 + - @backstage/plugin-rollbar@0.3.8 + - @backstage/plugin-search@0.4.2 + - @backstage/plugin-sentry@0.3.14 + - @backstage/plugin-todo@0.1.4 + ## 0.2.34 ### Patch Changes diff --git a/packages/app/package.json b/packages/app/package.json index 448dbb4e9a..b26e94fc26 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,43 +1,43 @@ { "name": "example-app", - "version": "0.2.34", + "version": "0.2.36", "private": true, "bundled": true, "dependencies": { - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/cli": "^0.7.2", "@backstage/core-app-api": "^0.1.3", - "@backstage/core-components": "^0.1.3", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-api-docs": "^0.6.0", - "@backstage/plugin-badges": "^0.2.3", - "@backstage/plugin-catalog": "^0.6.4", - "@backstage/plugin-catalog-import": "^0.5.11", - "@backstage/plugin-catalog-react": "^0.2.4", - "@backstage/plugin-circleci": "^0.2.17", - "@backstage/plugin-cloudbuild": "^0.2.17", - "@backstage/plugin-code-coverage": "^0.1.5", + "@backstage/plugin-api-docs": "^0.6.1", + "@backstage/plugin-badges": "^0.2.4", + "@backstage/plugin-catalog": "^0.6.6", + "@backstage/plugin-catalog-import": "^0.5.12", + "@backstage/plugin-catalog-react": "^0.2.6", + "@backstage/plugin-circleci": "^0.2.18", + "@backstage/plugin-cloudbuild": "^0.2.18", + "@backstage/plugin-code-coverage": "^0.1.6", "@backstage/plugin-cost-insights": "^0.11.0", - "@backstage/plugin-explore": "^0.3.7", + "@backstage/plugin-explore": "^0.3.9", "@backstage/plugin-gcp-projects": "^0.3.0", - "@backstage/plugin-github-actions": "^0.4.10", + "@backstage/plugin-github-actions": "^0.4.12", "@backstage/plugin-graphiql": "^0.2.12", - "@backstage/plugin-jenkins": "^0.4.6", - "@backstage/plugin-kafka": "^0.2.9", - "@backstage/plugin-kubernetes": "^0.4.6", - "@backstage/plugin-lighthouse": "^0.2.18", + "@backstage/plugin-jenkins": "^0.4.7", + "@backstage/plugin-kafka": "^0.2.10", + "@backstage/plugin-kubernetes": "^0.4.7", + "@backstage/plugin-lighthouse": "^0.2.19", "@backstage/plugin-newrelic": "^0.3.0", - "@backstage/plugin-org": "^0.3.15", - "@backstage/plugin-pagerduty": "0.3.6", - "@backstage/plugin-rollbar": "^0.3.7", - "@backstage/plugin-scaffolder": "^0.9.9", - "@backstage/plugin-search": "^0.4.1", - "@backstage/plugin-sentry": "^0.3.13", + "@backstage/plugin-org": "^0.3.16", + "@backstage/plugin-pagerduty": "0.3.7", + "@backstage/plugin-rollbar": "^0.3.8", + "@backstage/plugin-scaffolder": "^0.10.0", + "@backstage/plugin-search": "^0.4.2", + "@backstage/plugin-sentry": "^0.3.14", "@backstage/plugin-shortcuts": "^0.1.4", "@backstage/plugin-tech-radar": "^0.4.1", - "@backstage/plugin-techdocs": "^0.9.7", - "@backstage/plugin-todo": "^0.1.3", + "@backstage/plugin-techdocs": "^0.9.9", + "@backstage/plugin-todo": "^0.1.4", "@backstage/plugin-user-settings": "^0.2.12", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", diff --git a/packages/backend-common/CHANGELOG.md b/packages/backend-common/CHANGELOG.md index 8010c14aec..93016bd13c 100644 --- a/packages/backend-common/CHANGELOG.md +++ b/packages/backend-common/CHANGELOG.md @@ -1,5 +1,65 @@ # @backstage/backend-common +## 0.8.5 + +### Patch Changes + +- 09d3eb684: Added a `readUrl` method to the `UrlReader` interface that allows for complex response objects and is intended to replace the `read` method. This new method is currently optional to implement which allows for a soft migration to `readUrl` instead of `read` in the future. + + The main use case for `readUrl` returning an object instead of solely a read buffer is to allow for additional metadata such as ETag, which is a requirement for more efficient catalog processing. + + The `GithubUrlReader` and `GitlabUrlReader` readers fully implement `readUrl`. The other existing readers implement the new method but do not propagate or return ETags. + + While the `readUrl` method is not yet required, it will be in the future, and we already log deprecation warnings when custom `UrlReader` implementations that do not implement `readUrl` are used. We therefore recommend that any existing custom implementations are migrated to implement `readUrl`. + + The old `read` and the new `readUrl` methods can easily be implemented using one another, but we recommend moving the chunk of the implementation to the new `readUrl` method as `read` is being removed, for example this: + + ```ts + class CustomUrlReader implements UrlReader { + read(url: string): Promise { + const res = await fetch(url); + + if (!res.ok) { + // error handling ... + } + + return Buffer.from(await res.text()); + } + } + ``` + + Can be migrated to something like this: + + ```ts + class CustomUrlReader implements UrlReader { + read(url: string): Promise { + const res = await this.readUrl(url); + return res.buffer(); + } + + async readUrl( + url: string, + _options?: ReadUrlOptions, + ): Promise { + const res = await fetch(url); + + if (!res.ok) { + // error handling ... + } + + const buffer = Buffer.from(await res.text()); + return { buffer: async () => buffer }; + } + } + ``` + + While there is no usage of the ETag capability yet in the main Backstage packages, you can already add it to your custom implementations. To do so, refer to the documentation of the `readUrl` method and surrounding types, and the existing implementation in `packages/backend-common/src/reading/GithubUrlReader.ts`. + +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- c2db794f5: add defaultBranch property for publish GitHub action +- Updated dependencies + - @backstage/integration@0.5.8 + ## 0.8.4 ### Patch Changes diff --git a/packages/backend-common/api-report.md b/packages/backend-common/api-report.md index 22eac06ed2..94f1147b5a 100644 --- a/packages/backend-common/api-report.md +++ b/packages/backend-common/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 { AzureIntegration } from '@backstage/integration'; import { BitbucketIntegration } from '@backstage/integration'; @@ -32,105 +34,133 @@ import { Writable } from 'stream'; // @public (undocumented) export class AzureUrlReader implements UrlReader { - constructor(integration: AzureIntegration, deps: { - treeResponseFactory: ReadTreeResponseFactory; - }); - // (undocumented) - static factory: ReaderFactory; - // (undocumented) - read(url: string): Promise; - // (undocumented) - readTree(url: string, options?: ReadTreeOptions): Promise; - // (undocumented) - readUrl(url: string, _options?: ReadUrlOptions): Promise; - // (undocumented) - search(url: string, options?: SearchOptions): Promise; - // (undocumented) - toString(): string; + constructor( + integration: AzureIntegration, + deps: { + treeResponseFactory: ReadTreeResponseFactory; + }, + ); + // (undocumented) + static factory: ReaderFactory; + // (undocumented) + read(url: string): Promise; + // (undocumented) + readTree(url: string, options?: ReadTreeOptions): Promise; + // (undocumented) + readUrl(url: string, _options?: ReadUrlOptions): Promise; + // (undocumented) + search(url: string, options?: SearchOptions): Promise; + // (undocumented) + toString(): string; } // @public export class BitbucketUrlReader implements UrlReader { - constructor(integration: BitbucketIntegration, deps: { - treeResponseFactory: ReadTreeResponseFactory; - }); - // (undocumented) - static factory: ReaderFactory; - // (undocumented) - read(url: string): Promise; - // (undocumented) - readTree(url: string, options?: ReadTreeOptions): Promise; - // (undocumented) - readUrl(url: string, _options?: ReadUrlOptions): Promise; - // (undocumented) - search(url: string, options?: SearchOptions): Promise; - // (undocumented) - toString(): string; + constructor( + integration: BitbucketIntegration, + deps: { + treeResponseFactory: ReadTreeResponseFactory; + }, + ); + // (undocumented) + static factory: ReaderFactory; + // (undocumented) + read(url: string): Promise; + // (undocumented) + readTree(url: string, options?: ReadTreeOptions): Promise; + // (undocumented) + readUrl(url: string, _options?: ReadUrlOptions): Promise; + // (undocumented) + search(url: string, options?: SearchOptions): Promise; + // (undocumented) + toString(): string; } // @public export interface CacheClient { - delete(key: string): Promise; - get(key: string): Promise; - set(key: string, value: JsonValue, options?: CacheSetOptions): Promise; + delete(key: string): Promise; + get(key: string): Promise; + set(key: string, value: JsonValue, options?: CacheSetOptions): Promise; } // @public export class CacheManager { - forPlugin(pluginId: string): PluginCacheManager; - static fromConfig(config: Config, options?: CacheManagerOptions): CacheManager; - } + forPlugin(pluginId: string): PluginCacheManager; + static fromConfig( + config: Config, + options?: CacheManagerOptions, + ): CacheManager; +} // @public (undocumented) export const coloredFormat: winston.Logform.Format; // @public (undocumented) export interface ContainerRunner { - // (undocumented) - runContainer(opts: RunContainerOptions): Promise; + // (undocumented) + runContainer(opts: RunContainerOptions): Promise; } // @public @deprecated export const createDatabase: typeof createDatabaseClient; // @public -export function createDatabaseClient(dbConfig: Config, overrides?: Partial): Knex; +export function createDatabaseClient( + dbConfig: Config, + overrides?: Partial, +): Knex; // @public (undocumented) -export function createRootLogger(options?: winston.LoggerOptions, env?: NodeJS.ProcessEnv): winston.Logger; +export function createRootLogger( + options?: winston.LoggerOptions, + env?: NodeJS.ProcessEnv, +): winston.Logger; // @public export function createServiceBuilder(_module: NodeModule): ServiceBuilderImpl; // @public (undocumented) -export function createStatusCheckRouter(options: StatusCheckRouterOptions): Promise; +export function createStatusCheckRouter( + options: StatusCheckRouterOptions, +): Promise; // @public (undocumented) export class DatabaseManager { - forPlugin(pluginId: string): PluginDatabaseManager; - static fromConfig(config: Config): DatabaseManager; - } + forPlugin(pluginId: string): PluginDatabaseManager; + static fromConfig(config: Config): DatabaseManager; +} // @public (undocumented) export class DockerContainerRunner implements ContainerRunner { - constructor({ dockerClient }: { - dockerClient: Docker; - }); - // (undocumented) - runContainer({ imageName, command, args, logStream, mountDirs, workingDir, envVars, }: RunContainerOptions): Promise; + constructor({ dockerClient }: { dockerClient: Docker }); + // (undocumented) + runContainer({ + imageName, + command, + args, + logStream, + mountDirs, + workingDir, + envVars, + }: RunContainerOptions): Promise; } // @public -export function ensureDatabaseExists(dbConfig: Config, ...databases: Array): Promise; +export function ensureDatabaseExists( + dbConfig: Config, + ...databases: Array +): Promise; // @public -export function errorHandler(options?: ErrorHandlerOptions): ErrorRequestHandler; +export function errorHandler( + options?: ErrorHandlerOptions, +): ErrorRequestHandler; // @public (undocumented) export type ErrorHandlerOptions = { - showStackTraces?: boolean; - logger?: Logger_2; - logClientErrors?: boolean; + showStackTraces?: boolean; + logger?: Logger_2; + logClientErrors?: boolean; }; // @public (undocumented) @@ -141,128 +171,154 @@ export function getVoidLogger(): winston.Logger; // @public (undocumented) export class Git { - // (undocumented) - add({ dir, filepath, }: { - dir: string; - filepath: string; - }): Promise; - // (undocumented) - addRemote({ dir, url, remote, }: { - dir: string; - remote: string; - url: string; - }): Promise; - // (undocumented) - clone({ url, dir, ref, }: { - url: string; - dir: string; - ref?: string; - }): Promise; - // (undocumented) - commit({ dir, message, author, committer, }: { - dir: string; - message: string; - author: { - name: string; - email: string; - }; - committer: { - name: string; - email: string; - }; - }): Promise; - // (undocumented) - currentBranch({ dir, fullName, }: { - dir: string; - fullName?: boolean; - }): Promise; - // (undocumented) - fetch({ dir, remote, }: { - dir: string; - remote?: string; - }): Promise; - // (undocumented) - static fromAuth: ({ username, password, logger, }: { - username?: string | undefined; - password?: string | undefined; - logger?: Logger_2 | undefined; - }) => Git; - // (undocumented) - init({ dir, defaultBranch, }: { - dir: string; - defaultBranch?: string; - }): Promise; - // (undocumented) - merge({ dir, theirs, ours, author, committer, }: { - dir: string; - theirs: string; - ours?: string; - author: { - name: string; - email: string; - }; - committer: { - name: string; - email: string; - }; - }): Promise; - // (undocumented) - push({ dir, remote }: { - dir: string; - remote: string; - }): Promise; - // (undocumented) - readCommit({ dir, sha, }: { - dir: string; - sha: string; - }): Promise; - // (undocumented) - resolveRef({ dir, ref, }: { - dir: string; - ref: string; - }): Promise; + // (undocumented) + add({ dir, filepath }: { dir: string; filepath: string }): Promise; + // (undocumented) + addRemote({ + dir, + url, + remote, + }: { + dir: string; + remote: string; + url: string; + }): Promise; + // (undocumented) + clone({ + url, + dir, + ref, + }: { + url: string; + dir: string; + ref?: string; + }): Promise; + // (undocumented) + commit({ + dir, + message, + author, + committer, + }: { + dir: string; + message: string; + author: { + name: string; + email: string; + }; + committer: { + name: string; + email: string; + }; + }): Promise; + // (undocumented) + currentBranch({ + dir, + fullName, + }: { + dir: string; + fullName?: boolean; + }): Promise; + // (undocumented) + fetch({ dir, remote }: { dir: string; remote?: string }): Promise; + // (undocumented) + static fromAuth: ({ + username, + password, + logger, + }: { + username?: string | undefined; + password?: string | undefined; + logger?: Logger_2 | undefined; + }) => Git; + // (undocumented) + init({ + dir, + defaultBranch, + }: { + dir: string; + defaultBranch?: string; + }): Promise; + // (undocumented) + merge({ + dir, + theirs, + ours, + author, + committer, + }: { + dir: string; + theirs: string; + ours?: string; + author: { + name: string; + email: string; + }; + committer: { + name: string; + email: string; + }; + }): Promise; + // (undocumented) + push({ dir, remote }: { dir: string; remote: string }): Promise; + // (undocumented) + readCommit({ + dir, + sha, + }: { + dir: string; + sha: string; + }): Promise; + // (undocumented) + resolveRef({ dir, ref }: { dir: string; ref: string }): Promise; } // @public export class GithubUrlReader implements UrlReader { - constructor(integration: GitHubIntegration, deps: { - treeResponseFactory: ReadTreeResponseFactory; - credentialsProvider: GithubCredentialsProvider; - }); - // (undocumented) - static factory: ReaderFactory; - // (undocumented) - read(url: string): Promise; - // (undocumented) - readTree(url: string, options?: ReadTreeOptions): Promise; - // (undocumented) - readUrl(url: string, options?: ReadUrlOptions): Promise; - // (undocumented) - search(url: string, options?: SearchOptions): Promise; - // (undocumented) - toString(): string; + constructor( + integration: GitHubIntegration, + deps: { + treeResponseFactory: ReadTreeResponseFactory; + credentialsProvider: GithubCredentialsProvider; + }, + ); + // (undocumented) + static factory: ReaderFactory; + // (undocumented) + read(url: string): Promise; + // (undocumented) + readTree(url: string, options?: ReadTreeOptions): Promise; + // (undocumented) + readUrl(url: string, options?: ReadUrlOptions): Promise; + // (undocumented) + search(url: string, options?: SearchOptions): Promise; + // (undocumented) + toString(): string; } // @public (undocumented) export class GitlabUrlReader implements UrlReader { - constructor(integration: GitLabIntegration, deps: { - treeResponseFactory: ReadTreeResponseFactory; - }); - // (undocumented) - static factory: ReaderFactory; - // (undocumented) - read(url: string): Promise; - // (undocumented) - readTree(url: string, options?: ReadTreeOptions): Promise; - // (undocumented) - readUrl(url: string, options?: ReadUrlOptions): Promise; - // (undocumented) - search(url: string, options?: SearchOptions): Promise; - // (undocumented) - toString(): string; + constructor( + integration: GitLabIntegration, + deps: { + treeResponseFactory: ReadTreeResponseFactory; + }, + ); + // (undocumented) + static factory: ReaderFactory; + // (undocumented) + read(url: string): Promise; + // (undocumented) + readTree(url: string, options?: ReadTreeOptions): Promise; + // (undocumented) + readUrl(url: string, options?: ReadUrlOptions): Promise; + // (undocumented) + search(url: string, options?: SearchOptions): Promise; + // (undocumented) + toString(): string; } -export { isChildPath } +export { isChildPath }; // @public export function loadBackendConfig(options: Options): Promise; @@ -272,32 +328,32 @@ export function notFoundHandler(): RequestHandler; // @public export type PluginCacheManager = { - getClient: (options?: ClientOptions) => CacheClient; + getClient: (options?: ClientOptions) => CacheClient; }; // @public export interface PluginDatabaseManager { - getClient(): Promise; + getClient(): Promise; } // @public export type PluginEndpointDiscovery = { - getBaseUrl(pluginId: string): Promise; - getExternalBaseUrl(pluginId: string): Promise; + getBaseUrl(pluginId: string): Promise; + getExternalBaseUrl(pluginId: string): Promise; }; // @public export type ReadTreeResponse = { - files(): Promise; - archive(): Promise; - dir(options?: ReadTreeResponseDirOptions): Promise; - etag: string; + files(): Promise; + archive(): Promise; + dir(options?: ReadTreeResponseDirOptions): Promise; + etag: string; }; // @public export type ReadTreeResponseFile = { - path: string; - content(): Promise; + path: string; + content(): Promise; }; // @public @@ -311,37 +367,37 @@ export function resolveSafeChildPath(base: string, path: string): string; // @public (undocumented) export type RunContainerOptions = { - imageName: string; - command?: string | string[]; - args: string[]; - logStream?: Writable; - mountDirs?: Record; - workingDir?: string; - envVars?: Record; + imageName: string; + command?: string | string[]; + args: string[]; + logStream?: Writable; + mountDirs?: Record; + workingDir?: string; + envVars?: Record; }; // @public export type SearchResponse = { - files: SearchResponseFile[]; - etag: string; + files: SearchResponseFile[]; + etag: string; }; // @public export type SearchResponseFile = { - url: string; - content(): Promise; + url: string; + content(): Promise; }; // @public (undocumented) export type ServiceBuilder = { - loadConfig(config: ConfigReader): ServiceBuilder; - setPort(port: number): ServiceBuilder; - setHost(host: string): ServiceBuilder; - setLogger(logger: Logger_2): ServiceBuilder; - enableCors(options: cors.CorsOptions): ServiceBuilder; - setHttpsSettings(settings: HttpsSettings): ServiceBuilder; - addRouter(root: string, router: Router | RequestHandler): ServiceBuilder; - start(): Promise; + loadConfig(config: ConfigReader): ServiceBuilder; + setPort(port: number): ServiceBuilder; + setHost(host: string): ServiceBuilder; + setLogger(logger: Logger_2): ServiceBuilder; + enableCors(options: cors.CorsOptions): ServiceBuilder; + setHttpsSettings(settings: HttpsSettings): ServiceBuilder; + addRouter(root: string, router: Router | RequestHandler): ServiceBuilder; + start(): Promise; }; // @public (undocumented) @@ -352,47 +408,53 @@ export const SingleConnectionDatabaseManager: typeof DatabaseManager; // @public export class SingleHostDiscovery implements PluginEndpointDiscovery { - static fromConfig(config: Config, options?: { - basePath?: string; - }): SingleHostDiscovery; - // (undocumented) - getBaseUrl(pluginId: string): Promise; - // (undocumented) - getExternalBaseUrl(pluginId: string): Promise; - } + static fromConfig( + config: Config, + options?: { + basePath?: string; + }, + ): SingleHostDiscovery; + // (undocumented) + getBaseUrl(pluginId: string): Promise; + // (undocumented) + getExternalBaseUrl(pluginId: string): Promise; +} // @public (undocumented) export type StatusCheck = () => Promise; // @public -export function statusCheckHandler(options?: StatusCheckHandlerOptions): Promise; +export function statusCheckHandler( + options?: StatusCheckHandlerOptions, +): Promise; // @public (undocumented) export interface StatusCheckHandlerOptions { - statusCheck?: StatusCheck; + statusCheck?: StatusCheck; } // @public export type UrlReader = { - read(url: string): Promise; - readUrl?(url: string, options?: ReadUrlOptions): Promise; - readTree(url: string, options?: ReadTreeOptions): Promise; - search(url: string, options?: SearchOptions): Promise; + read(url: string): Promise; + readUrl?(url: string, options?: ReadUrlOptions): Promise; + readTree(url: string, options?: ReadTreeOptions): Promise; + search(url: string, options?: SearchOptions): Promise; }; // @public export class UrlReaders { - static create({ logger, config, factories }: CreateOptions): UrlReader; - static default({ logger, config, factories }: CreateOptions): UrlReader; + static create({ logger, config, factories }: CreateOptions): UrlReader; + static default({ logger, config, factories }: CreateOptions): UrlReader; } // @public -export function useHotCleanup(_module: NodeModule, cancelEffect: () => void): void; +export function useHotCleanup( + _module: NodeModule, + cancelEffect: () => void, +): void; // @public export function useHotMemoize(_module: NodeModule, valueFactory: () => T): T; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/backend-common/config.d.ts b/packages/backend-common/config.d.ts index 4de4631982..bc5e026375 100644 --- a/packages/backend-common/config.d.ts +++ b/packages/backend-common/config.d.ts @@ -64,6 +64,11 @@ export interface Config { connection: string | object; /** Database name prefix override */ prefix?: string; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to true if unspecified. + */ + ensureExists?: boolean; /** Plugin specific database configuration and client override */ plugin?: { [pluginId: string]: { @@ -74,6 +79,11 @@ export interface Config { * @secret */ connection?: string | object; + /** + * Whether to ensure the given database exists by creating it if it does not. + * Defaults to base config if unspecified. + */ + ensureExists?: boolean; }; }; }; diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index fdbea23bd8..6cf20b8234 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.8.4", + "version": "0.8.5", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -33,7 +33,7 @@ "@backstage/config": "^0.1.5", "@backstage/config-loader": "^0.6.4", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@google-cloud/storage": "^5.8.0", "@octokit/rest": "^18.5.3", "@types/cors": "^2.8.6", @@ -47,8 +47,8 @@ "dockerode": "^3.2.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "fs-extra": "^10.0.0", - "git-url-parse": "^11.4.4", + "fs-extra": "9.1.0", + "git-url-parse": "~11.4.4", "helmet": "^4.0.0", "isomorphic-git": "^1.8.0", "keyv": "^4.0.3", diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 55869e221d..f3a3f218b0 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -128,6 +128,14 @@ export class DatabaseManager { }; } + private getEnsureExistsConfig(pluginId: string): boolean { + const baseConfig = this.config.getOptionalBoolean('ensureExists') ?? true; + return ( + this.config.getOptionalBoolean(`${pluginPath(pluginId)}.ensureExists`) ?? + baseConfig + ); + } + /** * Provides a Knex connection plugin config by combining base and plugin config. * @@ -203,13 +211,15 @@ export class DatabaseManager { this.getConfigForPlugin(pluginId) as JsonObject, ); - const databaseName = this.getDatabaseName(pluginId); - try { - await ensureDatabaseExists(pluginConfig, databaseName); - } catch (error) { - throw new Error( - `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, - ); + if (this.getEnsureExistsConfig(pluginId)) { + const databaseName = this.getDatabaseName(pluginId); + try { + await ensureDatabaseExists(pluginConfig, databaseName); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + ); + } } return createDatabaseClient( diff --git a/packages/backend-common/src/service/createStatusCheckRouter.ts b/packages/backend-common/src/service/createStatusCheckRouter.ts index ab24a6953b..fd794cc9c4 100644 --- a/packages/backend-common/src/service/createStatusCheckRouter.ts +++ b/packages/backend-common/src/service/createStatusCheckRouter.ts @@ -22,6 +22,10 @@ import { errorHandler, statusCheckHandler, StatusCheck } from '../middleware'; export interface StatusCheckRouterOptions { logger: Logger; path?: string; + /** + * If not implemented, the default express middleware always returns 200. + * Override this to implement your own logic for a health check. + */ statusCheck?: StatusCheck; } diff --git a/packages/backend-test-utils/CHANGELOG.md b/packages/backend-test-utils/CHANGELOG.md index e529fe464f..2faa5afbc3 100644 --- a/packages/backend-test-utils/CHANGELOG.md +++ b/packages/backend-test-utils/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/backend-test-utils +## 0.1.4 + +### Patch Changes + +- f7134c368: bump sqlite3 to 5.0.1 +- Updated dependencies + - @backstage/backend-common@0.8.5 + ## 0.1.3 ### Patch Changes diff --git a/packages/backend-test-utils/api-report.md b/packages/backend-test-utils/api-report.md index 28d4a00463..184ed74978 100644 --- a/packages/backend-test-utils/api-report.md +++ b/packages/backend-test-utils/api-report.md @@ -3,29 +3,30 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { Knex } from 'knex'; // @public (undocumented) export function isDockerDisabledForTests(): boolean; // @public -export type TestDatabaseId = 'POSTGRES_13' | 'POSTGRES_9' | 'MYSQL_8' | 'SQLITE_3'; +export type TestDatabaseId = + | 'POSTGRES_13' + | 'POSTGRES_9' + | 'MYSQL_8' + | 'SQLITE_3'; // @public export class TestDatabases { - static create(options?: { - ids?: TestDatabaseId[]; - disableDocker?: boolean; - }): TestDatabases; - // (undocumented) - eachSupportedId(): [TestDatabaseId][]; - init(id: TestDatabaseId): Promise; - // (undocumented) - supports(id: TestDatabaseId): boolean; + static create(options?: { + ids?: TestDatabaseId[]; + disableDocker?: boolean; + }): TestDatabases; + // (undocumented) + eachSupportedId(): [TestDatabaseId][]; + init(id: TestDatabaseId): Promise; + // (undocumented) + supports(id: TestDatabaseId): boolean; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/backend-test-utils/package.json b/packages/backend-test-utils/package.json index 65852c3a4f..9ec11a58b7 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.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", + "@backstage/backend-common": "^0.8.5", "@backstage/cli": "^0.7.1", "@backstage/config": "^0.1.5", "knex": "^0.95.1", diff --git a/packages/backend/CHANGELOG.md b/packages/backend/CHANGELOG.md index 9659932635..47edc5107f 100644 --- a/packages/backend/CHANGELOG.md +++ b/packages/backend/CHANGELOG.md @@ -1,5 +1,28 @@ # example-backend +## 0.2.36 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/plugin-scaffolder-backend@0.13.0 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-backend@0.12.0 + - @backstage/backend-common@0.8.5 + - @backstage/plugin-search-backend-node@0.3.0 + - example-app@0.2.36 + - @backstage/plugin-scaffolder-backend-module-rails@0.1.2 + - @backstage/catalog-client@0.3.16 + - @backstage/plugin-auth-backend@0.3.16 + - @backstage/plugin-badges-backend@0.1.8 + - @backstage/plugin-code-coverage-backend@0.1.8 + - @backstage/plugin-kafka-backend@0.2.8 + - @backstage/plugin-kubernetes-backend@0.3.9 + - @backstage/plugin-techdocs-backend@0.8.6 + - @backstage/plugin-todo-backend@0.1.8 + - @backstage/plugin-search-backend@0.2.2 + ## 0.2.35 ### Patch Changes diff --git a/packages/backend/README.md b/packages/backend/README.md index 97890f72cb..e6f0c899ca 100644 --- a/packages/backend/README.md +++ b/packages/backend/README.md @@ -39,6 +39,16 @@ You can also, instead of using dummy values for a huge number of environment var The backend starts up on port 7000 per default. +### Debugging + +The backend is a node process that can be inspected to allow breakpoints and live debugging. To enable this, pass the `--inspect` flag to [backend:dev](https://backstage.io/docs/cli/commands#backenddev). + +To debug the backend in [Visual Studio Code](https://code.visualstudio.com/): + +- Enable Auto Attach (⌘ + Shift + P > Toggle Auto Attach > Only With Flag) +- Open a VSCode terminal (Control + `) +- Run the backend from the VSCode terminal: `yarn start-backend --inspect` + ## Populating The Catalog If you want to use the catalog functionality, you need to add so called diff --git a/packages/backend/package.json b/packages/backend/package.json index c3e693865f..4338f3e2b7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -1,6 +1,6 @@ { "name": "example-backend", - "version": "0.2.35", + "version": "0.2.36", "main": "dist/index.cjs.js", "types": "src/index.ts", "license": "Apache-2.0", @@ -27,32 +27,32 @@ "migrate:create": "knex migrate:make -x ts" }, "dependencies": { - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.2", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/integration": "^0.5.6", + "@backstage/integration": "^0.5.8", "@backstage/plugin-app-backend": "^0.3.13", - "@backstage/plugin-auth-backend": "^0.3.15", - "@backstage/plugin-badges-backend": "^0.1.6", - "@backstage/plugin-catalog-backend": "^0.11.0", - "@backstage/plugin-code-coverage-backend": "^0.1.6", + "@backstage/plugin-auth-backend": "^0.3.16", + "@backstage/plugin-badges-backend": "^0.1.8", + "@backstage/plugin-catalog-backend": "^0.12.0", + "@backstage/plugin-code-coverage-backend": "^0.1.8", "@backstage/plugin-graphql-backend": "^0.1.8", - "@backstage/plugin-kubernetes-backend": "^0.3.8", - "@backstage/plugin-kafka-backend": "^0.2.7", + "@backstage/plugin-kubernetes-backend": "^0.3.9", + "@backstage/plugin-kafka-backend": "^0.2.8", "@backstage/plugin-proxy-backend": "^0.2.9", "@backstage/plugin-rollbar-backend": "^0.1.11", - "@backstage/plugin-scaffolder-backend": "^0.12.4", - "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.1", - "@backstage/plugin-search-backend": "^0.2.0", - "@backstage/plugin-search-backend-node": "^0.2.0", - "@backstage/plugin-techdocs-backend": "^0.8.5", - "@backstage/plugin-todo-backend": "^0.1.6", + "@backstage/plugin-scaffolder-backend": "^0.13.0", + "@backstage/plugin-scaffolder-backend-module-rails": "^0.1.2", + "@backstage/plugin-search-backend": "^0.2.2", + "@backstage/plugin-search-backend-node": "^0.3.0", + "@backstage/plugin-techdocs-backend": "^0.8.6", + "@backstage/plugin-todo-backend": "^0.1.8", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", "azure-devops-node-api": "^10.2.2", "dockerode": "^3.2.1", - "example-app": "^0.2.32", + "example-app": "^0.2.36", "express": "^4.17.1", "express-promise-router": "^4.1.0", "knex": "^0.95.1", diff --git a/packages/catalog-client/CHANGELOG.md b/packages/catalog-client/CHANGELOG.md index 2bae1fc11e..70b79b961f 100644 --- a/packages/catalog-client/CHANGELOG.md +++ b/packages/catalog-client/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/catalog-client +## 0.3.16 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + ## 0.3.15 ### Patch Changes diff --git a/packages/catalog-client/api-report.md b/packages/catalog-client/api-report.md index 1f911c84e9..2abdeb8c75 100644 --- a/packages/catalog-client/api-report.md +++ b/packages/catalog-client/api-report.md @@ -3,83 +3,130 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { Location as Location_2 } from '@backstage/catalog-model'; // @public (undocumented) export type AddLocationRequest = { - type?: string; - target: string; - dryRun?: boolean; - presence?: 'optional' | 'required'; + type?: string; + target: string; + dryRun?: boolean; + presence?: 'optional' | 'required'; }; // @public (undocumented) export type AddLocationResponse = { - location: Location_2; - entities: Entity[]; + location: Location_2; + entities: Entity[]; }; // @public (undocumented) export interface CatalogApi { - // (undocumented) - addLocation(location: AddLocationRequest, options?: CatalogRequestOptions): Promise; - // (undocumented) - getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise>; - // (undocumented) - getEntityByName(name: EntityName, options?: CatalogRequestOptions): Promise; - // (undocumented) - getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise; - // (undocumented) - getLocationById(id: string, options?: CatalogRequestOptions): Promise; - // (undocumented) - getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise; - // (undocumented) - removeEntityByUid(uid: string, options?: CatalogRequestOptions): Promise; - // (undocumented) - removeLocationById(id: string, options?: CatalogRequestOptions): Promise; + // (undocumented) + addLocation( + location: AddLocationRequest, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getEntities( + request?: CatalogEntitiesRequest, + options?: CatalogRequestOptions, + ): Promise>; + // (undocumented) + getEntityByName( + name: EntityName, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getOriginLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeEntityByUid( + uid: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; } // @public (undocumented) export class CatalogClient implements CatalogApi { - constructor(options: { - discoveryApi: DiscoveryApi; - }); - // (undocumented) - addLocation({ type, target, dryRun, presence }: AddLocationRequest, options?: CatalogRequestOptions): Promise; - // (undocumented) - getEntities(request?: CatalogEntitiesRequest, options?: CatalogRequestOptions): Promise>; - // (undocumented) - getEntityByName(compoundName: EntityName, options?: CatalogRequestOptions): Promise; - // (undocumented) - getLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise; - // (undocumented) - getLocationById(id: string, options?: CatalogRequestOptions): Promise; - // (undocumented) - getOriginLocationByEntity(entity: Entity, options?: CatalogRequestOptions): Promise; - // (undocumented) - removeEntityByUid(uid: string, options?: CatalogRequestOptions): Promise; - // (undocumented) - removeLocationById(id: string, options?: CatalogRequestOptions): Promise; - } + constructor(options: { discoveryApi: DiscoveryApi }); + // (undocumented) + addLocation( + { type, target, dryRun, presence }: AddLocationRequest, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getEntities( + request?: CatalogEntitiesRequest, + options?: CatalogRequestOptions, + ): Promise>; + // (undocumented) + getEntityByName( + compoundName: EntityName, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + getOriginLocationByEntity( + entity: Entity, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeEntityByUid( + uid: string, + options?: CatalogRequestOptions, + ): Promise; + // (undocumented) + removeLocationById( + id: string, + options?: CatalogRequestOptions, + ): Promise; +} // @public (undocumented) export type CatalogEntitiesRequest = { - filter?: Record[] | Record | undefined; - fields?: string[] | undefined; + filter?: + | Record[] + | Record + | undefined; + fields?: string[] | undefined; }; // @public (undocumented) export type CatalogListResponse = { - items: T[]; + items: T[]; }; // @public -export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = "backstage.io/catalog-processing"; - +export const ENTITY_STATUS_CATALOG_PROCESSING_TYPE = + 'backstage.io/catalog-processing'; // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/catalog-client/package.json b/packages/catalog-client/package.json index 425e70c20d..fbb37d2449 100644 --- a/packages/catalog-client/package.json +++ b/packages/catalog-client/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-client", - "version": "0.3.15", + "version": "0.3.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,7 +29,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "cross-fetch": "^3.0.6" diff --git a/packages/catalog-model/CHANGELOG.md b/packages/catalog-model/CHANGELOG.md index 7a6cce0a6b..5dbbfd241f 100644 --- a/packages/catalog-model/CHANGELOG.md +++ b/packages/catalog-model/CHANGELOG.md @@ -1,5 +1,71 @@ # @backstage/catalog-model +## 0.9.0 + +### Minor Changes + +- 77db0c454: Changed the regex to validate names following the Kubernetes validation rule, this allow to be more permissive validating the name of the object in Backstage. +- 60e830222: Support for `Template` kinds with version `backstage.io/v1alpha1` has now been removed. This means that the old method of running templates with `Preparers`, `Templaters` and `Publishers` has also been removed. If you had any logic in these abstractions, they should now be moved to `actions` instead, and you can find out more about those in the [documentation](https://backstage.io/docs/features/software-templates/writing-custom-actions) + + If you need any help migrating existing templates, there's a [migration guide](https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2). Reach out to us on Discord in the #support channel if you're having problems. + + The `scaffolder-backend` now no longer requires these `Preparers`, `Templaters`, and `Publishers` to be passed in, now all it needs is the `containerRunner`. + + Please update your `packages/backend/src/plugins/scaffolder.ts` like the following + + ```diff + - import { + - DockerContainerRunner, + - SingleHostDiscovery, + - } from '@backstage/backend-common'; + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { CatalogClient } from '@backstage/catalog-client'; + - import { + - CookieCutter, + - CreateReactAppTemplater, + - createRouter, + - Preparers, + - Publishers, + - Templaters, + - } from '@backstage/plugin-scaffolder-backend'; + + import { createRouter } from '@backstage/plugin-scaffolder-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import type { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + config, + database, + reader, + + discovery, + }: PluginEnvironment): Promise { + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + - const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const templaters = new Templaters(); + + - templaters.register('cookiecutter', cookiecutterTemplater); + - templaters.register('cra', craTemplater); + - + - const preparers = await Preparers.fromConfig(config, { logger }); + - const publishers = await Publishers.fromConfig(config, { logger }); + + - const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + - preparers, + - templaters, + - publishers, + + containerRunner, + logger, + config, + database, + + ``` + ## 0.8.4 ### Patch Changes diff --git a/packages/catalog-model/api-report.md b/packages/catalog-model/api-report.md index 5d055566be..74027716de 100644 --- a/packages/catalog-model/api-report.md +++ b/packages/catalog-model/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 { JsonObject } from '@backstage/config'; import { JSONSchema7 } from 'json-schema'; import { JsonValue } from '@backstage/config'; @@ -11,204 +10,222 @@ import { SerializedError } from '@backstage/errors'; import * as yup from 'yup'; // @public @deprecated (undocumented) -export const analyzeLocationSchema: yup.ObjectSchema<{ +export const analyzeLocationSchema: yup.ObjectSchema< + { location: LocationSpec; -}, object>; + }, + object +>; // @public (undocumented) interface ApiEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'API'; - // (undocumented) - spec: { - type: string; - lifecycle: string; - owner: string; - definition: string; - system?: string; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'API'; + // (undocumented) + spec: { + type: string; + lifecycle: string; + owner: string; + definition: string; + system?: string; + }; } - -export { ApiEntityV1alpha1 as ApiEntity } - -export { ApiEntityV1alpha1 } +export { ApiEntityV1alpha1 as ApiEntity }; +export { ApiEntityV1alpha1 }; // @public (undocumented) export const apiEntityV1alpha1Validator: KindValidator; // @public export class CommonValidatorFunctions { - static isJsonSafe(value: unknown): boolean; - static isValidDnsLabel(value: unknown): boolean; - static isValidDnsSubdomain(value: unknown): boolean; - static isValidPrefixAndOrSuffix(value: unknown, separator: string, isValidPrefix: (value: string) => boolean, isValidSuffix: (value: string) => boolean): boolean; - static isValidString(value: unknown): boolean; - static isValidUrl(value: unknown): boolean; + static isJsonSafe(value: unknown): boolean; + static isValidDnsLabel(value: unknown): boolean; + static isValidDnsSubdomain(value: unknown): boolean; + static isValidPrefixAndOrSuffix( + value: unknown, + separator: string, + isValidPrefix: (value: string) => boolean, + isValidSuffix: (value: string) => boolean, + ): boolean; + static isValidString(value: unknown): boolean; + static isValidUrl(value: unknown): boolean; } // @public -export function compareEntityToRef(entity: Entity, ref: EntityRef | EntityName, context?: EntityRefContext): boolean; +export function compareEntityToRef( + entity: Entity, + ref: EntityRef | EntityName, + context?: EntityRefContext, +): boolean; // @public (undocumented) interface ComponentEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'Component'; - // (undocumented) - spec: { - type: string; - lifecycle: string; - owner: string; - subcomponentOf?: string; - providesApis?: string[]; - consumesApis?: string[]; - dependsOn?: string[]; - system?: string; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'Component'; + // (undocumented) + spec: { + type: string; + lifecycle: string; + owner: string; + subcomponentOf?: string; + providesApis?: string[]; + consumesApis?: string[]; + dependsOn?: string[]; + system?: string; + }; } - -export { ComponentEntityV1alpha1 as ComponentEntity } - -export { ComponentEntityV1alpha1 } +export { ComponentEntityV1alpha1 as ComponentEntity }; +export { ComponentEntityV1alpha1 }; // @public (undocumented) export const componentEntityV1alpha1Validator: KindValidator; // @public export class DefaultNamespaceEntityPolicy implements EntityPolicy { - constructor(namespace?: string); - // (undocumented) - enforce(entity: Entity): Promise; - } + constructor(namespace?: string); + // (undocumented) + enforce(entity: Entity): Promise; +} // @public (undocumented) interface DomainEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'Domain'; - // (undocumented) - spec: { - owner: string; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'Domain'; + // (undocumented) + spec: { + owner: string; + }; } - -export { DomainEntityV1alpha1 as DomainEntity } - -export { DomainEntityV1alpha1 } +export { DomainEntityV1alpha1 as DomainEntity }; +export { DomainEntityV1alpha1 }; // @public (undocumented) export const domainEntityV1alpha1Validator: KindValidator; // @public (undocumented) -export const EDIT_URL_ANNOTATION = "backstage.io/edit-url"; +export const EDIT_URL_ANNOTATION = 'backstage.io/edit-url'; // @public export type Entity = { - apiVersion: string; - kind: string; - metadata: EntityMeta; - spec?: JsonObject; - relations?: EntityRelation[]; - status?: UNSTABLE_EntityStatus; + apiVersion: string; + kind: string; + metadata: EntityMeta; + spec?: JsonObject; + relations?: EntityRelation[]; + status?: UNSTABLE_EntityStatus; }; // @public -export const ENTITY_DEFAULT_NAMESPACE = "default"; +export const ENTITY_DEFAULT_NAMESPACE = 'default'; // @public -export const ENTITY_META_GENERATED_FIELDS: readonly ["uid", "etag", "generation"]; +export const ENTITY_META_GENERATED_FIELDS: readonly [ + 'uid', + 'etag', + 'generation', +]; // @public export type EntityEnvelope = { - apiVersion: string; - kind: string; - metadata: { - name: string; - namespace?: string; - }; + apiVersion: string; + kind: string; + metadata: { + name: string; + namespace?: string; + }; }; // @public -export function entityEnvelopeSchemaValidator(schema?: unknown): (data: unknown) => T; +export function entityEnvelopeSchemaValidator< + T extends EntityEnvelope = EntityEnvelope +>(schema?: unknown): (data: unknown) => T; // @public export function entityHasChanges(previous: Entity, next: Entity): boolean; // @public -export function entityKindSchemaValidator(schema: unknown): (data: unknown) => T | false; +export function entityKindSchemaValidator( + schema: unknown, +): (data: unknown) => T | false; // @public export type EntityLink = { - url: string; - title?: string; - icon?: string; + url: string; + title?: string; + icon?: string; }; // @public export type EntityMeta = JsonObject & { - uid?: string; - etag?: string; - generation?: number; - name: string; - namespace?: string; - description?: string; - labels?: Record; - annotations?: Record; - tags?: string[]; - links?: EntityLink[]; + uid?: string; + etag?: string; + generation?: number; + name: string; + namespace?: string; + description?: string; + labels?: Record; + annotations?: Record; + tags?: string[]; + links?: EntityLink[]; }; // @public export type EntityName = { - kind: string; - namespace: string; - name: string; + kind: string; + namespace: string; + name: string; }; // @public (undocumented) export const EntityPolicies: { - allOf(policies: EntityPolicy[]): AllEntityPolicies; - oneOf(policies: EntityPolicy[]): AnyEntityPolicy; + allOf(policies: EntityPolicy[]): AllEntityPolicies; + oneOf(policies: EntityPolicy[]): AnyEntityPolicy; }; // @public export type EntityPolicy = { - enforce(entity: Entity): Promise; + enforce(entity: Entity): Promise; }; // @public -export type EntityRef = string | { - kind?: string; - namespace?: string; - name: string; -}; +export type EntityRef = + | string + | { + kind?: string; + namespace?: string; + name: string; + }; // @public export type EntityRelation = { - type: string; - target: EntityName; + type: string; + target: EntityName; }; // @public export type EntityRelationSpec = { - source: EntityName; - type: string; - target: EntityName; + source: EntityName; + type: string; + target: EntityName; }; // @public -export function entitySchemaValidator(schema?: unknown): (data: unknown) => T; +export function entitySchemaValidator( + schema?: unknown, +): (data: unknown) => T; // @public export class FieldFormatEntityPolicy implements EntityPolicy { - constructor(validators?: Validators); - // (undocumented) - enforce(entity: Entity): Promise; - } + constructor(validators?: Validators); + // (undocumented) + enforce(entity: Entity): Promise; +} // @public export function generateEntityEtag(): string; @@ -223,95 +240,93 @@ export function generateUpdatedEntity(previous: Entity, next: Entity): Entity; export function getEntityName(entity: Entity): EntityName; // @public -export function getEntitySourceLocation(entity: Entity): { - type: string; - target: string; +export function getEntitySourceLocation( + entity: Entity, +): { + type: string; + target: string; }; // @public (undocumented) interface GroupEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'Group'; - // (undocumented) - spec: { - type: string; - profile?: { - displayName?: string; - email?: string; - picture?: string; - }; - parent?: string; - children: string[]; - members?: string[]; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'Group'; + // (undocumented) + spec: { + type: string; + profile?: { + displayName?: string; + email?: string; + picture?: string; }; + parent?: string; + children: string[]; + members?: string[]; + }; } - -export { GroupEntityV1alpha1 as GroupEntity } - -export { GroupEntityV1alpha1 } +export { GroupEntityV1alpha1 as GroupEntity }; +export { GroupEntityV1alpha1 }; // @public (undocumented) export const groupEntityV1alpha1Validator: KindValidator; // @public (undocumented) -export type JSONSchema = JSONSchema7 & { +export type JSONSchema = JSONSchema7 & + { [key in string]?: JsonValue; -}; + }; // @public export type KindValidator = { - check(entity: Entity): Promise; + check(entity: Entity): Promise; }; // @public export class KubernetesValidatorFunctions { - // (undocumented) - static isValidAnnotationKey(value: unknown): boolean; - // (undocumented) - static isValidAnnotationValue(value: unknown): boolean; - // (undocumented) - static isValidApiVersion(value: unknown): boolean; - // (undocumented) - static isValidKind(value: unknown): boolean; - // (undocumented) - static isValidLabelKey(value: unknown): boolean; - // (undocumented) - static isValidLabelValue(value: unknown): boolean; - // (undocumented) - static isValidNamespace(value: unknown): boolean; - // (undocumented) - static isValidObjectName(value: unknown): boolean; + // (undocumented) + static isValidAnnotationKey(value: unknown): boolean; + // (undocumented) + static isValidAnnotationValue(value: unknown): boolean; + // (undocumented) + static isValidApiVersion(value: unknown): boolean; + // (undocumented) + static isValidKind(value: unknown): boolean; + // (undocumented) + static isValidLabelKey(value: unknown): boolean; + // (undocumented) + static isValidLabelValue(value: unknown): boolean; + // (undocumented) + static isValidNamespace(value: unknown): boolean; + // (undocumented) + static isValidObjectName(value: unknown): boolean; } // @public (undocumented) type Location_2 = { - id: string; + id: string; } & LocationSpec; - -export { Location_2 as Location } +export { Location_2 as Location }; // @public (undocumented) -export const LOCATION_ANNOTATION = "backstage.io/managed-by-location"; +export const LOCATION_ANNOTATION = 'backstage.io/managed-by-location'; // @public (undocumented) interface LocationEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'Location'; - // (undocumented) - spec: { - type?: string; - target?: string; - targets?: string[]; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'Location'; + // (undocumented) + spec: { + type?: string; + target?: string; + targets?: string[]; + }; } - -export { LocationEntityV1alpha1 as LocationEntity } - -export { LocationEntityV1alpha1 } +export { LocationEntityV1alpha1 as LocationEntity }; +export { LocationEntityV1alpha1 }; // @public (undocumented) export const locationEntityV1alpha1Validator: KindValidator; @@ -321,9 +336,9 @@ export const locationSchema: yup.ObjectSchema; // @public (undocumented) export type LocationSpec = { - type: string; - target: string; - presence?: 'optional' | 'required'; + type: string; + target: string; + presence?: 'optional' | 'required'; }; // @public @deprecated (undocumented) @@ -334,190 +349,209 @@ export function makeValidator(overrides?: Partial): Validators; // @public export class NoForeignRootFieldsEntityPolicy implements EntityPolicy { - constructor(knownFields?: string[]); - // (undocumented) - enforce(entity: Entity): Promise; - } + constructor(knownFields?: string[]); + // (undocumented) + enforce(entity: Entity): Promise; +} // @public (undocumented) -export const ORIGIN_LOCATION_ANNOTATION = "backstage.io/managed-by-origin-location"; +export const ORIGIN_LOCATION_ANNOTATION = + 'backstage.io/managed-by-origin-location'; // @public -export function parseEntityName(ref: EntityRef, context?: EntityRefContext): EntityName; +export function parseEntityName( + ref: EntityRef, + context?: EntityRefContext, +): EntityName; // @public -export function parseEntityRef(ref: EntityRef, context?: { +export function parseEntityRef( + ref: EntityRef, + context?: { defaultKind: string; defaultNamespace: string; -}): { - kind: string; - namespace: string; - name: string; + }, +): { + kind: string; + namespace: string; + name: string; }; // @public (undocumented) -export function parseEntityRef(ref: EntityRef, context?: { +export function parseEntityRef( + ref: EntityRef, + context?: { defaultKind: string; -}): { - kind: string; - namespace?: string; - name: string; + }, +): { + kind: string; + namespace?: string; + name: string; }; // @public (undocumented) -export function parseEntityRef(ref: EntityRef, context?: { +export function parseEntityRef( + ref: EntityRef, + context?: { defaultNamespace: string; -}): { - kind?: string; - namespace: string; - name: string; + }, +): { + kind?: string; + namespace: string; + name: string; }; // @public -export function parseLocationReference(ref: string): { - type: string; - target: string; +export function parseLocationReference( + ref: string, +): { + type: string; + target: string; }; // @public (undocumented) -export const RELATION_API_CONSUMED_BY = "apiConsumedBy"; +export const RELATION_API_CONSUMED_BY = 'apiConsumedBy'; // @public (undocumented) -export const RELATION_API_PROVIDED_BY = "apiProvidedBy"; +export const RELATION_API_PROVIDED_BY = 'apiProvidedBy'; // @public (undocumented) -export const RELATION_CHILD_OF = "childOf"; +export const RELATION_CHILD_OF = 'childOf'; // @public -export const RELATION_CONSUMES_API = "consumesApi"; +export const RELATION_CONSUMES_API = 'consumesApi'; // @public (undocumented) -export const RELATION_DEPENDENCY_OF = "dependencyOf"; +export const RELATION_DEPENDENCY_OF = 'dependencyOf'; // @public -export const RELATION_DEPENDS_ON = "dependsOn"; +export const RELATION_DEPENDS_ON = 'dependsOn'; // @public (undocumented) -export const RELATION_HAS_MEMBER = "hasMember"; +export const RELATION_HAS_MEMBER = 'hasMember'; // @public (undocumented) -export const RELATION_HAS_PART = "hasPart"; +export const RELATION_HAS_PART = 'hasPart'; // @public -export const RELATION_MEMBER_OF = "memberOf"; +export const RELATION_MEMBER_OF = 'memberOf'; // @public -export const RELATION_OWNED_BY = "ownedBy"; +export const RELATION_OWNED_BY = 'ownedBy'; // @public (undocumented) -export const RELATION_OWNER_OF = "ownerOf"; +export const RELATION_OWNER_OF = 'ownerOf'; // @public -export const RELATION_PARENT_OF = "parentOf"; +export const RELATION_PARENT_OF = 'parentOf'; // @public -export const RELATION_PART_OF = "partOf"; +export const RELATION_PART_OF = 'partOf'; // @public (undocumented) -export const RELATION_PROVIDES_API = "providesApi"; +export const RELATION_PROVIDES_API = 'providesApi'; // @public (undocumented) interface ResourceEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'Resource'; - // (undocumented) - spec: { - type: string; - owner: string; - dependsOn?: string[]; - system?: string; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'Resource'; + // (undocumented) + spec: { + type: string; + owner: string; + dependsOn?: string[]; + system?: string; + }; } - -export { ResourceEntityV1alpha1 as ResourceEntity } - -export { ResourceEntityV1alpha1 } +export { ResourceEntityV1alpha1 as ResourceEntity }; +export { ResourceEntityV1alpha1 }; // @public (undocumented) export const resourceEntityV1alpha1Validator: KindValidator; // @public export class SchemaValidEntityPolicy implements EntityPolicy { - // (undocumented) - enforce(entity: Entity): Promise; - } + // (undocumented) + enforce(entity: Entity): Promise; +} // @public @deprecated -export function serializeEntityRef(ref: Entity | { - kind?: string; - namespace?: string; - name: string; -}): EntityRef; +export function serializeEntityRef( + ref: + | Entity + | { + kind?: string; + namespace?: string; + name: string; + }, +): EntityRef; // @public (undocumented) -export const SOURCE_LOCATION_ANNOTATION = "backstage.io/source-location"; +export const SOURCE_LOCATION_ANNOTATION = 'backstage.io/source-location'; // @public -export function stringifyEntityRef(ref: Entity | { - kind: string; - namespace?: string; - name: string; -}): string; +export function stringifyEntityRef( + ref: + | Entity + | { + kind: string; + namespace?: string; + name: string; + }, +): string; // @public export function stringifyLocationReference(ref: { - type: string; - target: string; + type: string; + target: string; }): string; // @public (undocumented) interface SystemEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'System'; - // (undocumented) - spec: { - owner: string; - domain?: string; - }; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'System'; + // (undocumented) + spec: { + owner: string; + domain?: string; + }; } - -export { SystemEntityV1alpha1 as SystemEntity } - -export { SystemEntityV1alpha1 } +export { SystemEntityV1alpha1 as SystemEntity }; +export { SystemEntityV1alpha1 }; // @public (undocumented) export const systemEntityV1alpha1Validator: KindValidator; // @public (undocumented) export interface TemplateEntityV1beta2 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1beta2'; - // (undocumented) - kind: 'Template'; - // (undocumented) - metadata: EntityMeta & { - title?: string; - }; - // (undocumented) - spec: { - type: string; - parameters?: JsonObject | JsonObject[]; - steps: Array<{ - id?: string; - name?: string; - action: string; - input?: JsonObject; - if?: string | boolean; - }>; - output?: { - [name: string]: string; - }; - owner?: string; + // (undocumented) + apiVersion: 'backstage.io/v1beta2'; + // (undocumented) + kind: 'Template'; + // (undocumented) + metadata: EntityMeta & { + title?: string; + }; + // (undocumented) + spec: { + type: string; + parameters?: JsonObject | JsonObject[]; + steps: Array<{ + id?: string; + name?: string; + action: string; + input?: JsonObject; + if?: string | boolean; + }>; + output?: { + [name: string]: string; }; + owner?: string; + }; } // @public (undocumented) @@ -525,15 +559,15 @@ export const templateEntityV1beta2Validator: KindValidator; // @alpha export type UNSTABLE_EntityStatus = { - items?: UNSTABLE_EntityStatusItem[]; + items?: UNSTABLE_EntityStatusItem[]; }; // @alpha export type UNSTABLE_EntityStatusItem = { - type: string; - level: UNSTABLE_EntityStatusLevel; - message: string; - error?: SerializedError; + type: string; + level: UNSTABLE_EntityStatusLevel; + message: string; + error?: SerializedError; }; // @alpha @@ -541,45 +575,41 @@ export type UNSTABLE_EntityStatusLevel = 'info' | 'warning' | 'error'; // @public (undocumented) interface UserEntityV1alpha1 extends Entity { - // (undocumented) - apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; - // (undocumented) - kind: 'User'; - // (undocumented) - spec: { - profile?: { - displayName?: string; - email?: string; - picture?: string; - }; - memberOf: string[]; + // (undocumented) + apiVersion: 'backstage.io/v1alpha1' | 'backstage.io/v1beta1'; + // (undocumented) + kind: 'User'; + // (undocumented) + spec: { + profile?: { + displayName?: string; + email?: string; + picture?: string; }; + memberOf: string[]; + }; } - -export { UserEntityV1alpha1 as UserEntity } - -export { UserEntityV1alpha1 } +export { UserEntityV1alpha1 as UserEntity }; +export { UserEntityV1alpha1 }; // @public (undocumented) export const userEntityV1alpha1Validator: KindValidator; // @public (undocumented) export type Validators = { - isValidApiVersion(value: unknown): boolean; - isValidKind(value: unknown): boolean; - isValidEntityName(value: unknown): boolean; - isValidNamespace(value: unknown): boolean; - isValidLabelKey(value: unknown): boolean; - isValidLabelValue(value: unknown): boolean; - isValidAnnotationKey(value: unknown): boolean; - isValidAnnotationValue(value: unknown): boolean; - isValidTag(value: unknown): boolean; + isValidApiVersion(value: unknown): boolean; + isValidKind(value: unknown): boolean; + isValidEntityName(value: unknown): boolean; + isValidNamespace(value: unknown): boolean; + isValidLabelKey(value: unknown): boolean; + isValidLabelValue(value: unknown): boolean; + isValidAnnotationKey(value: unknown): boolean; + isValidAnnotationValue(value: unknown): boolean; + isValidTag(value: unknown): boolean; }; // @public -export const VIEW_URL_ANNOTATION = "backstage.io/view-url"; - +export const VIEW_URL_ANNOTATION = 'backstage.io/view-url'; // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/catalog-model/package.json b/packages/catalog-model/package.json index 1ddf1b89ae..13c3214252 100644 --- a/packages/catalog-model/package.json +++ b/packages/catalog-model/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/catalog-model", - "version": "0.8.4", + "version": "0.9.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/packages/cli/package.json b/packages/cli/package.json index c9bde05bd7..a7032879bc 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -77,7 +77,7 @@ "express": "^4.17.1", "file-loader": "^6.2.0", "fork-ts-checker-webpack-plugin": "^6.2.9", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "handlebars": "^4.7.3", "html-webpack-plugin": "^4.3.0", "inquirer": "^7.0.4", diff --git a/packages/codemods/CHANGELOG.md b/packages/codemods/CHANGELOG.md index d5291222d8..dc1f694dca 100644 --- a/packages/codemods/CHANGELOG.md +++ b/packages/codemods/CHANGELOG.md @@ -1,5 +1,12 @@ # @backstage/codemods +## 0.1.5 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + ## 0.1.4 ### Patch Changes diff --git a/packages/codemods/package.json b/packages/codemods/package.json index 2b557685bd..9cf5730126 100644 --- a/packages/codemods/package.json +++ b/packages/codemods/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/codemods", "description": "A collection of codemods for Backstage projects", - "version": "0.1.4", + "version": "0.1.5", "private": false, "publishConfig": { "access": "public", diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index a09311b786..ffd15b0c9a 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -3,15 +3,17 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/config'; import { JSONSchema7 } from 'json-schema'; // @public export type ConfigSchema = { - process(appConfigs: AppConfig[], options?: ConfigProcessingOptions): AppConfig[]; - serialize(): JsonObject; + process( + appConfigs: AppConfig[], + options?: ConfigProcessingOptions, + ): AppConfig[]; + serialize(): JsonObject; }; // @public @@ -22,10 +24,10 @@ export function loadConfig(options: LoadConfigOptions): Promise; // @public (undocumented) export type LoadConfigOptions = { - configRoot: string; - configPaths: string[]; - env?: string; - experimentalEnvFunc?: EnvFunc; + configRoot: string; + configPaths: string[]; + env?: string; + experimentalEnvFunc?: EnvFunc; }; // @public @@ -36,10 +38,8 @@ export function mergeConfigSchemas(schemas: JSONSchema7[]): JSONSchema7; // @public export function readEnvConfig(env: { - [name: string]: string | undefined; + [name: string]: string | undefined; }): AppConfig[]; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index d5a4f600fd..cff324484a 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -34,7 +34,7 @@ "@backstage/config": "^0.1.5", "@types/json-schema": "^7.0.6", "ajv": "^7.0.3", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "json-schema": "^0.3.0", "json-schema-merge-allof": "^0.8.1", "typescript-json-schema": "^0.50.1", diff --git a/packages/config/api-report.md b/packages/config/api-report.md index be250778ec..0c243aab48 100644 --- a/packages/config/api-report.md +++ b/packages/config/api-report.md @@ -3,79 +3,82 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - // @public (undocumented) export type AppConfig = { - context: string; - data: JsonObject; + context: string; + data: JsonObject; }; // @public (undocumented) export type Config = { - has(key: string): boolean; - keys(): string[]; - get(key?: string): T; - getOptional(key?: string): T | undefined; - getConfig(key: string): Config; - getOptionalConfig(key: string): Config | undefined; - getConfigArray(key: string): Config[]; - getOptionalConfigArray(key: string): Config[] | undefined; - getNumber(key: string): number; - getOptionalNumber(key: string): number | undefined; - getBoolean(key: string): boolean; - getOptionalBoolean(key: string): boolean | undefined; - getString(key: string): string; - getOptionalString(key: string): string | undefined; - getStringArray(key: string): string[]; - getOptionalStringArray(key: string): string[] | undefined; + has(key: string): boolean; + keys(): string[]; + get(key?: string): T; + getOptional(key?: string): T | undefined; + getConfig(key: string): Config; + getOptionalConfig(key: string): Config | undefined; + getConfigArray(key: string): Config[]; + getOptionalConfigArray(key: string): Config[] | undefined; + getNumber(key: string): number; + getOptionalNumber(key: string): number | undefined; + getBoolean(key: string): boolean; + getOptionalBoolean(key: string): boolean | undefined; + getString(key: string): string; + getOptionalString(key: string): string | undefined; + getStringArray(key: string): string[]; + getOptionalStringArray(key: string): string[] | undefined; }; // @public (undocumented) export class ConfigReader implements Config { - constructor(data: JsonObject | undefined, context?: string, fallback?: ConfigReader | undefined, prefix?: string); - // (undocumented) - static fromConfigs(configs: AppConfig[]): ConfigReader; - // (undocumented) - get(key?: string): T; - // (undocumented) - getBoolean(key: string): boolean; - // (undocumented) - getConfig(key: string): ConfigReader; - // (undocumented) - getConfigArray(key: string): ConfigReader[]; - // (undocumented) - getNumber(key: string): number; - // (undocumented) - getOptional(key?: string): T | undefined; - // (undocumented) - getOptionalBoolean(key: string): boolean | undefined; - // (undocumented) - getOptionalConfig(key: string): ConfigReader | undefined; - // (undocumented) - getOptionalConfigArray(key: string): ConfigReader[] | undefined; - // (undocumented) - getOptionalNumber(key: string): number | undefined; - // (undocumented) - getOptionalString(key: string): string | undefined; - // (undocumented) - getOptionalStringArray(key: string): string[] | undefined; - // (undocumented) - getString(key: string): string; - // (undocumented) - getStringArray(key: string): string[]; - // (undocumented) - has(key: string): boolean; - // (undocumented) - keys(): string[]; - } - -// @public (undocumented) -export interface JsonArray extends Array { + constructor( + data: JsonObject | undefined, + context?: string, + fallback?: ConfigReader | undefined, + prefix?: string, + ); + // (undocumented) + static fromConfigs(configs: AppConfig[]): ConfigReader; + // (undocumented) + get(key?: string): T; + // (undocumented) + getBoolean(key: string): boolean; + // (undocumented) + getConfig(key: string): ConfigReader; + // (undocumented) + getConfigArray(key: string): ConfigReader[]; + // (undocumented) + getNumber(key: string): number; + // (undocumented) + getOptional(key?: string): T | undefined; + // (undocumented) + getOptionalBoolean(key: string): boolean | undefined; + // (undocumented) + getOptionalConfig(key: string): ConfigReader | undefined; + // (undocumented) + getOptionalConfigArray(key: string): ConfigReader[] | undefined; + // (undocumented) + getOptionalNumber(key: string): number | undefined; + // (undocumented) + getOptionalString(key: string): string | undefined; + // (undocumented) + getOptionalStringArray(key: string): string[] | undefined; + // (undocumented) + getString(key: string): string; + // (undocumented) + getStringArray(key: string): string[]; + // (undocumented) + has(key: string): boolean; + // (undocumented) + keys(): string[]; } +// @public (undocumented) +export interface JsonArray extends Array {} + // @public (undocumented) export type JsonObject = { - [key in string]?: JsonValue; + [key in string]?: JsonValue; }; // @public (undocumented) @@ -84,7 +87,5 @@ export type JsonPrimitive = number | string | boolean | null; // @public (undocumented) export type JsonValue = JsonObject | JsonArray | JsonPrimitive; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/core-app-api/api-report.md b/packages/core-app-api/api-report.md index 8cb18cf1ce..af86f042a0 100644 --- a/packages/core-app-api/api-report.md +++ b/packages/core-app-api/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 { AlertApi } from '@backstage/core-plugin-api'; import { AlertMessage } from '@backstage/core-plugin-api'; import { AnyApiFactory } from '@backstage/core-plugin-api'; @@ -57,72 +56,97 @@ import { SubRouteRef } from '@backstage/core-plugin-api'; // @public export class AlertApiForwarder implements AlertApi { - // (undocumented) - alert$(): Observable; - // (undocumented) - post(alert: AlertMessage): void; - } + // (undocumented) + alert$(): Observable; + // (undocumented) + post(alert: AlertMessage): void; +} // @public (undocumented) export type ApiFactoryHolder = { - get(api: ApiRef): ApiFactory | undefined; + get( + api: ApiRef, + ): + | ApiFactory< + T, + T, + { + [key in string]: unknown; + } + > + | undefined; }; // @public export class ApiFactoryRegistry implements ApiFactoryHolder { - // (undocumented) - get(api: ApiRef): ApiFactory | undefined; - // (undocumented) - getAllApis(): Set; - register(scope: ApiFactoryScope, factory: ApiFactory): boolean; + // (undocumented) + get( + api: ApiRef, + ): + | ApiFactory< + T, + T, + { + [x: string]: unknown; + } + > + | undefined; + // (undocumented) + getAllApis(): Set; + register< + Api, + Impl extends Api, + Deps extends { + [name in string]: unknown; + } + >(scope: ApiFactoryScope, factory: ApiFactory): boolean; } // @public (undocumented) export const ApiProvider: { - ({ apis, children, }: PropsWithChildren): JSX.Element; - propTypes: { - apis: PropTypes.Validator any>; - }>>; - children: PropTypes.Requireable; - }; + ({ apis, children }: PropsWithChildren): JSX.Element; + propTypes: { + apis: PropTypes.Validator< + PropTypes.InferProps<{ + get: PropTypes.Validator<(...args: any[]) => any>; + }> + >; + children: PropTypes.Requireable; + }; }; // @public (undocumented) export class ApiRegistry implements ApiHolder { - constructor(apis: Map); - // (undocumented) - static builder(): ApiRegistryBuilder; - // (undocumented) - static from(apis: ApiImpl[]): ApiRegistry; - // (undocumented) - get(api: ApiRef): T | undefined; - static with(api: ApiRef, impl: T): ApiRegistry; - with(api: ApiRef, impl: T): ApiRegistry; + constructor(apis: Map); + // (undocumented) + static builder(): ApiRegistryBuilder; + // (undocumented) + static from(apis: ApiImpl[]): ApiRegistry; + // (undocumented) + get(api: ApiRef): T | undefined; + static with(api: ApiRef, impl: T): ApiRegistry; + with(api: ApiRef, impl: T): ApiRegistry; } // @public (undocumented) export class ApiResolver implements ApiHolder { - constructor(factories: ApiFactoryHolder); - // (undocumented) - get(ref: ApiRef): T | undefined; - static validateFactories(factories: ApiFactoryHolder, apis: Iterable): void; + constructor(factories: ApiFactoryHolder); + // (undocumented) + get(ref: ApiRef): T | undefined; + static validateFactories( + factories: ApiFactoryHolder, + apis: Iterable, + ): void; } // @public (undocumented) export type AppComponents = { - NotFoundErrorPage: ComponentType<{}>; - BootErrorPage: ComponentType; - Progress: ComponentType<{}>; - Router: ComponentType<{}>; - ErrorBoundaryFallback: ComponentType; - SignInPage?: ComponentType; + NotFoundErrorPage: ComponentType<{}>; + BootErrorPage: ComponentType; + Progress: ComponentType<{}>; + Router: ComponentType<{}>; + ErrorBoundaryFallback: ComponentType; + SignInPage?: ComponentType; }; // @public @@ -130,72 +154,88 @@ export type AppConfigLoader = () => Promise; // @public (undocumented) export type AppContext = { - getPlugins(): BackstagePlugin[]; - getSystemIcon(key: string): IconComponent | undefined; - getComponents(): AppComponents; + getPlugins(): BackstagePlugin[]; + getSystemIcon(key: string): IconComponent | undefined; + getComponents(): AppComponents; }; // @public (undocumented) export type AppOptions = { - apis?: Iterable; - icons?: Partial & { - [key in string]: IconComponent; + apis?: Iterable; + icons?: Partial & + { + [key in string]: IconComponent; }; - plugins?: BackstagePluginWithAnyOutput[]; - components?: Partial; - themes?: AppTheme[]; - configLoader?: AppConfigLoader; - bindRoutes?(context: { - bind: AppRouteBinder; - }): void; + plugins?: BackstagePluginWithAnyOutput[]; + components?: Partial; + themes?: AppTheme[]; + configLoader?: AppConfigLoader; + bindRoutes?(context: { bind: AppRouteBinder }): void; }; // @public (undocumented) -export type AppRouteBinder = (externalRoutes: ExternalRoutes, targetRoutes: PartialKeys, KeysWithType>>) => void; + } +>( + externalRoutes: ExternalRoutes, + targetRoutes: PartialKeys< + TargetRouteMap, + KeysWithType> + >, +) => void; // @public (undocumented) export class AppThemeSelector implements AppThemeApi { - constructor(themes: AppTheme[]); - // (undocumented) - activeThemeId$(): Observable; - // (undocumented) - static createWithStorage(themes: AppTheme[]): AppThemeSelector; - // (undocumented) - getActiveThemeId(): string | undefined; - // (undocumented) - getInstalledThemes(): AppTheme[]; - // (undocumented) - setActiveThemeId(themeId?: string): void; - } + constructor(themes: AppTheme[]); + // (undocumented) + activeThemeId$(): Observable; + // (undocumented) + static createWithStorage(themes: AppTheme[]): AppThemeSelector; + // (undocumented) + getActiveThemeId(): string | undefined; + // (undocumented) + getInstalledThemes(): AppTheme[]; + // (undocumented) + setActiveThemeId(themeId?: string): void; +} // @public (undocumented) export class Auth0Auth { - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, defaultScopes, }: OAuthApiCreateOptions): typeof auth0AuthApiRef.T; + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + defaultScopes, + }: OAuthApiCreateOptions): typeof auth0AuthApiRef.T; } // @public (undocumented) export type BackstageApp = { - getPlugins(): BackstagePlugin[]; - getSystemIcon(key: string): IconComponent | undefined; - getProvider(): ComponentType<{}>; - getRouter(): ComponentType<{}>; + getPlugins(): BackstagePlugin[]; + getSystemIcon(key: string): IconComponent | undefined; + getProvider(): ComponentType<{}>; + getRouter(): ComponentType<{}>; }; // @public (undocumented) -export type BackstagePluginWithAnyOutput = Omit, 'output'> & { - output(): (PluginOutput | UnknownPluginOutput)[]; +export type BackstagePluginWithAnyOutput = Omit< + BackstagePlugin, + 'output' +> & { + output(): (PluginOutput | UnknownPluginOutput)[]; }; // @public (undocumented) export type BootErrorPageProps = { - step: 'load-config' | 'load-chunk'; - error: Error; + step: 'load-config' | 'load-chunk'; + error: Error; }; -export { ConfigReader } +export { ConfigReader }; // @public export function createApp(options?: AppOptions): PrivateAppImpl; @@ -205,36 +245,36 @@ export const defaultConfigLoader: AppConfigLoader; // @public export class ErrorAlerter implements ErrorApi { - constructor(alertApi: AlertApi, errorApi: ErrorApi); - // (undocumented) - error$(): Observable<{ - error: { - name: string; - message: string; - stack?: string | undefined; - }; - context?: ErrorContext | undefined; - }>; - // (undocumented) - post(error: Error, context?: ErrorContext): void; + constructor(alertApi: AlertApi, errorApi: ErrorApi); + // (undocumented) + error$(): Observable<{ + error: { + name: string; + message: string; + stack?: string | undefined; + }; + context?: ErrorContext | undefined; + }>; + // (undocumented) + post(error: Error, context?: ErrorContext): void; } // @public export class ErrorApiForwarder implements ErrorApi { - // (undocumented) - error$(): Observable<{ - error: Error; - context?: ErrorContext; - }>; - // (undocumented) - post(error: Error, context?: ErrorContext): void; - } + // (undocumented) + error$(): Observable<{ + error: Error; + context?: ErrorContext; + }>; + // (undocumented) + post(error: Error, context?: ErrorContext): void; +} // @public (undocumented) export type ErrorBoundaryFallbackProps = { - plugin?: BackstagePlugin; - error: Error; - resetError: () => void; + plugin?: BackstagePlugin; + error: Error; + resetError: () => void; }; // @public (undocumented) @@ -242,191 +282,254 @@ export const FeatureFlagged: (props: FeatureFlaggedProps) => JSX.Element; // @public (undocumented) export type FeatureFlaggedProps = { - children: ReactNode; -} & ({ - with: string; -} | { - without: string; -}); + children: ReactNode; +} & ( + | { + with: string; + } + | { + without: string; + } +); // @public (undocumented) export const FlatRoutes: (props: FlatRoutesProps) => JSX.Element | null; // @public (undocumented) export class GithubAuth implements OAuthApi, SessionApi { - constructor(sessionManager: SessionManager); - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, defaultScopes, }: OAuthApiCreateOptions): GithubAuth; - // (undocumented) - getAccessToken(scope?: string, options?: AuthRequestOptions): Promise; - // (undocumented) - getBackstageIdentity(options?: AuthRequestOptions): Promise; - // (undocumented) - getProfile(options?: AuthRequestOptions): Promise; - // (undocumented) - static normalizeScope(scope?: string): Set; - // (undocumented) - sessionState$(): Observable; - // (undocumented) - signIn(): Promise; - // (undocumented) - signOut(): Promise; + constructor(sessionManager: SessionManager); + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + defaultScopes, + }: OAuthApiCreateOptions): GithubAuth; + // (undocumented) + getAccessToken(scope?: string, options?: AuthRequestOptions): Promise; + // (undocumented) + getBackstageIdentity( + options?: AuthRequestOptions, + ): Promise; + // (undocumented) + getProfile(options?: AuthRequestOptions): Promise; + // (undocumented) + static normalizeScope(scope?: string): Set; + // (undocumented) + sessionState$(): Observable; + // (undocumented) + signIn(): Promise; + // (undocumented) + signOut(): Promise; } // @public (undocumented) export type GithubSession = { - providerInfo: { - accessToken: string; - scopes: Set; - expiresAt: Date; - }; - profile: ProfileInfo; - backstageIdentity: BackstageIdentity; + providerInfo: { + accessToken: string; + scopes: Set; + expiresAt: Date; + }; + profile: ProfileInfo; + backstageIdentity: BackstageIdentity; }; // @public (undocumented) export class GitlabAuth { - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, defaultScopes, }: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T; + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + defaultScopes, + }: OAuthApiCreateOptions): typeof gitlabAuthApiRef.T; } // @public (undocumented) export class GoogleAuth { - // (undocumented) - static create({ discoveryApi, oauthRequestApi, environment, provider, defaultScopes, }: OAuthApiCreateOptions): typeof googleAuthApiRef.T; + // (undocumented) + static create({ + discoveryApi, + oauthRequestApi, + environment, + provider, + defaultScopes, + }: OAuthApiCreateOptions): typeof googleAuthApiRef.T; } // @public export class LocalStorageFeatureFlags implements FeatureFlagsApi { - // (undocumented) - getRegisteredFlags(): FeatureFlag[]; - // (undocumented) - isActive(name: string): boolean; - // (undocumented) - registerFlag(flag: FeatureFlag): void; - // (undocumented) - save(options: FeatureFlagsSaveOptions): void; + // (undocumented) + getRegisteredFlags(): FeatureFlag[]; + // (undocumented) + isActive(name: string): boolean; + // (undocumented) + registerFlag(flag: FeatureFlag): void; + // (undocumented) + save(options: FeatureFlagsSaveOptions): void; } // @public (undocumented) export class MicrosoftAuth { - // (undocumented) - static create({ environment, provider, oauthRequestApi, discoveryApi, defaultScopes, }: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T; + // (undocumented) + static create({ + environment, + provider, + oauthRequestApi, + discoveryApi, + defaultScopes, + }: OAuthApiCreateOptions): typeof microsoftAuthApiRef.T; } // @public (undocumented) -export class OAuth2 implements OAuthApi, OpenIdConnectApi, ProfileInfoApi, BackstageIdentityApi, SessionApi { - constructor(options: Options); - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, defaultScopes, scopeTransform, }: CreateOptions): OAuth2; - // (undocumented) - getAccessToken(scope?: string | string[], options?: AuthRequestOptions): Promise; - // (undocumented) - getBackstageIdentity(options?: AuthRequestOptions): Promise; - // (undocumented) - getIdToken(options?: AuthRequestOptions): Promise; - // (undocumented) - getProfile(options?: AuthRequestOptions): Promise; - // (undocumented) - sessionState$(): Observable; - // (undocumented) - signIn(): Promise; - // (undocumented) - signOut(): Promise; +export class OAuth2 + implements + OAuthApi, + OpenIdConnectApi, + ProfileInfoApi, + BackstageIdentityApi, + SessionApi { + constructor(options: Options); + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + defaultScopes, + scopeTransform, + }: CreateOptions): OAuth2; + // (undocumented) + getAccessToken( + scope?: string | string[], + options?: AuthRequestOptions, + ): Promise; + // (undocumented) + getBackstageIdentity( + options?: AuthRequestOptions, + ): Promise; + // (undocumented) + getIdToken(options?: AuthRequestOptions): Promise; + // (undocumented) + getProfile(options?: AuthRequestOptions): Promise; + // (undocumented) + sessionState$(): Observable; + // (undocumented) + signIn(): Promise; + // (undocumented) + signOut(): Promise; } // @public (undocumented) export type OAuth2Session = { - providerInfo: { - idToken: string; - accessToken: string; - scopes: Set; - expiresAt: Date; - }; - profile: ProfileInfo; - backstageIdentity: BackstageIdentity; + providerInfo: { + idToken: string; + accessToken: string; + scopes: Set; + expiresAt: Date; + }; + profile: ProfileInfo; + backstageIdentity: BackstageIdentity; }; // @public export class OAuthRequestManager implements OAuthRequestApi { - // (undocumented) - authRequest$(): Observable; - // (undocumented) - createAuthRequester(options: AuthRequesterOptions): AuthRequester; - } + // (undocumented) + authRequest$(): Observable; + // (undocumented) + createAuthRequester(options: AuthRequesterOptions): AuthRequester; +} // @public (undocumented) export class OktaAuth { - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, defaultScopes, }: OAuthApiCreateOptions): typeof oktaAuthApiRef.T; + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + defaultScopes, + }: OAuthApiCreateOptions): typeof oktaAuthApiRef.T; } // @public (undocumented) export class OneLoginAuth { - // (undocumented) - static create({ discoveryApi, environment, provider, oauthRequestApi, }: CreateOptions_2): typeof oneloginAuthApiRef.T; + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + oauthRequestApi, + }: CreateOptions_2): typeof oneloginAuthApiRef.T; } // @public (undocumented) -export class SamlAuth implements ProfileInfoApi, BackstageIdentityApi, SessionApi { - constructor(sessionManager: SessionManager); - // (undocumented) - static create({ discoveryApi, environment, provider, }: AuthApiCreateOptions): SamlAuth; - // (undocumented) - getBackstageIdentity(options?: AuthRequestOptions): Promise; - // (undocumented) - getProfile(options?: AuthRequestOptions): Promise; - // (undocumented) - sessionState$(): Observable; - // (undocumented) - signIn(): Promise; - // (undocumented) - signOut(): Promise; +export class SamlAuth + implements ProfileInfoApi, BackstageIdentityApi, SessionApi { + constructor(sessionManager: SessionManager); + // (undocumented) + static create({ + discoveryApi, + environment, + provider, + }: AuthApiCreateOptions): SamlAuth; + // (undocumented) + getBackstageIdentity( + options?: AuthRequestOptions, + ): Promise; + // (undocumented) + getProfile(options?: AuthRequestOptions): Promise; + // (undocumented) + sessionState$(): Observable; + // (undocumented) + signIn(): Promise; + // (undocumented) + signOut(): Promise; } // @public (undocumented) export type SignInPageProps = { - onResult(result: SignInResult): void; + onResult(result: SignInResult): void; }; // @public (undocumented) export type SignInResult = { - userId: string; - profile: ProfileInfo; - getIdToken?: () => Promise; - signOut?: () => Promise; + userId: string; + profile: ProfileInfo; + getIdToken?: () => Promise; + signOut?: () => Promise; }; // @public (undocumented) export class UnhandledErrorForwarder { - static forward(errorApi: ErrorApi, errorContext: ErrorContext): void; + static forward(errorApi: ErrorApi, errorContext: ErrorContext): void; } // @public export class UrlPatternDiscovery implements DiscoveryApi { - static compile(pattern: string): UrlPatternDiscovery; - // (undocumented) - getBaseUrl(pluginId: string): Promise; - } + static compile(pattern: string): UrlPatternDiscovery; + // (undocumented) + getBaseUrl(pluginId: string): Promise; +} // @public (undocumented) export class WebStorage implements StorageApi { - constructor(namespace: string, errorApi: ErrorApi); - // (undocumented) - static create(options: CreateStorageApiOptions): WebStorage; - // (undocumented) - forBucket(name: string): WebStorage; - // (undocumented) - get(key: string): T | undefined; - // (undocumented) - observe$(key: string): Observable>; - // (undocumented) - remove(key: string): Promise; - // (undocumented) - set(key: string, data: T): Promise; - } - + constructor(namespace: string, errorApi: ErrorApi); + // (undocumented) + static create(options: CreateStorageApiOptions): WebStorage; + // (undocumented) + forBucket(name: string): WebStorage; + // (undocumented) + get(key: string): T | undefined; + // (undocumented) + observe$(key: string): Observable>; + // (undocumented) + remove(key: string): Promise; + // (undocumented) + set(key: string, data: T): Promise; +} // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/core-components/CHANGELOG.md b/packages/core-components/CHANGELOG.md index 7f3bc09225..1536cf3fb2 100644 --- a/packages/core-components/CHANGELOG.md +++ b/packages/core-components/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/core-components +## 0.1.5 + +### Patch Changes + +- a446bffdb: Improve UX of the Sidebar by adding SidebarScrollWrapper component allowing the user to scroll through Plugins & Shortcuts on smaller screens. Prevent the Sidebar from opening on click on small devices +- f11e50ea7: - Enhanced core `Button` component to open external links in new tab. + - Replaced the use of `Button` component from material by `core-components` in tools card. +- 76bb7aeda: Show scroll bar of the sidebar wrapper only on hover +- 2a13aa1b7: Handle empty code blocks in markdown files so they don't fail rendering +- 47748c7e6: Fix error in error panel, and console warnings about DOM nesting pre inside p +- 34352a79c: Add edit button to Group Profile Card +- 612e25fd7: Add custom styles to scroll bar of the sidebar wrapper to fix flaky behaviour + ## 0.1.4 ### Patch Changes diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md new file mode 100644 index 0000000000..3d8e60d4fb --- /dev/null +++ b/packages/core-components/api-report.md @@ -0,0 +1,2411 @@ +## API Report File for "@backstage/core-components" + +> 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 { BackstageIdentityApi } from '@backstage/core-plugin-api'; +import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; +import { ButtonProps } from '@material-ui/core'; +import { ButtonTypeMap } from '@material-ui/core'; +import { CardHeaderProps } from '@material-ui/core'; +import { Column } from 'material-table'; +import { CommonProps } from '@material-ui/core/OverridableComponent'; +import { ComponentClass } from 'react'; +import { ComponentProps } from 'react'; +import { ComponentType } from 'react'; +import { Context } from 'react'; +import { default as CSS_2 } from 'csstype'; +import { CSSProperties } from 'react'; +import { default as dagre_2 } from 'dagre'; +import { ElementType } from 'react'; +import { ErrorInfo } from 'react'; +import { IconComponent } from '@backstage/core-plugin-api'; +import { LinearProgressProps } from '@material-ui/core'; +import { LinkProps as LinkProps_2 } from '@material-ui/core'; +import { LinkProps as LinkProps_3 } from 'react-router-dom'; +import { MaterialTableProps } from 'material-table'; +import { NavLinkProps } from 'react-router-dom'; +import { ProfileInfoApi } from '@backstage/core-plugin-api'; +import { PropsWithChildren } from 'react'; +import PropTypes from 'prop-types'; +import { default as React_2 } from 'react'; +import * as React_3 from 'react'; +import { ReactElement } from 'react'; +import { ReactNode } from 'react'; +import { SessionApi } from '@backstage/core-plugin-api'; +import { SignInPageProps } from '@backstage/core-plugin-api'; +import { SparklinesLineProps } from 'react-sparklines'; +import { SparklinesProps } from 'react-sparklines'; +import { StyledComponentProps } from '@material-ui/core'; +import { StyleRules } from '@material-ui/styles'; +import { TabProps } from '@material-ui/core'; +import { TextTruncateProps } from 'react-text-truncate'; +import { Theme } from '@material-ui/core'; +import { TooltipProps } from '@material-ui/core'; +import { WithStyles } from '@material-ui/core'; + +// @public (undocumented) +export const AlertDisplay: () => JSX.Element | null; + +// @public (undocumented) +enum Alignment { + // (undocumented) + DOWN_LEFT = 'DL', + // (undocumented) + DOWN_RIGHT = 'DR', + // (undocumented) + UP_LEFT = 'UL', + // (undocumented) + UP_RIGHT = 'UR', +} + +// @public (undocumented) +export const Avatar: ({ + displayName, + picture, + customStyles, +}: AvatarProps) => JSX.Element; + +// @public (undocumented) +export const Breadcrumbs: ({ children, ...props }: Props_25) => JSX.Element; + +// @public (undocumented) +export const BrokenImageIcon: IconComponent; + +// @public +export const Button: React_2.ForwardRefExoticComponent< + Pick< + Props, + | 'replace' + | 'media' + | 'hidden' + | 'dir' + | 'form' + | 'slot' + | 'title' + | 'disabled' + | 'color' + | 'size' + | 'underline' + | 'display' + | 'translate' + | 'prefix' + | 'children' + | 'key' + | 'value' + | 'id' + | 'name' + | 'action' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | 'component' + | 'variant' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'rel' + | 'target' + | 'type' + | 'referrerPolicy' + | 'disableElevation' + | 'fullWidth' + | 'startIcon' + | 'endIcon' + | 'noWrap' + | 'gutterBottom' + | 'paragraph' + | 'autoFocus' + | 'formAction' + | 'formEncType' + | 'formMethod' + | 'formNoValidate' + | 'formTarget' + | 'disableFocusRipple' + | 'buttonRef' + | 'centerRipple' + | 'disableRipple' + | 'disableTouchRipple' + | 'focusRipple' + | 'focusVisibleClassName' + | 'onFocusVisible' + | 'TouchRippleProps' + | 'align' + | 'variantMapping' + | 'to' + | 'state' + | 'TypographyClasses' + | keyof CommonProps> + > & + React_2.RefAttributes +>; + +// @public (undocumented) +export const CardTab: ({ + children, + ...props +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const CatalogIcon: IconComponent; + +// @public (undocumented) +export const ChatIcon: IconComponent; + +// @public (undocumented) +export const CodeSnippet: ({ + text, + language, + showLineNumbers, + showCopyCodeButton, + highlightedNumbers, + customStyle, +}: Props_2) => JSX.Element; + +// @public (undocumented) +export const Content: ({ + className, + stretch, + noPadding, + children, + ...props +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const ContentHeader: ({ + description, + title, + titleComponent: TitleComponent, + children, + textAlign, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const CopyTextButton: { + (props: Props_3): JSX.Element; + propTypes: { + text: PropTypes.Validator; + tooltipDelay: PropTypes.Requireable; + tooltipText: PropTypes.Requireable; + }; +}; + +// @public (undocumented) +export const DashboardIcon: IconComponent; + +// @public (undocumented) +type DependencyEdge = T & { + from: string; + to: string; + label?: string; +}; + +// @public (undocumented) +export function DependencyGraph({ + edges, + nodes, + renderNode, + direction, + align, + nodeMargin, + edgeMargin, + rankMargin, + paddingX, + paddingY, + acyclicer, + ranker, + labelPosition, + labelOffset, + edgeRanks, + edgeWeight, + renderLabel, + defs, + ...svgProps +}: DependencyGraphProps): JSX.Element; + +declare namespace DependencyGraphTypes { + export { + DependencyEdge, + GraphEdge, + RenderLabelProps, + RenderLabelFunction, + DependencyNode, + GraphNode, + RenderNodeProps, + RenderNodeFunction, + EdgeProperties, + Direction, + Alignment, + Ranker, + LabelPosition, + }; +} +export { DependencyGraphTypes }; + +// @public (undocumented) +type DependencyNode = T & { + id: string; +}; + +// @public (undocumented) +enum Direction { + // (undocumented) + BOTTOM_TOP = 'BT', + // (undocumented) + LEFT_RIGHT = 'LR', + // (undocumented) + RIGHT_LEFT = 'RL', + // (undocumented) + TOP_BOTTOM = 'TB', +} + +// @public (undocumented) +export const DismissableBanner: ({ + variant, + message, + id, + fixed, +}: Props_4) => JSX.Element; + +// @public (undocumented) +export const DocsIcon: IconComponent; + +// @public (undocumented) +type EdgeProperties = { + label?: string; + width?: number; + height?: number; + labeloffset?: number; + labelpos?: LabelPosition; + minlen?: number; + weight?: number; + [customKey: string]: any; +}; + +// @public (undocumented) +export const EmailIcon: IconComponent; + +// @public (undocumented) +export const EmptyState: ({ + title, + description, + missing, + action, +}: Props_5) => JSX.Element; + +// @public (undocumented) +export const ErrorBoundary: ComponentClass; + +// @public (undocumented) +export type ErrorBoundaryProps = { + slackChannel?: string | SlackChannel; + onError?: (error: Error, errorInfo: string) => null; +}; + +// @public (undocumented) +export const ErrorPage: ({ + status, + statusMessage, + additionalInfo, +}: IErrorPageProps) => JSX.Element; + +// @public +export const ErrorPanel: ({ + title, + error, + defaultExpanded, + children, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export type ErrorPanelProps = { + error: Error; + defaultExpanded?: boolean; + title?: string; +}; + +// @public (undocumented) +export const FeatureCalloutCircular: ({ + featureId, + title, + description, + children, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const Gauge: (props: Props_14) => JSX.Element; + +// @public (undocumented) +export const GaugeCard: (props: Props_13) => JSX.Element; + +// @public (undocumented) +export const GitHubIcon: IconComponent; + +// @public (undocumented) +type GraphEdge = DependencyEdge & + dagre_2.GraphEdge & + EdgeProperties; + +// @public (undocumented) +type GraphNode = dagre_2.Node>; + +// @public (undocumented) +export const GroupIcon: IconComponent; + +// @public (undocumented) +export const Header: ({ + children, + pageTitleOverride, + style, + subtitle, + title, + tooltip, + type, + typeLink, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const HeaderIconLinkRow: ({ links }: Props_8) => JSX.Element; + +// @public (undocumented) +export const HeaderLabel: ({ + label, + value, + url, +}: HeaderLabelProps) => JSX.Element; + +// @public (undocumented) +export const HeaderTabs: ({ + tabs, + onChange, + selectedIndex, +}: HeaderTabsProps) => JSX.Element; + +// @public (undocumented) +export const HelpIcon: IconComponent; + +// @public (undocumented) +export const HomepageTimer: () => JSX.Element | null; + +// @public (undocumented) +export const HorizontalScrollGrid: ( + props: PropsWithChildren, +) => JSX.Element; + +// @public (undocumented) +export type IconLinkVerticalProps = { + color?: 'primary' | 'secondary'; + disabled?: boolean; + href?: string; + icon?: React_2.ReactNode; + label: string; + onClick?: React_2.MouseEventHandler; + title?: string; +}; + +// @public (undocumented) +export const InfoCard: ({ + title, + subheader, + divider, + deepLink, + slackChannel, + errorBoundaryProps, + variant, + children, + headerStyle, + headerProps, + action, + actionsClassName, + actions, + cardClassName, + actionsTopRight, + className, + noPadding, + titleTypographyProps, +}: Props_20) => JSX.Element; + +// @public (undocumented) +export type InfoCardVariants = 'flex' | 'fullHeight' | 'gridItem'; + +// @public (undocumented) +export const IntroCard: (props: IntroCardProps) => JSX.Element; + +// @public @deprecated +export const ItemCard: ({ + description, + tags, + title, + type, + subtitle, + label, + onClick, + href, +}: ItemCardProps) => JSX.Element; + +// @public +export const ItemCardGrid: (props: ItemCardGridProps) => JSX.Element; + +// @public (undocumented) +export type ItemCardGridProps = Partial> & { + children?: React_2.ReactNode; +}; + +// @public +export const ItemCardHeader: (props: ItemCardHeaderProps) => JSX.Element; + +// @public (undocumented) +export type ItemCardHeaderProps = Partial> & { + title?: React_2.ReactNode; + subtitle?: React_2.ReactNode; + children?: React_2.ReactNode; +}; + +// @public (undocumented) +enum LabelPosition { + // (undocumented) + CENTER = 'c', + // (undocumented) + LEFT = 'l', + // (undocumented) + RIGHT = 'r', +} + +// @public (undocumented) +export const Lifecycle: (props: Props_10) => JSX.Element; + +// @public (undocumented) +export const LinearGauge: ({ value }: Props_15) => JSX.Element | null; + +// @public +export const Link: React_2.ForwardRefExoticComponent< + Pick< + LinkProps, + | 'replace' + | 'media' + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'underline' + | 'display' + | 'translate' + | 'prefix' + | 'children' + | 'key' + | 'id' + | 'classes' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'className' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | 'component' + | 'variant' + | 'innerRef' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'rel' + | 'target' + | 'type' + | 'referrerPolicy' + | 'noWrap' + | 'gutterBottom' + | 'paragraph' + | 'align' + | 'variantMapping' + | 'to' + | 'state' + | 'TypographyClasses' + > & + React_2.RefAttributes +>; + +// @public (undocumented) +export type LinkProps = LinkProps_2 & + LinkProps_3 & { + component?: ElementType; + }; + +// @public +export const MarkdownContent: ({ content, dialect }: Props_11) => JSX.Element; + +// @public (undocumented) +export const MissingAnnotationEmptyState: ({ + annotation, +}: Props_6) => JSX.Element; + +// @public (undocumented) +export const OAuthRequestDialog: () => JSX.Element; + +// @public (undocumented) +export const OverflowTooltip: (props: Props_12) => JSX.Element; + +// @public (undocumented) +export const Page: ({ + themeId, + children, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const Progress: ( + props: PropsWithChildren, +) => JSX.Element; + +// @public (undocumented) +enum Ranker { + // (undocumented) + LONGEST_PATH = 'longest-path', + // (undocumented) + NETWORK_SIMPLEX = 'network-simplex', + // (undocumented) + TIGHT_TREE = 'tight-tree', +} + +// @public (undocumented) +type RenderLabelFunction = (props: RenderLabelProps) => React.ReactNode; + +// @public (undocumented) +type RenderLabelProps = { + edge: DependencyEdge; +}; + +// @public (undocumented) +type RenderNodeFunction = (props: RenderNodeProps) => React.ReactNode; + +// @public (undocumented) +type RenderNodeProps = { + node: DependencyNode; +}; + +// @public +export const ResponseErrorPanel: ({ + title, + error, + defaultExpanded, +}: ErrorPanelProps) => JSX.Element; + +// @public (undocumented) +export const RoutedTabs: ({ routes }: { routes: SubRoute_2[] }) => JSX.Element; + +// @public (undocumented) +export const Select: ({ + multiple, + items, + label, + placeholder, + selected, + onChange, + triggerReset, +}: SelectProps) => JSX.Element; + +// @public (undocumented) +export const Sidebar: ({ + openDelayMs, + closeDelayMs, + children, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const SIDEBAR_INTRO_LOCAL_STORAGE = + '@backstage/core/sidebar-intro-dismissed'; + +// @public (undocumented) +export const sidebarConfig: { + drawerWidthClosed: number; + drawerWidthOpen: number; + defaultOpenDelayMs: number; + defaultCloseDelayMs: number; + defaultFadeDuration: number; + logoHeight: number; + iconContainerWidth: number; + iconSize: number; + iconPadding: number; + selectedIndicatorWidth: number; + userBadgePadding: number; + userBadgeDiameter: number; +}; + +// @public (undocumented) +export const SidebarContext: Context; + +// @public (undocumented) +export type SidebarContextType = { + isOpen: boolean; +}; + +// @public (undocumented) +export const SidebarDivider: React_2.ComponentType< + Pick< + React_2.DetailedHTMLProps< + React_2.HTMLAttributes, + HTMLHRElement + >, + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'translate' + | 'prefix' + | 'children' + | 'id' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | keyof React_2.ClassAttributes + > & + StyledComponentProps<'root'> & { + className?: string | undefined; + } +>; + +// @public (undocumented) +export const SidebarIntro: () => JSX.Element | null; + +// @public (undocumented) +export const SidebarItem: React_2.ForwardRefExoticComponent< + SidebarItemProps & React_2.RefAttributes +>; + +// @public (undocumented) +export const SidebarPage: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const SidebarPinStateContext: React_2.Context; + +// @public (undocumented) +export type SidebarPinStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; +}; + +// @public (undocumented) +export const SidebarScrollWrapper: React_2.ComponentType< + Pick< + React_2.DetailedHTMLProps< + React_2.HTMLAttributes, + HTMLDivElement + >, + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'translate' + | 'prefix' + | 'children' + | 'id' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | keyof React_2.ClassAttributes + > & + StyledComponentProps<'root'> & { + className?: string | undefined; + } +>; + +// @public (undocumented) +export const SidebarSearchField: ( + props: SidebarSearchFieldProps, +) => JSX.Element; + +// @public (undocumented) +export const SidebarSpace: React_2.ComponentType< + Pick< + React_2.DetailedHTMLProps< + React_2.HTMLAttributes, + HTMLDivElement + >, + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'translate' + | 'prefix' + | 'children' + | 'id' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | keyof React_2.ClassAttributes + > & + StyledComponentProps<'root'> & { + className?: string | undefined; + } +>; + +// @public (undocumented) +export const SidebarSpacer: React_2.ComponentType< + Pick< + React_2.DetailedHTMLProps< + React_2.HTMLAttributes, + HTMLDivElement + >, + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'translate' + | 'prefix' + | 'children' + | 'id' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | keyof React_2.ClassAttributes + > & + StyledComponentProps<'root'> & { + className?: string | undefined; + } +>; + +// @public (undocumented) +export const SignInPage: (props: Props_23) => JSX.Element; + +// @public (undocumented) +export type SignInProviderConfig = { + id: string; + title: string; + message: string; + apiRef: ApiRef; +}; + +// @public (undocumented) +export const SimpleStepper: ({ + children, + elevated, + onStepChange, + activeStep, +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const SimpleStepperStep: ({ + title, + children, + end, + actions, + ...muiProps +}: PropsWithChildren) => JSX.Element; + +// @public (undocumented) +export const StatusAborted: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StatusError: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StatusOK: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StatusPending: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StatusRunning: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StatusWarning: (props: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export const StructuredMetadataTable: ({ + metadata, + dense, + options, +}: Props_16) => JSX.Element; + +// @public (undocumented) +export const SubvalueCell: ({ + value, + subvalue, +}: SubvalueCellProps) => JSX.Element; + +// @public (undocumented) +export const SupportButton: ({ + title, + children, +}: SupportButtonProps) => JSX.Element; + +// @public (undocumented) +export type SupportConfig = { + url: string; + items: SupportItem[]; +}; + +// @public (undocumented) +export type SupportItem = { + title: string; + icon?: string; + links: SupportItemLink[]; +}; + +// @public (undocumented) +export type SupportItemLink = { + url: string; + title: string; +}; + +// @public (undocumented) +export type Tab = { + id: string; + label: string; + tabProps?: TabProps< + React_2.ElementType, + { + component?: React_2.ElementType; + } + >; +}; + +// @public (undocumented) +export const TabbedCard: ({ + slackChannel, + errorBoundaryProps, + children, + title, + deepLink, + value, + onChange, +}: PropsWithChildren) => JSX.Element; + +// @public +export const TabbedLayout: { + ({ children }: PropsWithChildren<{}>): JSX.Element; + Route: (props: SubRoute) => null; +}; + +// @public (undocumented) +export function Table({ + columns, + options, + title, + subtitle, + filters, + initialState, + emptyContent, + onStateChange, + ...props +}: TableProps): JSX.Element; + +// @public (undocumented) +export interface TableColumn extends Column { + // (undocumented) + highlight?: boolean; + // (undocumented) + width?: string; +} + +// @public (undocumented) +export type TableFilter = { + column: string; + type: 'select' | 'multiple-select' | 'checkbox-tree'; +}; + +// @public (undocumented) +export interface TableProps + extends MaterialTableProps { + // (undocumented) + columns: TableColumn[]; + // (undocumented) + emptyContent?: ReactNode; + // (undocumented) + filters?: TableFilter[]; + // (undocumented) + initialState?: TableState; + // (undocumented) + onStateChange?: (state: TableState) => any; + // (undocumented) + subtitle?: string; +} + +// @public (undocumented) +export type TableState = { + search?: string; + filtersOpen?: boolean; + filters?: SelectedFilters; +}; + +// @public (undocumented) +export const Tabs: ({ tabs }: TabsProps) => JSX.Element; + +// @public (undocumented) +export const TrendLine: ( + props: SparklinesProps & + Pick & { + title?: string; + }, +) => JSX.Element | null; + +// @public (undocumented) +export function useQueryParamState( + stateName: string, + debounceTime?: number, +): [T | undefined, SetQueryParams]; + +// @public (undocumented) +export const UserIcon: IconComponent; + +// @public (undocumented) +export function useSupportConfig(): SupportConfig; + +// @public (undocumented) +export const WarningIcon: IconComponent; + +// @public +export const WarningPanel: (props: Props_17) => JSX.Element; + +// (No @packageDocumentation comment for this package) +``` diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 1c2e160bd5..b9d514d074 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.1.4", + "version": "0.1.5", "private": false, "publishConfig": { "access": "public", diff --git a/packages/core-plugin-api/api-report.md b/packages/core-plugin-api/api-report.md index 90ea1f5e46..f3cb134fe1 100644 --- a/packages/core-plugin-api/api-report.md +++ b/packages/core-plugin-api/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 { BackstageTheme } from '@backstage/theme'; import { ComponentType } from 'react'; @@ -14,8 +15,8 @@ import { SvgIconProps } from '@material-ui/core'; // @public export type AlertApi = { - post(alert: AlertMessage): void; - alert$(): Observable; + post(alert: AlertMessage): void; + alert$(): Observable; }; // @public (undocumented) @@ -23,43 +24,53 @@ export const alertApiRef: ApiRef; // @public (undocumented) export type AlertMessage = { - message: string; - severity?: 'success' | 'info' | 'warning' | 'error'; + message: string; + severity?: 'success' | 'info' | 'warning' | 'error'; }; // @public (undocumented) -export type AnyApiFactory = ApiFactory; + } +>; // @public (undocumented) export type AnyApiRef = ApiRef; // @public (undocumented) -export type ApiFactory = { - api: ApiRef; - deps: TypesToApiRefs; - factory(deps: Deps): Impl; + } +> = { + api: ApiRef; + deps: TypesToApiRefs; + factory(deps: Deps): Impl; }; // @public (undocumented) export type ApiHolder = { - get(api: ApiRef): T | undefined; + get(api: ApiRef): T | undefined; }; // @public (undocumented) export type ApiRef = { - id: string; - T: T; + id: string; + T: T; }; // @public (undocumented) -export type ApiRefsToTypes; -}> = { - [key in keyof T]: ApiRefType; + } +> = { + [key in keyof T]: ApiRefType; }; // @public (undocumented) @@ -67,93 +78,106 @@ export type ApiRefType = T extends ApiRef ? U : never; // @public (undocumented) export type AppComponents = { - NotFoundErrorPage: ComponentType<{}>; - BootErrorPage: ComponentType; - Progress: ComponentType<{}>; - Router: ComponentType<{}>; - ErrorBoundaryFallback: ComponentType; - SignInPage?: ComponentType; + NotFoundErrorPage: ComponentType<{}>; + BootErrorPage: ComponentType; + Progress: ComponentType<{}>; + Router: ComponentType<{}>; + ErrorBoundaryFallback: ComponentType; + SignInPage?: ComponentType; }; // @public (undocumented) export type AppContext = { - getPlugins(): BackstagePlugin[]; - getSystemIcon(key: string): IconComponent | undefined; - getComponents(): AppComponents; + getPlugins(): BackstagePlugin[]; + getSystemIcon(key: string): IconComponent | undefined; + getComponents(): AppComponents; }; // @public export type AppTheme = { - id: string; - title: string; - variant: 'light' | 'dark'; - theme: BackstageTheme; - icon?: React.ReactElement; + id: string; + title: string; + variant: 'light' | 'dark'; + theme: BackstageTheme; + icon?: React.ReactElement; }; // @public export type AppThemeApi = { - getInstalledThemes(): AppTheme[]; - activeThemeId$(): Observable; - getActiveThemeId(): string | undefined; - setActiveThemeId(themeId?: string): void; + getInstalledThemes(): AppTheme[]; + activeThemeId$(): Observable; + getActiveThemeId(): string | undefined; + setActiveThemeId(themeId?: string): void; }; // @public (undocumented) export const appThemeApiRef: ApiRef; // @public (undocumented) -export function attachComponentData

(component: ComponentType

, type: string, data: unknown): void; +export function attachComponentData

( + component: ComponentType

, + type: string, + data: unknown, +): void; // @public -export const auth0AuthApiRef: ApiRef; +export const auth0AuthApiRef: ApiRef< + OpenIdConnectApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +>; // @public export type AuthProvider = { - title: string; - icon: IconComponent; + title: string; + icon: IconComponent; }; // @public -export type AuthRequester = (scopes: Set) => Promise; +export type AuthRequester = ( + scopes: Set, +) => Promise; // @public export type AuthRequesterOptions = { - provider: AuthProvider; - onAuthRequest(scopes: Set): Promise; + provider: AuthProvider; + onAuthRequest(scopes: Set): Promise; }; // @public (undocumented) export type AuthRequestOptions = { - optional?: boolean; - instantPopup?: boolean; + optional?: boolean; + instantPopup?: boolean; }; // @public (undocumented) export type BackstageIdentity = { - id: string; - idToken: string; + id: string; + idToken: string; }; // @public export type BackstageIdentityApi = { - getBackstageIdentity(options?: AuthRequestOptions): Promise; + getBackstageIdentity( + options?: AuthRequestOptions, + ): Promise; }; // @public (undocumented) -export type BackstagePlugin = { - getId(): string; - output(): PluginOutput[]; - getApis(): Iterable; - provide(extension: Extension): T; - routes: Routes; - externalRoutes: ExternalRoutes; +export type BackstagePlugin< + Routes extends AnyRoutes = {}, + ExternalRoutes extends AnyExternalRoutes = {} +> = { + getId(): string; + output(): PluginOutput[]; + getApis(): Iterable; + provide(extension: Extension): T; + routes: Routes; + externalRoutes: ExternalRoutes; }; // @public (undocumented) export type BootErrorPageProps = { - step: 'load-config' | 'load-chunk'; - error: Error; + step: 'load-config' | 'load-chunk'; + error: Error; }; // @public @@ -163,66 +187,89 @@ export type ConfigApi = Config; export const configApiRef: ApiRef; // @public -export function createApiFactory(factory: ApiFactory): ApiFactory; + } +>(factory: ApiFactory): ApiFactory; // @public (undocumented) -export function createApiFactory(api: ApiRef, instance: Impl): ApiFactory; +export function createApiFactory( + api: ApiRef, + instance: Impl, +): ApiFactory; // @public (undocumented) export function createApiRef(config: ApiRefConfig): ApiRef; // @public (undocumented) -export function createComponentExtension JSX.Element | null>(options: { - component: ComponentLoader; -}): Extension; +export function createComponentExtension< + T extends (props: any) => JSX.Element | null +>(options: { component: ComponentLoader }): Extension; // @public (undocumented) -export function createExternalRouteRef(options: { - id: string; - params?: ParamKey[]; - optional?: Optional; + }, + Optional extends boolean = false, + ParamKey extends string = never +>(options: { + id: string; + params?: ParamKey[]; + optional?: Optional; }): ExternalRouteRef, Optional>; // @public (undocumented) -export function createPlugin(config: PluginConfig): BackstagePlugin; +export function createPlugin< + Routes extends AnyRoutes = {}, + ExternalRoutes extends AnyExternalRoutes = {} +>( + config: PluginConfig, +): BackstagePlugin; // @public (undocumented) -export function createReactExtension JSX.Element | null>(options: { - component: ComponentLoader; - data?: Record; +export function createReactExtension< + T extends (props: any) => JSX.Element | null +>(options: { + component: ComponentLoader; + data?: Record; }): Extension; // @public (undocumented) -export function createRoutableExtension JSX.Element | null>(options: { - component: () => Promise; - mountPoint: RouteRef; -}): Extension; +export function createRoutableExtension< + T extends (props: any) => JSX.Element | null +>(options: { component: () => Promise; mountPoint: RouteRef }): Extension; // @public (undocumented) -export function createRouteRef(config: { - id?: string; - params?: ParamKey[]; - path?: string; - icon?: OldIconComponent; - title?: string; + }, + ParamKey extends string = never +>(config: { + id?: string; + params?: ParamKey[]; + path?: string; + icon?: OldIconComponent; + title?: string; }): RouteRef>; // @public (undocumented) -export function createSubRouteRef(config: { - id: string; - path: Path; - parent: RouteRef; +export function createSubRouteRef< + Path extends string, + ParentParams extends AnyParams = never +>(config: { + id: string; + path: Path; + parent: RouteRef; }): MakeSubRouteRef, ParentParams>; // @public export type DiscoveryApi = { - getBaseUrl(pluginId: string): Promise; + getBaseUrl(pluginId: string): Promise; }; // @public (undocumented) @@ -230,25 +277,25 @@ export const discoveryApiRef: ApiRef; // @public export interface ElementCollection { - findComponentData(query: { - key: string; - }): T[]; - getElements(): Array>; - selectByComponentData(query: { - key: string; - withStrictError?: string; - }): ElementCollection; + findComponentData(query: { key: string }): T[]; + getElements< + Props extends { + [name: string]: unknown; + } + >(): Array>; + selectByComponentData(query: { + key: string; + withStrictError?: string; + }): ElementCollection; } // @public export type ErrorApi = { - post(error: Error_2, context?: ErrorContext): void; - error$(): Observable<{ - error: Error_2; - context?: ErrorContext; - }>; + post(error: Error_2, context?: ErrorContext): void; + error$(): Observable<{ + error: Error_2; + context?: ErrorContext; + }>; }; // @public (undocumented) @@ -256,46 +303,49 @@ export const errorApiRef: ApiRef; // @public (undocumented) export type ErrorBoundaryFallbackProps = { - plugin?: BackstagePlugin; - error: Error; - resetError: () => void; + plugin?: BackstagePlugin; + error: Error; + resetError: () => void; }; // @public export type ErrorContext = { - hidden?: boolean; + hidden?: boolean; }; // @public (undocumented) export type Extension = { - expose(plugin: BackstagePlugin): T; + expose(plugin: BackstagePlugin): T; }; // @public (undocumented) -export type ExternalRouteRef = { - readonly [routeRefType]: 'external'; - params: ParamKeys; - optional?: Optional; +export type ExternalRouteRef< + Params extends AnyParams = any, + Optional extends boolean = any +> = { + readonly [routeRefType]: 'external'; + params: ParamKeys; + optional?: Optional; }; // @public export type FeatureFlag = { - name: string; - pluginId: string; + name: string; + pluginId: string; }; // @public (undocumented) export type FeatureFlagOutput = { - type: 'feature-flag'; - name: string; + type: 'feature-flag'; + name: string; }; // @public (undocumented) export interface FeatureFlagsApi { - getRegisteredFlags(): FeatureFlag[]; - isActive(name: string): boolean; - registerFlag(flag: FeatureFlag): void; - save(options: FeatureFlagsSaveOptions): void; + getRegisteredFlags(): FeatureFlag[]; + isActive(name: string): boolean; + registerFlag(flag: FeatureFlag): void; + save(options: FeatureFlagsSaveOptions): void; } // @public (undocumented) @@ -303,66 +353,96 @@ export const featureFlagsApiRef: ApiRef; // @public (undocumented) export type FeatureFlagsHooks = { - register(name: string): void; + register(name: string): void; }; // @public export type FeatureFlagsSaveOptions = { - states: Record; - merge?: boolean; + states: Record; + merge?: boolean; }; // @public (undocumented) export enum FeatureFlagState { - // (undocumented) - Active = 1, - // (undocumented) - None = 0 + // (undocumented) + Active = 1, + // (undocumented) + None = 0, } // @public (undocumented) -export function getComponentData(node: ReactNode, type: string): T | undefined; +export function getComponentData( + node: ReactNode, + type: string, +): T | undefined; // @public -export const githubAuthApiRef: ApiRef; +export const githubAuthApiRef: ApiRef< + OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +>; // @public -export const gitlabAuthApiRef: ApiRef; +export const gitlabAuthApiRef: ApiRef< + OAuthApi & ProfileInfoApi & BackstageIdentityApi & SessionApi +>; // @public -export const googleAuthApiRef: ApiRef; +export const googleAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public export type IconComponent = ComponentType<{ - fontSize?: 'default' | 'small' | 'large'; + fontSize?: 'default' | 'small' | 'large'; }>; // @public export type IdentityApi = { - getUserId(): string; - getProfile(): ProfileInfo; - getIdToken(): Promise; - signOut(): Promise; + getUserId(): string; + getProfile(): ProfileInfo; + getIdToken(): Promise; + signOut(): Promise; }; // @public (undocumented) export const identityApiRef: ApiRef; // @public -export const microsoftAuthApiRef: ApiRef; +export const microsoftAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public -export const oauth2ApiRef: ApiRef; +export const oauth2ApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public export type OAuthApi = { - getAccessToken(scope?: OAuthScope, options?: AuthRequestOptions): Promise; + getAccessToken( + scope?: OAuthScope, + options?: AuthRequestOptions, + ): Promise; }; // @public export type OAuthRequestApi = { - createAuthRequester(options: AuthRequesterOptions): AuthRequester; - authRequest$(): Observable; + createAuthRequester( + options: AuthRequesterOptions, + ): AuthRequester; + authRequest$(): Observable; }; // @public (undocumented) @@ -373,51 +453,76 @@ export type OAuthScope = string | string[]; // @public export type Observable = { - [Symbol.observable](): Observable; - subscribe(observer: Observer): Subscription; - subscribe(onNext?: (value: T) => void, onError?: (error: Error) => void, onComplete?: () => void): Subscription; + [Symbol.observable](): Observable; + subscribe(observer: Observer): Subscription; + subscribe( + onNext?: (value: T) => void, + onError?: (error: Error) => void, + onComplete?: () => void, + ): Subscription; }; // @public export type Observer = { - next?(value: T): void; - error?(error: Error): void; - complete?(): void; + next?(value: T): void; + error?(error: Error): void; + complete?(): void; }; // @public -export const oidcAuthApiRef: ApiRef; +export const oidcAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public -export const oktaAuthApiRef: ApiRef; +export const oktaAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public (undocumented) -export const oneloginAuthApiRef: ApiRef; +export const oneloginAuthApiRef: ApiRef< + OAuthApi & + OpenIdConnectApi & + ProfileInfoApi & + BackstageIdentityApi & + SessionApi +>; // @public export type OpenIdConnectApi = { - getIdToken(options?: AuthRequestOptions): Promise; + getIdToken(options?: AuthRequestOptions): Promise; }; // @public export type PendingAuthRequest = { - provider: AuthProvider; - reject: () => void; - trigger(): Promise; + provider: AuthProvider; + reject: () => void; + trigger(): Promise; }; // @public (undocumented) -export type PluginConfig = { - id: string; - apis?: Iterable; - register?(hooks: PluginHooks): void; - routes?: Routes; - externalRoutes?: ExternalRoutes; +export type PluginConfig< + Routes extends AnyRoutes, + ExternalRoutes extends AnyExternalRoutes +> = { + id: string; + apis?: Iterable; + register?(hooks: PluginHooks): void; + routes?: Routes; + externalRoutes?: ExternalRoutes; }; // @public (undocumented) export type PluginHooks = { - featureFlags: FeatureFlagsHooks; + featureFlags: FeatureFlagsHooks; }; // @public (undocumented) @@ -425,19 +530,19 @@ export type PluginOutput = FeatureFlagOutput; // @public export type ProfileInfo = { - email?: string; - displayName?: string; - picture?: string; + email?: string; + displayName?: string; + picture?: string; }; // @public export type ProfileInfoApi = { - getProfile(options?: AuthRequestOptions): Promise; + getProfile(options?: AuthRequestOptions): Promise; }; // @public (undocumented) export type RouteOptions = { - exact?: boolean; + exact?: boolean; }; // @public (undocumented) @@ -445,51 +550,53 @@ export type RoutePath = string; // @public (undocumented) export type RouteRef = { - readonly [routeRefType]: 'absolute'; - params: ParamKeys; - path: string; - icon?: OldIconComponent; - title?: string; + readonly [routeRefType]: 'absolute'; + params: ParamKeys; + path: string; + icon?: OldIconComponent; + title?: string; }; // @public -export const samlAuthApiRef: ApiRef; +export const samlAuthApiRef: ApiRef< + ProfileInfoApi & BackstageIdentityApi & SessionApi +>; // @public export type SessionApi = { - signIn(): Promise; - signOut(): Promise; - sessionState$(): Observable; + signIn(): Promise; + signOut(): Promise; + sessionState$(): Observable; }; // @public export enum SessionState { - // (undocumented) - SignedIn = "SignedIn", - // (undocumented) - SignedOut = "SignedOut" + // (undocumented) + SignedIn = 'SignedIn', + // (undocumented) + SignedOut = 'SignedOut', } // @public (undocumented) export type SignInPageProps = { - onResult(result: SignInResult): void; + onResult(result: SignInResult): void; }; // @public (undocumented) export type SignInResult = { - userId: string; - profile: ProfileInfo; - getIdToken?: () => Promise; - signOut?: () => Promise; + userId: string; + profile: ProfileInfo; + getIdToken?: () => Promise; + signOut?: () => Promise; }; // @public (undocumented) export interface StorageApi { - forBucket(name: string): StorageApi; - get(key: string): T | undefined; - observe$(key: string): Observable>; - remove(key: string): Promise; - set(key: string, data: any): Promise; + forBucket(name: string): StorageApi; + get(key: string): T | undefined; + observe$(key: string): Observable>; + remove(key: string): Promise; + set(key: string, data: any): Promise; } // @public (undocumented) @@ -497,27 +604,27 @@ export const storageApiRef: ApiRef; // @public (undocumented) export type StorageValueChange = { - key: string; - newValue?: T; + key: string; + newValue?: T; }; // @public (undocumented) export type SubRouteRef = { - readonly [routeRefType]: 'sub'; - parent: RouteRef; - path: string; - params: ParamKeys; + readonly [routeRefType]: 'sub'; + parent: RouteRef; + path: string; + params: ParamKeys; }; // @public export type Subscription = { - unsubscribe(): void; - readonly closed: boolean; + unsubscribe(): void; + readonly closed: boolean; }; // @public (undocumented) export type TypesToApiRefs = { - [key in keyof T]: ApiRef; + [key in keyof T]: ApiRef; }; // @public (undocumented) @@ -530,27 +637,39 @@ export function useApiHolder(): ApiHolder; export const useApp: () => AppContext; // @public -export function useElementFilter(node: ReactNode, filterFn: (arg: ElementCollection) => T, dependencies?: any[]): T; +export function useElementFilter( + node: ReactNode, + filterFn: (arg: ElementCollection) => T, + dependencies?: any[], +): T; // @public (undocumented) export type UserFlags = {}; // @public (undocumented) -export function useRouteRef(routeRef: ExternalRouteRef): Optional extends true ? RouteFunc | undefined : RouteFunc; +export function useRouteRef( + routeRef: ExternalRouteRef, +): Optional extends true ? RouteFunc | undefined : RouteFunc; // @public (undocumented) -export function useRouteRef(routeRef: RouteRef | SubRouteRef): RouteFunc; +export function useRouteRef( + routeRef: RouteRef | SubRouteRef, +): RouteFunc; // @public (undocumented) -export function useRouteRefParams(_routeRef: RouteRef | SubRouteRef): Params; +export function useRouteRefParams( + _routeRef: RouteRef | SubRouteRef, +): Params; // @public (undocumented) -export function withApis(apis: TypesToApiRefs):

(WrappedComponent: React_2.ComponentType

) => { - (props: React_2.PropsWithChildren>): JSX.Element; - displayName: string; +export function withApis( + apis: TypesToApiRefs, +):

( + WrappedComponent: React_2.ComponentType

, +) => { + (props: React_2.PropsWithChildren>): JSX.Element; + displayName: string; }; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 5a5949b53a..91863f51d7 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -1,5 +1,73 @@ # @backstage/create-app +## 0.3.30 + +### Patch Changes + +- 60e830222: Support for `Template` kinds with version `backstage.io/v1alpha1` has now been removed. This means that the old method of running templates with `Preparers`, `Templaters` and `Publishers` has also been removed. If you had any logic in these abstractions, they should now be moved to `actions` instead, and you can find out more about those in the [documentation](https://backstage.io/docs/features/software-templates/writing-custom-actions) + + If you need any help migrating existing templates, there's a [migration guide](https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2). Reach out to us on Discord in the #support channel if you're having problems. + + The `scaffolder-backend` now no longer requires these `Preparers`, `Templaters`, and `Publishers` to be passed in, now all it needs is the `containerRunner`. + + Please update your `packages/backend/src/plugins/scaffolder.ts` like the following + + ```diff + - import { + - DockerContainerRunner, + - SingleHostDiscovery, + - } from '@backstage/backend-common'; + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { CatalogClient } from '@backstage/catalog-client'; + - import { + - CookieCutter, + - CreateReactAppTemplater, + - createRouter, + - Preparers, + - Publishers, + - Templaters, + - } from '@backstage/plugin-scaffolder-backend'; + + import { createRouter } from '@backstage/plugin-scaffolder-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import type { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + config, + database, + reader, + + discovery, + }: PluginEnvironment): Promise { + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + - const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const templaters = new Templaters(); + + - templaters.register('cookiecutter', cookiecutterTemplater); + - templaters.register('cra', craTemplater); + - + - const preparers = await Preparers.fromConfig(config, { logger }); + - const publishers = await Publishers.fromConfig(config, { logger }); + + - const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + - preparers, + - templaters, + - publishers, + + containerRunner, + logger, + config, + database, + + ``` + +- f7134c368: bump sqlite3 to 5.0.1 +- e4244f94b: Use SidebarScrollWrapper to improve responsiveness of the current sidebar. Change: Wrap a section of SidebarItems with this component to enable scroll for smaller screens. It can also be used in sidebar plugins (see shortcuts plugin for an example). + ## 0.3.29 ### Patch Changes diff --git a/packages/create-app/package.json b/packages/create-app/package.json index 73c41a79f0..b3f587ac32 100644 --- a/packages/create-app/package.json +++ b/packages/create-app/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/create-app", "description": "Create app package for Backstage", - "version": "0.3.29", + "version": "0.3.30", "private": false, "publishConfig": { "access": "public" @@ -30,7 +30,7 @@ "@backstage/cli-common": "^0.1.2", "chalk": "^4.0.0", "commander": "^6.1.0", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "handlebars": "^4.7.3", "inquirer": "^7.0.4", "ora": "^5.3.0", diff --git a/packages/dev-utils/CHANGELOG.md b/packages/dev-utils/CHANGELOG.md index 6f7c33e46c..9cd431fba8 100644 --- a/packages/dev-utils/CHANGELOG.md +++ b/packages/dev-utils/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/dev-utils +## 0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.2.0 ### Minor Changes diff --git a/packages/dev-utils/api-report.md b/packages/dev-utils/api-report.md index d3852b1c95..18bee415ff 100644 --- a/packages/dev-utils/api-report.md +++ b/packages/dev-utils/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 { ApiFactory } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; @@ -16,11 +17,13 @@ import { ReactNode } from 'react'; export function createDevApp(): DevAppBuilder; // @public (undocumented) -export const EntityGridItem: ({ entity, classes, ...rest }: Omit, "container" | "item"> & { - entity: Entity; +export const EntityGridItem: ({ + entity, + classes, + ...rest +}: Omit, 'container' | 'item'> & { + entity: Entity; }) => JSX.Element; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/dev-utils/package.json b/packages/dev-utils/package.json index f9af637830..1f6833e573 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": "0.2.0", + "version": "0.2.1", "private": false, "publishConfig": { "access": "public", @@ -30,11 +30,11 @@ }, "dependencies": { "@backstage/core-app-api": "^0.1.3", - "@backstage/core-components": "^0.1.3", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/test-utils": "^0.1.14", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", diff --git a/packages/docgen/package.json b/packages/docgen/package.json index d6670bcf83..ad47991159 100644 --- a/packages/docgen/package.json +++ b/packages/docgen/package.json @@ -28,7 +28,7 @@ "dependencies": { "chalk": "^4.0.0", "commander": "^6.1.0", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "github-slugger": "^1.3.0", "ts-node": "^10.0.0", "typescript": "^4.0.3" diff --git a/packages/e2e-test/package.json b/packages/e2e-test/package.json index b4e61a7cc1..900fbba136 100644 --- a/packages/e2e-test/package.json +++ b/packages/e2e-test/package.json @@ -30,7 +30,7 @@ "chalk": "^4.0.0", "commander": "^6.1.0", "cross-fetch": "^3.0.6", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "handlebars": "^4.7.3", "pgtools": "^0.3.0", "tree-kill": "^1.2.2", diff --git a/packages/errors/api-report.md b/packages/errors/api-report.md index cb24cba80f..019b6dd387 100644 --- a/packages/errors/api-report.md +++ b/packages/errors/api-report.md @@ -3,86 +3,82 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { JsonObject } from '@backstage/config'; // @public -export class AuthenticationError extends CustomErrorBase { -} +export class AuthenticationError extends CustomErrorBase {} // @public -export class ConflictError extends CustomErrorBase { -} +export class ConflictError extends CustomErrorBase {} // @public (undocumented) export class CustomErrorBase extends Error { - constructor(message?: string, cause?: Error); - // (undocumented) - readonly cause?: Error; + constructor(message?: string, cause?: Error); + // (undocumented) + readonly cause?: Error; } // @public -export function deserializeError(data: SerializedError): T; +export function deserializeError( + data: SerializedError, +): T; // @public export type ErrorResponse = { - error: SerializedError; - request?: { - method: string; - url: string; - }; - response: { - statusCode: number; - }; + error: SerializedError; + request?: { + method: string; + url: string; + }; + response: { + statusCode: number; + }; }; // @public -export class InputError extends CustomErrorBase { -} +export class InputError extends CustomErrorBase {} // @public -export class NotAllowedError extends CustomErrorBase { -} +export class NotAllowedError extends CustomErrorBase {} // @public -export class NotFoundError extends CustomErrorBase { -} +export class NotFoundError extends CustomErrorBase {} // @public -export class NotModifiedError extends CustomErrorBase { -} +export class NotModifiedError extends CustomErrorBase {} // @public export function parseErrorResponse(response: Response): Promise; // @public export class ResponseError extends Error { - constructor(props: { - message: string; - response: Response; - data: ErrorResponse; - cause: Error; - }); - readonly cause: Error; - readonly data: ErrorResponse; - static fromResponse(response: Response): Promise; - readonly response: Response; + constructor(props: { + message: string; + response: Response; + data: ErrorResponse; + cause: Error; + }); + readonly cause: Error; + readonly data: ErrorResponse; + static fromResponse(response: Response): Promise; + readonly response: Response; } // @public export type SerializedError = JsonObject & { - name: string; - message: string; - stack?: string; - code?: string; + name: string; + message: string; + stack?: string; + code?: string; }; // @public -export function serializeError(error: Error, options?: { +export function serializeError( + error: Error, + options?: { includeStack?: boolean; -}): SerializedError; - + }, +): SerializedError; // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/integration-react/api-report.md b/packages/integration-react/api-report.md index fee08efe6f..e2574196e0 100644 --- a/packages/integration-react/api-report.md +++ b/packages/integration-react/api-report.md @@ -3,26 +3,27 @@ > 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 { Config } from '@backstage/config'; import { ScmIntegrationRegistry } from '@backstage/integration'; // @public (undocumented) -export const ScmIntegrationIcon: ({ type }: { - type?: string | undefined; +export const ScmIntegrationIcon: ({ + type, +}: { + type?: string | undefined; }) => JSX.Element; // @public (undocumented) export class ScmIntegrationsApi { - // (undocumented) - static fromConfig(config: Config): ScmIntegrationRegistry; + // (undocumented) + static fromConfig(config: Config): ScmIntegrationRegistry; } // @public (undocumented) export const scmIntegrationsApiRef: ApiRef; - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/integration/CHANGELOG.md b/packages/integration/CHANGELOG.md index 157446ad4c..a175c0c068 100644 --- a/packages/integration/CHANGELOG.md +++ b/packages/integration/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/integration +## 0.5.8 + +### Patch Changes + +- 43a4ef644: Do not throw in `ScmIntegration` `byUrl` for invalid URLs +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- b691a938e: Fix downloads from repositories located at bitbucket.org + ## 0.5.7 ### Patch Changes diff --git a/packages/integration/api-report.md b/packages/integration/api-report.md index afd048cb13..fb06ca0de6 100644 --- a/packages/integration/api-report.md +++ b/packages/integration/api-report.md @@ -3,72 +3,71 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { Config } from '@backstage/config'; import { RestEndpointMethodTypes } from '@octokit/rest'; // @public (undocumented) export class AzureIntegration implements ScmIntegration { - constructor(integrationConfig: AzureIntegrationConfig); - // (undocumented) - get config(): AzureIntegrationConfig; - // (undocumented) - static factory: ScmIntegrationsFactory; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - // (undocumented) - get title(): string; - // (undocumented) - get type(): string; + constructor(integrationConfig: AzureIntegrationConfig); + // (undocumented) + get config(): AzureIntegrationConfig; + // (undocumented) + static factory: ScmIntegrationsFactory; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; + // (undocumented) + get title(): string; + // (undocumented) + get type(): string; } // @public export type AzureIntegrationConfig = { - host: string; - token?: string; + host: string; + token?: string; }; // @public (undocumented) export class BitbucketIntegration implements ScmIntegration { - constructor(integrationConfig: BitbucketIntegrationConfig); - // (undocumented) - get config(): BitbucketIntegrationConfig; - // (undocumented) - static factory: ScmIntegrationsFactory; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - // (undocumented) - get title(): string; - // (undocumented) - get type(): string; + constructor(integrationConfig: BitbucketIntegrationConfig); + // (undocumented) + get config(): BitbucketIntegrationConfig; + // (undocumented) + static factory: ScmIntegrationsFactory; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; + // (undocumented) + get title(): string; + // (undocumented) + get type(): string; } // @public export type BitbucketIntegrationConfig = { - host: string; - apiBaseUrl?: string; - token?: string; - username?: string; - appPassword?: string; + host: string; + apiBaseUrl?: string; + token?: string; + username?: string; + appPassword?: string; }; // @public export function defaultScmResolveUrl(options: { - url: string; - base: string; - lineNumber?: number; + url: string; + base: string; + lineNumber?: number; }): string; // @public @@ -81,212 +80,253 @@ export function getAzureDownloadUrl(url: string): string; export function getAzureFileFetchUrl(url: string): string; // @public -export function getAzureRequestOptions(config: AzureIntegrationConfig, additionalHeaders?: Record): RequestInit; +export function getAzureRequestOptions( + config: AzureIntegrationConfig, + additionalHeaders?: Record, +): RequestInit; // @public -export function getBitbucketDefaultBranch(url: string, config: BitbucketIntegrationConfig): Promise; +export function getBitbucketDefaultBranch( + url: string, + config: BitbucketIntegrationConfig, +): Promise; // @public -export function getBitbucketDownloadUrl(url: string, config: BitbucketIntegrationConfig): Promise; +export function getBitbucketDownloadUrl( + url: string, + config: BitbucketIntegrationConfig, +): Promise; // @public -export function getBitbucketFileFetchUrl(url: string, config: BitbucketIntegrationConfig): string; +export function getBitbucketFileFetchUrl( + url: string, + config: BitbucketIntegrationConfig, +): string; // @public -export function getBitbucketRequestOptions(config: BitbucketIntegrationConfig): RequestInit; +export function getBitbucketRequestOptions( + config: BitbucketIntegrationConfig, +): RequestInit; // @public -export function getGitHubFileFetchUrl(url: string, config: GitHubIntegrationConfig): string; +export function getGitHubFileFetchUrl( + url: string, + config: GitHubIntegrationConfig, +): string; // @public -export function getGitHubRequestOptions(config: GitHubIntegrationConfig): RequestInit; +export function getGitHubRequestOptions( + config: GitHubIntegrationConfig, +): RequestInit; // @public -export function getGitLabFileFetchUrl(url: string, config: GitLabIntegrationConfig): Promise; +export function getGitLabFileFetchUrl( + url: string, + config: GitLabIntegrationConfig, +): Promise; // @public -export function getGitLabRequestOptions(config: GitLabIntegrationConfig): RequestInit; +export function getGitLabRequestOptions( + config: GitLabIntegrationConfig, +): RequestInit; // @public (undocumented) export class GithubAppCredentialsMux { - constructor(config: GitHubIntegrationConfig); - // (undocumented) - getAllInstallations(): Promise; - // (undocumented) - getAppToken(owner: string, repo?: string): Promise; + constructor(config: GitHubIntegrationConfig); + // (undocumented) + getAllInstallations(): Promise< + RestEndpointMethodTypes['apps']['listInstallations']['response']['data'] + >; + // (undocumented) + getAppToken(owner: string, repo?: string): Promise; } // @public (undocumented) export class GithubCredentialsProvider { - // (undocumented) - static create(config: GitHubIntegrationConfig): GithubCredentialsProvider; - getCredentials(opts: { - url: string; - }): Promise; - } + // (undocumented) + static create(config: GitHubIntegrationConfig): GithubCredentialsProvider; + getCredentials(opts: { url: string }): Promise; +} // @public (undocumented) export type GithubCredentialType = 'app' | 'token'; // @public (undocumented) export class GitHubIntegration implements ScmIntegration { - constructor(integrationConfig: GitHubIntegrationConfig); - // (undocumented) - get config(): GitHubIntegrationConfig; - // (undocumented) - static factory: ScmIntegrationsFactory; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - // (undocumented) - get title(): string; - // (undocumented) - get type(): string; + constructor(integrationConfig: GitHubIntegrationConfig); + // (undocumented) + get config(): GitHubIntegrationConfig; + // (undocumented) + static factory: ScmIntegrationsFactory; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; + // (undocumented) + get title(): string; + // (undocumented) + get type(): string; } // @public export type GitHubIntegrationConfig = { - host: string; - apiBaseUrl?: string; - rawBaseUrl?: string; - token?: string; - apps?: GithubAppConfig[]; + host: string; + apiBaseUrl?: string; + rawBaseUrl?: string; + token?: string; + apps?: GithubAppConfig[]; }; // @public (undocumented) export class GitLabIntegration implements ScmIntegration { - constructor(integrationConfig: GitLabIntegrationConfig); - // (undocumented) - get config(): GitLabIntegrationConfig; - // (undocumented) - static factory: ScmIntegrationsFactory; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - // (undocumented) - get title(): string; - // (undocumented) - get type(): string; + constructor(integrationConfig: GitLabIntegrationConfig); + // (undocumented) + get config(): GitLabIntegrationConfig; + // (undocumented) + static factory: ScmIntegrationsFactory; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; + // (undocumented) + get title(): string; + // (undocumented) + get type(): string; } // @public export type GitLabIntegrationConfig = { - host: string; - apiBaseUrl: string; - token?: string; - baseUrl: string; + host: string; + apiBaseUrl: string; + token?: string; + baseUrl: string; }; // @public export type GoogleGcsIntegrationConfig = { - clientEmail?: string; - privateKey?: string; + clientEmail?: string; + privateKey?: string; }; // @public -export function readAzureIntegrationConfig(config: Config): AzureIntegrationConfig; +export function readAzureIntegrationConfig( + config: Config, +): AzureIntegrationConfig; // @public -export function readAzureIntegrationConfigs(configs: Config[]): AzureIntegrationConfig[]; +export function readAzureIntegrationConfigs( + configs: Config[], +): AzureIntegrationConfig[]; // @public -export function readBitbucketIntegrationConfig(config: Config): BitbucketIntegrationConfig; +export function readBitbucketIntegrationConfig( + config: Config, +): BitbucketIntegrationConfig; // @public -export function readBitbucketIntegrationConfigs(configs: Config[]): BitbucketIntegrationConfig[]; +export function readBitbucketIntegrationConfigs( + configs: Config[], +): BitbucketIntegrationConfig[]; // @public -export function readGitHubIntegrationConfig(config: Config): GitHubIntegrationConfig; +export function readGitHubIntegrationConfig( + config: Config, +): GitHubIntegrationConfig; // @public -export function readGitHubIntegrationConfigs(configs: Config[]): GitHubIntegrationConfig[]; +export function readGitHubIntegrationConfigs( + configs: Config[], +): GitHubIntegrationConfig[]; // @public -export function readGitLabIntegrationConfig(config: Config): GitLabIntegrationConfig; +export function readGitLabIntegrationConfig( + config: Config, +): GitLabIntegrationConfig; // @public -export function readGitLabIntegrationConfigs(configs: Config[]): GitLabIntegrationConfig[]; +export function readGitLabIntegrationConfigs( + configs: Config[], +): GitLabIntegrationConfig[]; // @public -export function readGoogleGcsIntegrationConfig(config: Config): GoogleGcsIntegrationConfig; +export function readGoogleGcsIntegrationConfig( + config: Config, +): GoogleGcsIntegrationConfig; // @public export interface ScmIntegration { - resolveEditUrl(url: string): string; - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; - title: string; - type: string; + resolveEditUrl(url: string): string; + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; + title: string; + type: string; } // @public -export interface ScmIntegrationRegistry extends ScmIntegrationsGroup { - // (undocumented) - azure: ScmIntegrationsGroup; - // (undocumented) - bitbucket: ScmIntegrationsGroup; - // (undocumented) - github: ScmIntegrationsGroup; - // (undocumented) - gitlab: ScmIntegrationsGroup; - resolveEditUrl(url: string): string; - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; +export interface ScmIntegrationRegistry + extends ScmIntegrationsGroup { + // (undocumented) + azure: ScmIntegrationsGroup; + // (undocumented) + bitbucket: ScmIntegrationsGroup; + // (undocumented) + github: ScmIntegrationsGroup; + // (undocumented) + gitlab: ScmIntegrationsGroup; + resolveEditUrl(url: string): string; + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; } // @public (undocumented) export class ScmIntegrations implements ScmIntegrationRegistry { - constructor(integrationsByType: IntegrationsByType); - // (undocumented) - get azure(): ScmIntegrationsGroup; - // (undocumented) - get bitbucket(): ScmIntegrationsGroup; - // (undocumented) - byHost(host: string): ScmIntegration | undefined; - // (undocumented) - byUrl(url: string | URL): ScmIntegration | undefined; - // (undocumented) - static fromConfig(config: Config): ScmIntegrations; - // (undocumented) - get github(): ScmIntegrationsGroup; - // (undocumented) - get gitlab(): ScmIntegrationsGroup; - // (undocumented) - list(): ScmIntegration[]; - // (undocumented) - resolveEditUrl(url: string): string; - // (undocumented) - resolveUrl(options: { - url: string; - base: string; - lineNumber?: number; - }): string; + constructor(integrationsByType: IntegrationsByType); + // (undocumented) + get azure(): ScmIntegrationsGroup; + // (undocumented) + get bitbucket(): ScmIntegrationsGroup; + // (undocumented) + byHost(host: string): ScmIntegration | undefined; + // (undocumented) + byUrl(url: string | URL): ScmIntegration | undefined; + // (undocumented) + static fromConfig(config: Config): ScmIntegrations; + // (undocumented) + get github(): ScmIntegrationsGroup; + // (undocumented) + get gitlab(): ScmIntegrationsGroup; + // (undocumented) + list(): ScmIntegration[]; + // (undocumented) + resolveEditUrl(url: string): string; + // (undocumented) + resolveUrl(options: { + url: string; + base: string; + lineNumber?: number; + }): string; } // @public export interface ScmIntegrationsGroup { - byHost(host: string): T | undefined; - byUrl(url: string | URL): T | undefined; - list(): T[]; + byHost(host: string): T | undefined; + byUrl(url: string | URL): T | undefined; + list(): T[]; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/integration/package.json b/packages/integration/package.json index 9313906ce1..496376ac61 100644 --- a/packages/integration/package.json +++ b/packages/integration/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/integration", - "version": "0.5.7", + "version": "0.5.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,7 +31,7 @@ "dependencies": { "@backstage/config": "^0.1.5", "cross-fetch": "^3.0.6", - "git-url-parse": "^11.4.4", + "git-url-parse": "~11.4.4", "@octokit/rest": "^18.5.3", "@octokit/auth-app": "^3.4.0", "luxon": "^1.25.0" diff --git a/packages/search-common/api-report.md b/packages/search-common/api-report.md index a3de27b5a9..0ba995f673 100644 --- a/packages/search-common/api-report.md +++ b/packages/search-common/api-report.md @@ -3,57 +3,54 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import { JsonObject } from '@backstage/config'; // @public export interface DocumentCollator { - // (undocumented) - execute(): Promise; - readonly type: string; + // (undocumented) + execute(): Promise; + readonly type: string; } // @public export interface DocumentDecorator { - // (undocumented) - execute(documents: IndexableDocument[]): Promise; - readonly types?: string[]; + // (undocumented) + execute(documents: IndexableDocument[]): Promise; + readonly types?: string[]; } // @public export interface IndexableDocument { - location: string; - text: string; - title: string; + location: string; + text: string; + title: string; } // @public (undocumented) export interface SearchQuery { - // (undocumented) - filters?: JsonObject; - // (undocumented) - pageCursor: string; - // (undocumented) - term: string; - // (undocumented) - types?: string[]; + // (undocumented) + filters?: JsonObject; + // (undocumented) + pageCursor: string; + // (undocumented) + term: string; + // (undocumented) + types?: string[]; } // @public (undocumented) export interface SearchResult { - // (undocumented) - document: IndexableDocument; - // (undocumented) - type: string; + // (undocumented) + document: IndexableDocument; + // (undocumented) + type: string; } // @public (undocumented) export interface SearchResultSet { - // (undocumented) - results: SearchResult[]; + // (undocumented) + results: SearchResult[]; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/techdocs-common/CHANGELOG.md b/packages/techdocs-common/CHANGELOG.md index c39060a934..02edf9c95f 100644 --- a/packages/techdocs-common/CHANGELOG.md +++ b/packages/techdocs-common/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/techdocs-common +## 0.6.7 + +### Patch Changes + +- 683308ecf: Fix openStack swift publisher encoding issue. Remove utf8 forced encoding on binary files +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + ## 0.6.6 ### Patch Changes diff --git a/packages/techdocs-common/api-report.md b/packages/techdocs-common/api-report.md index d25f55bf3a..8cca624e11 100644 --- a/packages/techdocs-common/api-report.md +++ b/packages/techdocs-common/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 { AzureIntegrationConfig } from '@backstage/integration'; import { Config } from '@backstage/config'; @@ -18,166 +19,239 @@ import { UrlReader } from '@backstage/backend-common'; import { Writable } from 'stream'; // @public (undocumented) -export const checkoutGitRepository: (repoUrl: string, config: Config, logger: Logger_2) => Promise; +export const checkoutGitRepository: ( + repoUrl: string, + config: Config, + logger: Logger_2, +) => Promise; // @public (undocumented) export class CommonGitPreparer implements PreparerBase { - constructor(config: Config, logger: Logger_2); - // (undocumented) - prepare(entity: Entity, options?: { - etag?: string; - }): Promise; + constructor(config: Config, logger: Logger_2); + // (undocumented) + prepare( + entity: Entity, + options?: { + etag?: string; + }, + ): Promise; } // @public (undocumented) export class DirectoryPreparer implements PreparerBase { - constructor(config: Config, logger: Logger_2, reader: UrlReader); - // (undocumented) - prepare(entity: Entity): Promise; - } + constructor(config: Config, logger: Logger_2, reader: UrlReader); + // (undocumented) + prepare(entity: Entity): Promise; +} // @public (undocumented) export type GeneratorBase = { - run(opts: GeneratorRunOptions): Promise; + run(opts: GeneratorRunOptions): Promise; }; // @public export type GeneratorBuilder = { - register(protocol: SupportedGeneratorKey, generator: GeneratorBase): void; - get(entity: Entity): GeneratorBase; + register(protocol: SupportedGeneratorKey, generator: GeneratorBase): void; + get(entity: Entity): GeneratorBase; }; // @public (undocumented) export class Generators implements GeneratorBuilder { - // (undocumented) - static fromConfig(config: Config, { logger, containerRunner, }: { - logger: Logger_2; - containerRunner: ContainerRunner; - }): Promise; - // (undocumented) - get(entity: Entity): GeneratorBase; - // (undocumented) - register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase): void; + // (undocumented) + static fromConfig( + config: Config, + { + logger, + containerRunner, + }: { + logger: Logger_2; + containerRunner: ContainerRunner; + }, + ): Promise; + // (undocumented) + get(entity: Entity): GeneratorBase; + // (undocumented) + register(generatorKey: SupportedGeneratorKey, generator: GeneratorBase): void; } // @public (undocumented) -export const getAzureIntegrationConfig: (config: Config, host: string) => AzureIntegrationConfig; +export const getAzureIntegrationConfig: ( + config: Config, + host: string, +) => AzureIntegrationConfig; // @public (undocumented) -export const getDefaultBranch: (repositoryUrl: string, config: Config) => Promise; +export const getDefaultBranch: ( + repositoryUrl: string, + config: Config, +) => Promise; // @public (undocumented) -export const getDocFilesFromRepository: (reader: UrlReader, entity: Entity, opts?: { - etag?: string | undefined; - logger?: Logger_2 | undefined; -} | undefined) => Promise; +export const getDocFilesFromRepository: ( + reader: UrlReader, + entity: Entity, + opts?: + | { + etag?: string | undefined; + logger?: Logger_2 | undefined; + } + | undefined, +) => Promise; // @public (undocumented) export function getGitHost(url: string): string; // @public (undocumented) -export const getGitHubIntegrationConfig: (config: Config, host: string) => GitHubIntegrationConfig; +export const getGitHubIntegrationConfig: ( + config: Config, + host: string, +) => GitHubIntegrationConfig; // @public (undocumented) -export const getGitLabIntegrationConfig: (config: Config, host: string) => GitLabIntegrationConfig; +export const getGitLabIntegrationConfig: ( + config: Config, + host: string, +) => GitLabIntegrationConfig; // @public (undocumented) -export const getGitRepositoryTempFolder: (repositoryUrl: string, config: Config) => Promise; +export const getGitRepositoryTempFolder: ( + repositoryUrl: string, + config: Config, +) => Promise; // @public (undocumented) export function getGitRepoType(url: string): string; // @public (undocumented) -export const getLastCommitTimestamp: (repositoryLocation: string, logger: Logger_2) => Promise; +export const getLastCommitTimestamp: ( + repositoryLocation: string, + logger: Logger_2, +) => Promise; // @public (undocumented) export const getLocationForEntity: (entity: Entity) => ParsedLocationAnnotation; // @public (undocumented) -export const getTokenForGitRepo: (repositoryUrl: string, config: Config) => Promise; +export const getTokenForGitRepo: ( + repositoryUrl: string, + config: Config, +) => Promise; // @public (undocumented) export type ParsedLocationAnnotation = { - type: RemoteProtocol; - target: string; + type: RemoteProtocol; + target: string; }; // @public (undocumented) -export const parseReferenceAnnotation: (annotationName: string, entity: Entity) => ParsedLocationAnnotation; +export const parseReferenceAnnotation: ( + annotationName: string, + entity: Entity, +) => ParsedLocationAnnotation; // @public (undocumented) export type PreparerBase = { - prepare(entity: Entity, options?: { - logger?: Logger_2; - etag?: string; - }): Promise; + prepare( + entity: Entity, + options?: { + logger?: Logger_2; + etag?: string; + }, + ): Promise; }; // @public (undocumented) export type PreparerBuilder = { - register(protocol: RemoteProtocol, preparer: PreparerBase): void; - get(entity: Entity): PreparerBase; + register(protocol: RemoteProtocol, preparer: PreparerBase): void; + get(entity: Entity): PreparerBase; }; // @public (undocumented) export class Preparers implements PreparerBuilder { - // (undocumented) - static fromConfig(config: Config, { logger, reader }: factoryOptions): Promise; - // (undocumented) - get(entity: Entity): PreparerBase; - // (undocumented) - register(protocol: RemoteProtocol, preparer: PreparerBase): void; + // (undocumented) + static fromConfig( + config: Config, + { logger, reader }: factoryOptions, + ): Promise; + // (undocumented) + get(entity: Entity): PreparerBase; + // (undocumented) + register(protocol: RemoteProtocol, preparer: PreparerBase): void; } // @public export class Publisher { - // (undocumented) - static fromConfig(config: Config, { logger, discovery }: factoryOptions_2): Promise; + // (undocumented) + static fromConfig( + config: Config, + { logger, discovery }: factoryOptions_2, + ): Promise; } // @public export interface PublisherBase { - docsRouter(): express.Handler; - fetchTechDocsMetadata(entityName: EntityName): Promise; - getReadiness(): Promise; - hasDocsBeenGenerated(entityName: Entity): Promise; - publish(request: PublishRequest): Promise; + docsRouter(): express.Handler; + fetchTechDocsMetadata(entityName: EntityName): Promise; + getReadiness(): Promise; + hasDocsBeenGenerated(entityName: Entity): Promise; + publish(request: PublishRequest): Promise; } // @public -export type PublisherType = 'local' | 'googleGcs' | 'awsS3' | 'azureBlobStorage' | 'openStackSwift'; +export type PublisherType = + | 'local' + | 'googleGcs' + | 'awsS3' + | 'azureBlobStorage' + | 'openStackSwift'; // @public -export type RemoteProtocol = 'url' | 'dir' | 'github' | 'gitlab' | 'file' | 'azure/api'; +export type RemoteProtocol = + | 'url' + | 'dir' + | 'github' + | 'gitlab' + | 'file' + | 'azure/api'; // @public (undocumented) export class TechdocsGenerator implements GeneratorBase { - constructor({ logger, containerRunner, config, }: { - logger: Logger_2; - containerRunner: ContainerRunner; - config: Config; - }); - // (undocumented) - run({ inputDir, outputDir, parsedLocationAnnotation, etag, }: GeneratorRunOptions): Promise; + constructor({ + logger, + containerRunner, + config, + }: { + logger: Logger_2; + containerRunner: ContainerRunner; + config: Config; + }); + // (undocumented) + run({ + inputDir, + outputDir, + parsedLocationAnnotation, + etag, + }: GeneratorRunOptions): Promise; } // @public export type TechDocsMetadata = { - site_name: string; - site_description: string; - etag: string; + site_name: string; + site_description: string; + etag: string; }; // @public (undocumented) export class UrlPreparer implements PreparerBase { - constructor(reader: UrlReader, logger: Logger_2); - // (undocumented) - prepare(entity: Entity, options?: { - etag?: string; - }): Promise; - } - + constructor(reader: UrlReader, logger: Logger_2); + // (undocumented) + prepare( + entity: Entity, + options?: { + etag?: string; + }, + ): Promise; +} // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/techdocs-common/package.json b/packages/techdocs-common/package.json index 7cb0e37174..de97e9815d 100644 --- a/packages/techdocs-common/package.json +++ b/packages/techdocs-common/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/techdocs-common", "description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli", - "version": "0.6.6", + "version": "0.6.7", "main": "src/index.ts", "types": "src/index.ts", "private": false, @@ -38,18 +38,18 @@ "dependencies": { "@azure/identity": "^1.2.2", "@azure/storage-blob": "^12.4.0", - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-model": "^0.8.4", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@google-cloud/storage": "^5.6.0", "@types/express": "^4.17.6", "aws-sdk": "^2.840.0", "cross-fetch": "^3.0.6", "express": "^4.17.1", - "fs-extra": "^10.0.0", - "git-url-parse": "^11.4.4", + "fs-extra": "9.1.0", + "git-url-parse": "~11.4.4", "js-yaml": "^4.0.0", "json5": "^2.1.3", "mime-types": "^2.1.27", diff --git a/packages/test-utils-core/api-report.md b/packages/test-utils-core/api-report.md index 4b603b8e7b..8eb7ee458a 100644 --- a/packages/test-utils-core/api-report.md +++ b/packages/test-utils-core/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 { ReactElement } from 'react'; import { RenderResult } from '@testing-library/react'; @@ -12,48 +11,53 @@ export type AsyncLogCollector = () => Promise; // @public (undocumented) export type CollectedLogs = { - [key in T]: string[]; + [key in T]: string[]; }; // @public (undocumented) export class Keyboard { - constructor(target: any, { debug }?: { - debug?: boolean | undefined; - }); - // (undocumented) - click(): Promise; - // (undocumented) - debug: boolean; - // (undocumented) - document: any; - // (undocumented) - enter(value: any): Promise; - // (undocumented) - escape(): Promise; - // (undocumented) - get focused(): any; - // (undocumented) - static fromReadableInput(input: any): any; - // (undocumented) - _log(message: any, ...args: any[]): void; - // (undocumented) - _pretty(element: any): string; - // (undocumented) - send(chars: any): Promise; - // (undocumented) - _sendKey(key: any, charCode: any, action: any): Promise; - // (undocumented) - tab(): Promise; - // (undocumented) - static toReadableInput(chars: any): any; - // (undocumented) - toString(): string; - // (undocumented) - static type(target: any, input: any): Promise; - // (undocumented) - type(input: any): Promise; - // (undocumented) - static typeDebug(target: any, input: any): Promise; + constructor( + target: any, + { + debug, + }?: { + debug?: boolean | undefined; + }, + ); + // (undocumented) + click(): Promise; + // (undocumented) + debug: boolean; + // (undocumented) + document: any; + // (undocumented) + enter(value: any): Promise; + // (undocumented) + escape(): Promise; + // (undocumented) + get focused(): any; + // (undocumented) + static fromReadableInput(input: any): any; + // (undocumented) + _log(message: any, ...args: any[]): void; + // (undocumented) + _pretty(element: any): string; + // (undocumented) + send(chars: any): Promise; + // (undocumented) + _sendKey(key: any, charCode: any, action: any): Promise; + // (undocumented) + tab(): Promise; + // (undocumented) + static toReadableInput(chars: any): any; + // (undocumented) + toString(): string; + // (undocumented) + static type(target: any, input: any): Promise; + // (undocumented) + type(input: any): Promise; + // (undocumented) + static typeDebug(target: any, input: any): Promise; } // @public (undocumented) @@ -69,18 +73,26 @@ export function renderWithEffects(nodes: ReactElement): Promise; export type SyncLogCollector = () => void; // @public (undocumented) -export function withLogCollector(callback: AsyncLogCollector): Promise>; +export function withLogCollector( + callback: AsyncLogCollector, +): Promise>; // @public (undocumented) -export function withLogCollector(callback: SyncLogCollector): CollectedLogs; +export function withLogCollector( + callback: SyncLogCollector, +): CollectedLogs; // @public (undocumented) -export function withLogCollector(logsToCollect: T[], callback: AsyncLogCollector): Promise>; +export function withLogCollector( + logsToCollect: T[], + callback: AsyncLogCollector, +): Promise>; // @public (undocumented) -export function withLogCollector(logsToCollect: T[], callback: SyncLogCollector): CollectedLogs; - +export function withLogCollector( + logsToCollect: T[], + callback: SyncLogCollector, +): CollectedLogs; // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/test-utils/api-report.md b/packages/test-utils/api-report.md index 078fa14399..57020e6221 100644 --- a/packages/test-utils/api-report.md +++ b/packages/test-utils/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 { ComponentType } from 'react'; import { ErrorApi } from '@backstage/core-plugin-api'; import { ErrorContext } from '@backstage/core-plugin-api'; @@ -17,66 +16,72 @@ import { StorageApi } from '@backstage/core-plugin-api'; import { StorageValueChange } from '@backstage/core-plugin-api'; // @public (undocumented) -export function mockBreakpoint(initialBreakpoint?: Breakpoint): { - set(breakpoint: Breakpoint): void; - remove(): void; +export function mockBreakpoint( + initialBreakpoint?: Breakpoint, +): { + set(breakpoint: Breakpoint): void; + remove(): void; }; // @public (undocumented) export class MockErrorApi implements ErrorApi { - constructor(options?: Options); - // (undocumented) - error$(): Observable<{ - error: Error; - context?: ErrorContext; - }>; - // (undocumented) - getErrors(): ErrorWithContext[]; - // (undocumented) - post(error: Error, context?: ErrorContext): void; - // (undocumented) - waitForError(pattern: RegExp, timeoutMs?: number): Promise; + constructor(options?: Options); + // (undocumented) + error$(): Observable<{ + error: Error; + context?: ErrorContext; + }>; + // (undocumented) + getErrors(): ErrorWithContext[]; + // (undocumented) + post(error: Error, context?: ErrorContext): void; + // (undocumented) + waitForError(pattern: RegExp, timeoutMs?: number): Promise; } // @public (undocumented) export class MockStorageApi implements StorageApi { - // (undocumented) - static create(data?: MockStorageBucket): MockStorageApi; - // (undocumented) - forBucket(name: string): StorageApi; - // (undocumented) - get(key: string): T | undefined; - // (undocumented) - observe$(key: string): Observable>; - // (undocumented) - remove(key: string): Promise; - // (undocumented) - set(key: string, data: T): Promise; - } + // (undocumented) + static create(data?: MockStorageBucket): MockStorageApi; + // (undocumented) + forBucket(name: string): StorageApi; + // (undocumented) + get(key: string): T | undefined; + // (undocumented) + observe$(key: string): Observable>; + // (undocumented) + remove(key: string): Promise; + // (undocumented) + set(key: string, data: T): Promise; +} // @public (undocumented) export type MockStorageBucket = { - [key: string]: any; + [key: string]: any; }; // @public (undocumented) export const msw: { - setupDefaultHandlers: (worker: { - listen: (t: any) => void; - close: () => void; - resetHandlers: () => void; - }) => void; + setupDefaultHandlers: (worker: { + listen: (t: any) => void; + close: () => void; + resetHandlers: () => void; + }) => void; }; // @public -export function renderInTestApp(Component: ComponentType | ReactNode, options?: TestAppOptions): Promise; +export function renderInTestApp( + Component: ComponentType | ReactNode, + options?: TestAppOptions, +): Promise; // @public -export function wrapInTestApp(Component: ComponentType | ReactNode, options?: TestAppOptions): ReactElement; +export function wrapInTestApp( + Component: ComponentType | ReactNode, + options?: TestAppOptions, +): ReactElement; - -export * from "@backstage/test-utils-core"; +export * from '@backstage/test-utils-core'; // (No @packageDocumentation comment for this package) - ``` diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 93d0957f5a..e09f5838c7 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/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 { Overrides } from '@material-ui/core/styles/overrides'; import { Palette } from '@material-ui/core/styles/createPalette'; import { PaletteOptions } from '@material-ui/core/styles/createPalette'; @@ -18,22 +17,22 @@ export type BackstagePaletteOptions = PaletteOptions & PaletteAdditions; // @public (undocumented) export interface BackstageTheme extends Theme { - // (undocumented) - getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme; - // (undocumented) - page: PageTheme; - // (undocumented) - palette: BackstagePalette; + // (undocumented) + getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme; + // (undocumented) + page: PageTheme; + // (undocumented) + palette: BackstagePalette; } // @public (undocumented) export interface BackstageThemeOptions extends ThemeOptions { - // (undocumented) - getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme; - // (undocumented) - page: PageTheme; - // (undocumented) - palette: BackstagePaletteOptions; + // (undocumented) + getPageTheme: ({ themeId }: PageThemeSelector) => PageTheme; + // (undocumented) + page: PageTheme; + // (undocumented) + palette: BackstagePaletteOptions; } // @public (undocumented) @@ -43,7 +42,9 @@ export const colorVariants: Record; export function createTheme(options: SimpleThemeOptions): BackstageTheme; // @public (undocumented) -export function createThemeOptions(options: SimpleThemeOptions): BackstageThemeOptions; +export function createThemeOptions( + options: SimpleThemeOptions, +): BackstageThemeOptions; // @public (undocumented) export function createThemeOverrides(theme: BackstageTheme): Overrides; @@ -59,9 +60,9 @@ export const lightTheme: BackstageTheme; // @public (undocumented) export type PageTheme = { - colors: string[]; - shape: string; - backgroundImage: string; + colors: string[]; + shape: string; + backgroundImage: string; }; // @public (undocumented) @@ -69,7 +70,7 @@ export const pageTheme: Record; // @public (undocumented) export type PageThemeSelector = { - themeId: string; + themeId: string; }; // @public (undocumented) @@ -77,13 +78,11 @@ export const shapes: Record; // @public export type SimpleThemeOptions = { - palette: BackstagePaletteOptions; - defaultPageTheme: string; - pageTheme?: Record; - fontFamily?: string; + palette: BackstagePaletteOptions; + defaultPageTheme: string; + pageTheme?: Record; + fontFamily?: string; }; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/api-docs/CHANGELOG.md b/plugins/api-docs/CHANGELOG.md index 236517b105..9135443ac9 100644 --- a/plugins/api-docs/CHANGELOG.md +++ b/plugins/api-docs/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-api-docs +## 0.6.1 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog@0.6.6 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.6.0 ### Minor Changes diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index c7d3fdc694..bb83d4c539 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/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 { ApiEntity } from '@backstage/catalog-model'; import { ApiRef } from '@backstage/core-plugin-api'; @@ -20,32 +21,38 @@ export const ApiDefinitionCard: (_: Props) => JSX.Element; // @public (undocumented) export type ApiDefinitionWidget = { - type: string; - title: string; - component: (definition: string) => React_2.ReactElement; - rawLanguage?: string; + type: string; + title: string; + component: (definition: string) => React_2.ReactElement; + rawLanguage?: string; }; // @public (undocumented) export const apiDocsConfigRef: ApiRef; // @public (undocumented) -const apiDocsPlugin: BackstagePlugin<{ +const apiDocsPlugin: BackstagePlugin< + { root: RouteRef; -}, { + }, + { createComponent: ExternalRouteRef; -}>; - -export { apiDocsPlugin } - -export { apiDocsPlugin as plugin } + } +>; +export { apiDocsPlugin }; +export { apiDocsPlugin as plugin }; // @public (undocumented) -export const ApiExplorerPage: ({ initiallySelectedFilter, columns, }: ApiExplorerPageProps) => JSX.Element; +export const ApiExplorerPage: ({ + initiallySelectedFilter, + columns, +}: ApiExplorerPageProps) => JSX.Element; // @public (undocumented) -export const ApiTypeTitle: ({ apiEntity }: { - apiEntity: ApiEntity; +export const ApiTypeTitle: ({ + apiEntity, +}: { + apiEntity: ApiEntity; }) => JSX.Element; // @public (undocumented) @@ -62,36 +69,46 @@ export function defaultDefinitionWidgets(): ApiDefinitionWidget[]; // @public (undocumented) export const EntityApiDefinitionCard: (_: { - apiEntity?: ApiEntity | undefined; + apiEntity?: ApiEntity | undefined; }) => JSX.Element; // @public (undocumented) -export const EntityConsumedApisCard: ({ variant }: { - entity?: Entity| undefined; - variant?: "gridItem" | undefined; +export const EntityConsumedApisCard: ({ + variant, +}: { + entity?: Entity | undefined; + variant?: 'gridItem' | undefined; }) => JSX.Element; // @public (undocumented) -export const EntityConsumingComponentsCard: ({ variant }: { - entity?: Entity| undefined; - variant?: "gridItem" | undefined; +export const EntityConsumingComponentsCard: ({ + variant, +}: { + entity?: Entity | undefined; + variant?: 'gridItem' | undefined; }) => JSX.Element; // @public (undocumented) -export const EntityHasApisCard: ({ variant }: { - variant?: "gridItem" | undefined; +export const EntityHasApisCard: ({ + variant, +}: { + variant?: 'gridItem' | undefined; }) => JSX.Element; // @public (undocumented) -export const EntityProvidedApisCard: ({ variant }: { - entity?: Entity| undefined; - variant?: "gridItem" | undefined; +export const EntityProvidedApisCard: ({ + variant, +}: { + entity?: Entity | undefined; + variant?: 'gridItem' | undefined; }) => JSX.Element; // @public (undocumented) -export const EntityProvidingComponentsCard: ({ variant }: { - entity?: Entity| undefined; - variant?: "gridItem" | undefined; +export const EntityProvidingComponentsCard: ({ + variant, +}: { + entity?: Entity | undefined; + variant?: 'gridItem' | undefined; }) => JSX.Element; // @public (undocumented) @@ -101,7 +118,10 @@ export const HasApisCard: ({ variant }: Props_3) => JSX.Element; export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; // @public (undocumented) -export const PlainApiDefinitionWidget: ({ definition, language }: Props_9) => JSX.Element; +export const PlainApiDefinitionWidget: ({ + definition, + language, +}: Props_9) => JSX.Element; // @public (undocumented) export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element; @@ -109,7 +129,5 @@ export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element; // @public (undocumented) export const ProvidingComponentsCard: ({ variant }: Props_7) => JSX.Element; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index fbec69278e..3e667d6528 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-api-docs", - "version": "0.6.0", + "version": "0.6.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,11 +30,11 @@ }, "dependencies": { "@asyncapi/react-component": "^0.23.0", - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog": "^0.6.4", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog": "^0.6.6", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-icons/font": "^1.0.2", "@material-ui/core": "^4.11.0", @@ -53,7 +53,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/app-backend/api-report.md b/plugins/app-backend/api-report.md index 93aba5596f..7215366ebb 100644 --- a/plugins/app-backend/api-report.md +++ b/plugins/app-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 { Config } from '@backstage/config'; import express from 'express'; import { Logger as Logger_2 } from 'winston'; @@ -13,16 +12,14 @@ export function createRouter(options: RouterOptions): Promise; // @public (undocumented) export interface RouterOptions { - appPackageName: string; - // (undocumented) - config: Config; - disableConfigInjection?: boolean; - // (undocumented) - logger: Logger_2; - staticFallbackHandler?: express.Handler; + appPackageName: string; + // (undocumented) + config: Config; + disableConfigInjection?: boolean; + // (undocumented) + logger: Logger_2; + staticFallbackHandler?: express.Handler; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/app-backend/package.json b/plugins/app-backend/package.json index 384d44d82a..cd5b62dfb4 100644 --- a/plugins/app-backend/package.json +++ b/plugins/app-backend/package.json @@ -35,7 +35,7 @@ "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "winston": "^3.2.1", "yn": "^4.0.0" }, diff --git a/plugins/auth-backend/CHANGELOG.md b/plugins/auth-backend/CHANGELOG.md index 822ec104e0..dd96bee3cc 100644 --- a/plugins/auth-backend/CHANGELOG.md +++ b/plugins/auth-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-auth-backend +## 0.3.16 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + - @backstage/catalog-client@0.3.16 + ## 0.3.15 ### Patch Changes diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 9f9a8d18ef..50341c0392 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-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 { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -16,52 +15,67 @@ import { Profile } from 'passport'; import { UserEntity } from '@backstage/catalog-model'; // @public (undocumented) -export type AuthProviderFactory = (options: AuthProviderFactoryOptions) => AuthProviderRouteHandlers; +export type AuthProviderFactory = ( + options: AuthProviderFactoryOptions, +) => AuthProviderRouteHandlers; // @public (undocumented) export type AuthProviderFactoryOptions = { - providerId: string; - globalConfig: AuthProviderConfig; - config: Config; - logger: Logger_2; - tokenIssuer: TokenIssuer; - discovery: PluginEndpointDiscovery; - catalogApi: CatalogApi; - identityResolver?: ExperimentalIdentityResolver; + providerId: string; + globalConfig: AuthProviderConfig; + config: Config; + logger: Logger_2; + tokenIssuer: TokenIssuer; + discovery: PluginEndpointDiscovery; + catalogApi: CatalogApi; + identityResolver?: ExperimentalIdentityResolver; }; // @public export interface AuthProviderRouteHandlers { - frameHandler(req: express.Request, res: express.Response): Promise; - logout?(req: express.Request, res: express.Response): Promise; - refresh?(req: express.Request, res: express.Response): Promise; - start(req: express.Request, res: express.Response): Promise; + frameHandler(req: express.Request, res: express.Response): Promise; + logout?(req: express.Request, res: express.Response): Promise; + refresh?(req: express.Request, res: express.Response): Promise; + start(req: express.Request, res: express.Response): Promise; } // @public (undocumented) export type AuthResponse = { - providerInfo: ProviderInfo; - profile: ProfileInfo; - backstageIdentity?: BackstageIdentity; + providerInfo: ProviderInfo; + profile: ProfileInfo; + backstageIdentity?: BackstageIdentity; }; // @public (undocumented) export type BackstageIdentity = { - id: string; - idToken?: string; - token?: string; - entity?: Entity; + id: string; + idToken?: string; + token?: string; + entity?: Entity; }; // @public (undocumented) -export const createGoogleProvider: (options?: GoogleProviderOptions | undefined) => AuthProviderFactory; +export const createGoogleProvider: ( + options?: GoogleProviderOptions | undefined, +) => AuthProviderFactory; // @public (undocumented) -export function createRouter({ logger, config, discovery, database, providerFactories, }: RouterOptions): Promise; +export const createMicrosoftProvider: ( + options?: MicrosoftProviderOptions | undefined, +) => AuthProviderFactory; + +// @public (undocumented) +export function createRouter({ + logger, + config, + discovery, + database, + providerFactories, +}: RouterOptions): Promise; // @public (undocumented) export const defaultAuthProviderFactories: { - [providerId: string]: AuthProviderFactory; + [providerId: string]: AuthProviderFactory; }; // @public (undocumented) @@ -75,86 +89,108 @@ export const googleEmailSignInResolver: SignInResolver; // @public (undocumented) export type GoogleProviderOptions = { - authHandler?: AuthHandler; - signIn?: { - resolver?: SignInResolver; - }; + authHandler?: AuthHandler; + signIn?: { + resolver?: SignInResolver; + }; }; // @public export class IdentityClient { - constructor(options: { - discovery: PluginEndpointDiscovery; - issuer: string; - }); - authenticate(token: string | undefined): Promise; - static getBearerToken(authorizationHeader: string | undefined): string | undefined; - listPublicKeys(): Promise<{ - keys: JSONWebKey[]; - }>; - } + constructor(options: { discovery: PluginEndpointDiscovery; issuer: string }); + authenticate(token: string | undefined): Promise; + static getBearerToken( + authorizationHeader: string | undefined, + ): string | undefined; + listPublicKeys(): Promise<{ + keys: JSONWebKey[]; + }>; +} + +// @public (undocumented) +export const microsoftEmailSignInResolver: SignInResolver; + +// @public (undocumented) +export type MicrosoftProviderOptions = { + authHandler?: AuthHandler; + signIn?: { + resolver?: SignInResolver; + }; +}; // @public (undocumented) export class OAuthAdapter implements AuthProviderRouteHandlers { - constructor(handlers: OAuthHandlers, options: Options); - // (undocumented) - frameHandler(req: express.Request, res: express.Response): Promise; - // (undocumented) - static fromConfig(config: AuthProviderConfig, handlers: OAuthHandlers, options: Pick): OAuthAdapter; - // (undocumented) - logout(req: express.Request, res: express.Response): Promise; - // (undocumented) - refresh(req: express.Request, res: express.Response): Promise; - // (undocumented) - start(req: express.Request, res: express.Response): Promise; + constructor(handlers: OAuthHandlers, options: Options); + // (undocumented) + frameHandler(req: express.Request, res: express.Response): Promise; + // (undocumented) + static fromConfig( + config: AuthProviderConfig, + handlers: OAuthHandlers, + options: Pick< + Options, + 'providerId' | 'persistScopes' | 'disableRefresh' | 'tokenIssuer' + >, + ): OAuthAdapter; + // (undocumented) + logout(req: express.Request, res: express.Response): Promise; + // (undocumented) + refresh(req: express.Request, res: express.Response): Promise; + // (undocumented) + start(req: express.Request, res: express.Response): Promise; } // @public (undocumented) export class OAuthEnvironmentHandler implements AuthProviderRouteHandlers { - constructor(handlers: Map); - // (undocumented) - frameHandler(req: express.Request, res: express.Response): Promise; - // (undocumented) - logout(req: express.Request, res: express.Response): Promise; - // (undocumented) - static mapConfig(config: Config, factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers): OAuthEnvironmentHandler; - // (undocumented) - refresh(req: express.Request, res: express.Response): Promise; - // (undocumented) - start(req: express.Request, res: express.Response): Promise; + constructor(handlers: Map); + // (undocumented) + frameHandler(req: express.Request, res: express.Response): Promise; + // (undocumented) + logout(req: express.Request, res: express.Response): Promise; + // (undocumented) + static mapConfig( + config: Config, + factoryFunc: (envConfig: Config) => AuthProviderRouteHandlers, + ): OAuthEnvironmentHandler; + // (undocumented) + refresh(req: express.Request, res: express.Response): Promise; + // (undocumented) + start(req: express.Request, res: express.Response): Promise; } // @public export interface OAuthHandlers { - handler(req: express.Request): Promise<{ - response: AuthResponse; - refreshToken?: string; - }>; - logout?(): Promise; - refresh?(req: OAuthRefreshRequest): Promise>; - start(req: OAuthStartRequest): Promise; + handler( + req: express.Request, + ): Promise<{ + response: AuthResponse; + refreshToken?: string; + }>; + logout?(): Promise; + refresh?(req: OAuthRefreshRequest): Promise>; + start(req: OAuthStartRequest): Promise; } // @public (undocumented) export type OAuthProviderInfo = { - accessToken: string; - idToken?: string; - expiresInSeconds?: number; - scope: string; - refreshToken?: string; + accessToken: string; + idToken?: string; + expiresInSeconds?: number; + scope: string; + refreshToken?: string; }; // @public export type OAuthProviderOptions = { - clientId: string; - clientSecret: string; - callbackUrl: string; + clientId: string; + clientSecret: string; + callbackUrl: string; }; // @public (undocumented) export type OAuthRefreshRequest = express.Request<{}> & { - scope: string; - refreshToken: string; + scope: string; + refreshToken: string; }; // @public (undocumented) @@ -162,36 +198,40 @@ export type OAuthResponse = AuthResponse; // @public (undocumented) export type OAuthResult = { - fullProfile: Profile; - params: { - id_token?: string; - scope: string; - expires_in: number; - }; - accessToken: string; - refreshToken?: string; + fullProfile: Profile; + params: { + id_token?: string; + scope: string; + expires_in: number; + }; + accessToken: string; + refreshToken?: string; }; // @public (undocumented) export type OAuthStartRequest = express.Request<{}> & { - scope: string; - state: OAuthState; + scope: string; + state: OAuthState; }; // @public (undocumented) export type OAuthState = { - nonce: string; - env: string; + nonce: string; + env: string; }; // @public (undocumented) -export const postMessageResponse: (res: express.Response, appOrigin: string, response: WebMessageResponse) => void; +export const postMessageResponse: ( + res: express.Response, + appOrigin: string, + response: WebMessageResponse, +) => void; // @public export type ProfileInfo = { - email?: string; - displayName?: string; - picture?: string; + email?: string; + displayName?: string; + picture?: string; }; // @public (undocumented) @@ -199,31 +239,31 @@ export const readState: (stateString: string) => OAuthState; // @public (undocumented) export interface RouterOptions { - // (undocumented) - config: Config; - // (undocumented) - database: PluginDatabaseManager; - // (undocumented) - discovery: PluginEndpointDiscovery; - // (undocumented) - logger: Logger_2; - // (undocumented) - providerFactories?: ProviderFactories; + // (undocumented) + config: Config; + // (undocumented) + database: PluginDatabaseManager; + // (undocumented) + discovery: PluginEndpointDiscovery; + // (undocumented) + logger: Logger_2; + // (undocumented) + providerFactories?: ProviderFactories; } // @public (undocumented) export const verifyNonce: (req: express.Request, providerId: string) => void; // @public -export type WebMessageResponse = { - type: 'authorization_response'; - response: AuthResponse; -} | { - type: 'authorization_response'; - error: Error; -}; - +export type WebMessageResponse = + | { + type: 'authorization_response'; + response: AuthResponse; + } + | { + type: 'authorization_response'; + error: Error; + }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index 085590c8fa..0b108e11cd 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-auth-backend", - "version": "0.3.15", + "version": "0.3.16", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.4", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@backstage/test-utils": "^0.1.14", @@ -44,7 +44,7 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "express-session": "^1.17.1", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "got": "^11.5.2", "helmet": "^4.0.0", "jose": "^1.27.1", diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 940ceadffe..6b262dcf68 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -15,6 +15,7 @@ */ export * from './google'; +export * from './microsoft'; export { factories as defaultAuthProviderFactories } from './factories'; // Export the minimal interface required for implementing a diff --git a/plugins/auth-backend/src/providers/microsoft/index.ts b/plugins/auth-backend/src/providers/microsoft/index.ts index 374e451ae9..3167de5d0d 100644 --- a/plugins/auth-backend/src/providers/microsoft/index.ts +++ b/plugins/auth-backend/src/providers/microsoft/index.ts @@ -14,5 +14,8 @@ * limitations under the License. */ -export { createMicrosoftProvider } from './provider'; +export { + createMicrosoftProvider, + microsoftEmailSignInResolver, +} from './provider'; export type { MicrosoftProviderOptions } from './provider'; 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..c979bed582 --- /dev/null +++ b/plugins/auth-backend/src/providers/microsoft/provider.test.ts @@ -0,0 +1,105 @@ +/* + * 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 { MicrosoftAuthProvider } from './provider'; +import * as helpers from '../../lib/passport/PassportStrategyHelper'; +import { OAuthResult } from '../../lib/oauth'; +import { getVoidLogger } from '@backstage/backend-common'; +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient } from '../../lib/catalog'; + +const mockFrameHandler = (jest.spyOn( + helpers, + 'executeFrameHandlerStrategy', +) as unknown) as jest.MockedFunction< + () => Promise<{ result: OAuthResult; privateInfo: any }> +>; + +describe('createMicrosoftProvider', () => { + it('should auth', async () => { + const tokenIssuer = { + issueToken: jest.fn(), + listPublicKeys: jest.fn(), + }; + const catalogIdentityClient = { + findUser: jest.fn(), + }; + + const provider = new MicrosoftAuthProvider({ + logger: getVoidLogger(), + catalogIdentityClient: (catalogIdentityClient as unknown) as CatalogIdentityClient, + tokenIssuer: (tokenIssuer as unknown) as TokenIssuer, + authHandler: async ({ fullProfile }) => ({ + profile: { + email: fullProfile.emails![0]!.value, + displayName: fullProfile.displayName, + picture: 'http://microsoft.com/lols', + }, + }), + clientId: 'mock', + clientSecret: 'mock', + callbackUrl: 'mock', + }); + + mockFrameHandler.mockResolvedValueOnce({ + result: { + fullProfile: { + emails: [ + { + type: 'work', + value: 'conrad@example.com', + }, + ], + displayName: 'Conrad', + name: { + familyName: 'Ribas', + givenName: 'Francisco', + }, + id: 'conrad', + provider: 'microsoft', + photos: [ + { + value: 'some-data', + }, + ], + }, + params: { + id_token: 'idToken', + scope: 'scope', + expires_in: 123, + }, + accessToken: 'accessToken', + }, + privateInfo: { + refreshToken: 'wacka', + }, + }); + const { response } = await provider.handler({} as any); + expect(response).toEqual({ + providerInfo: { + accessToken: 'accessToken', + expiresInSeconds: 123, + idToken: 'idToken', + scope: 'scope', + }, + profile: { + email: 'conrad@example.com', + displayName: 'Conrad', + picture: 'http://microsoft.com/lols', + }, + }); + }); +}); diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 86c7d3a3a4..633844a351 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -17,45 +17,65 @@ import express from 'express'; import passport from 'passport'; import { Strategy as MicrosoftStrategy } from 'passport-microsoft'; - +import { TokenIssuer } from '../../identity/types'; +import { CatalogIdentityClient, getEntityClaims } from '../../lib/catalog'; import { + encodeState, + OAuthAdapter, + OAuthEnvironmentHandler, + OAuthHandlers, + OAuthProviderOptions, + OAuthRefreshRequest, + OAuthResponse, + OAuthResult, + OAuthStartRequest, +} from '../../lib/oauth'; +import { + executeFetchUserProfileStrategy, executeFrameHandlerStrategy, executeRedirectStrategy, executeRefreshTokenStrategy, makeProfileInfo, - executeFetchUserProfileStrategy, PassportDoneCallback, } from '../../lib/passport'; - -import { RedirectInfo, AuthProviderFactory } from '../types'; - import { - OAuthAdapter, - OAuthProviderOptions, - OAuthHandlers, - OAuthResponse, - OAuthEnvironmentHandler, - OAuthStartRequest, - encodeState, - OAuthRefreshRequest, - OAuthResult, -} from '../../lib/oauth'; - + AuthProviderFactory, + AuthHandler, + RedirectInfo, + SignInResolver, +} from '../types'; +import { Logger } from 'winston'; import got from 'got'; type PrivateInfo = { refreshToken: string; }; -export type MicrosoftAuthProviderOptions = OAuthProviderOptions & { +type Options = OAuthProviderOptions & { + signInResolver?: SignInResolver; + authHandler: AuthHandler; + tokenIssuer: TokenIssuer; + catalogIdentityClient: CatalogIdentityClient; + logger: Logger; authorizationUrl?: string; tokenUrl?: string; }; export class MicrosoftAuthProvider implements OAuthHandlers { private readonly _strategy: MicrosoftStrategy; + private readonly signInResolver?: SignInResolver; + private readonly authHandler: AuthHandler; + private readonly tokenIssuer: TokenIssuer; + private readonly catalogIdentityClient: CatalogIdentityClient; + private readonly logger: Logger; + + constructor(options: Options) { + this.signInResolver = options.signInResolver; + this.authHandler = options.authHandler; + this.tokenIssuer = options.tokenIssuer; + this.logger = options.logger; + this.catalogIdentityClient = options.catalogIdentityClient; - constructor(options: MicrosoftAuthProviderOptions) { this._strategy = new MicrosoftStrategy( { clientID: options.clientId, @@ -92,32 +112,10 @@ export class MicrosoftAuthProvider implements OAuthHandlers { PrivateInfo >(req, this._strategy); - try { - const photoUrl = await this.getUserPhoto(result.accessToken); - - const profile = makeProfileInfo( - { - ...result.fullProfile, - photos: photoUrl ? [{ value: photoUrl }] : undefined, - }, - result.params.id_token, - ); - - return { - response: await this.populateIdentity({ - profile, - providerInfo: { - idToken: result.params.id_token, - accessToken: result.accessToken, - scope: result.params.scope, - expiresInSeconds: result.params.expires_in, - }, - }), - refreshToken: privateInfo.refreshToken, - }; - } catch (error) { - throw new Error(`Error processing auth response: ${error}`); - } + return { + response: await this.handleResult(result), + refreshToken: privateInfo.refreshToken, + }; } async refresh(req: OAuthRefreshRequest): Promise { @@ -131,21 +129,46 @@ export class MicrosoftAuthProvider implements OAuthHandlers { this._strategy, accessToken, ); - const profile = makeProfileInfo(fullProfile, params.id_token); - const photo = await this.getUserPhoto(accessToken); - if (photo) { - profile.picture = photo; - } - return this.populateIdentity({ + return this.handleResult({ + fullProfile, + params, + accessToken, + refreshToken: req.refreshToken, + }); + } + + private async handleResult(result: OAuthResult) { + const photo = await this.getUserPhoto(result.accessToken); + result.fullProfile.photos = photo ? [{ value: photo }] : undefined; + + const { profile } = await this.authHandler(result); + + const response: OAuthResponse = { providerInfo: { - accessToken, - idToken: params.id_token, - expiresInSeconds: params.expires_in, - scope: params.scope, + idToken: result.params.id_token, + accessToken: result.accessToken, + scope: result.params.scope, + expiresInSeconds: result.params.expires_in, }, profile, - }); + }; + + if (this.signInResolver) { + response.backstageIdentity = await this.signInResolver( + { + result, + profile, + }, + { + tokenIssuer: this.tokenIssuer, + catalogIdentityClient: this.catalogIdentityClient, + logger: this.logger, + }, + ); + } + + return response; } private getUserPhoto(accessToken: string): Promise { @@ -165,7 +188,7 @@ export class MicrosoftAuthProvider implements OAuthHandlers { resolve(photoURL); }) .catch(error => { - console.log( + this.logger.warn( `Could not retrieve user profile photo from Microsoft Graph API: ${error}`, ); // User profile photo is optional, ignore errors and resolve undefined @@ -173,29 +196,81 @@ export class MicrosoftAuthProvider implements OAuthHandlers { }); }); } - - private async populateIdentity( - response: OAuthResponse, - ): Promise { - const { profile } = response; - - if (!profile.email) { - throw new Error('Microsoft profile contained no email'); - } - - // Like Google implementation, setting this to local part of email for now - const id = profile.email.split('@')[0]; - - return { ...response, backstageIdentity: { id } }; - } } -export type MicrosoftProviderOptions = {}; +export const microsoftEmailSignInResolver: SignInResolver = async ( + info, + ctx, +) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('Microsoft profile contained no email'); + } + + const entity = await ctx.catalogIdentityClient.findUser({ + annotations: { + 'microsoft.com/email': profile.email, + }, + }); + + const claims = getEntityClaims(entity); + const token = await ctx.tokenIssuer.issueToken({ claims }); + + return { id: entity.metadata.name, entity, token }; +}; + +export const microsoftDefaultSignInResolver: SignInResolver = async ( + info, + ctx, +) => { + const { profile } = info; + + if (!profile.email) { + throw new Error('Profile contained no email'); + } + + const userId = profile.email.split('@')[0]; + + const token = await ctx.tokenIssuer.issueToken({ + claims: { sub: userId, ent: [`user:default/${userId}`] }, + }); + + return { id: userId, token }; +}; + +export type MicrosoftProviderOptions = { + /** + * The profile transformation function used to verify and convert the auth response + * into the profile that will be presented to the user. + */ + authHandler?: AuthHandler; + + /** + * Configure sign-in for this provider, without it the provider can not be used to sign users in. + */ + /** + * Maps an auth result to a Backstage identity for the user. + * + * Set to `'email'` to use the default email-based sign in resolver, which will search + * the catalog for a single user entity that has a matching `microsoft.com/email` annotation. + */ + signIn?: { + resolver?: SignInResolver; + }; +}; export const createMicrosoftProvider = ( - _options?: MicrosoftProviderOptions, + options?: MicrosoftProviderOptions, ): AuthProviderFactory => { - return ({ providerId, globalConfig, config, tokenIssuer }) => + return ({ + providerId, + globalConfig, + config, + tokenIssuer, + catalogApi, + logger, + }) => OAuthEnvironmentHandler.mapConfig(config, envConfig => { const clientId = envConfig.getString('clientId'); const clientSecret = envConfig.getString('clientSecret'); @@ -205,12 +280,38 @@ export const createMicrosoftProvider = ( const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`; const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; + const catalogIdentityClient = new CatalogIdentityClient({ + catalogApi, + tokenIssuer, + }); + + const authHandler: AuthHandler = options?.authHandler + ? options.authHandler + : async ({ fullProfile, params }) => ({ + profile: makeProfileInfo(fullProfile, params.id_token), + }); + + const signInResolverFn = + options?.signIn?.resolver ?? microsoftDefaultSignInResolver; + + const signInResolver: SignInResolver = info => + signInResolverFn(info, { + catalogIdentityClient, + tokenIssuer, + logger, + }); + const provider = new MicrosoftAuthProvider({ clientId, clientSecret, callbackUrl, authorizationUrl, tokenUrl, + authHandler, + signInResolver, + catalogIdentityClient, + logger, + tokenIssuer, }); return OAuthAdapter.fromConfig(globalConfig, provider, { diff --git a/plugins/badges-backend/CHANGELOG.md b/plugins/badges-backend/CHANGELOG.md index 6ba2512b86..9b6a9bf433 100644 --- a/plugins/badges-backend/CHANGELOG.md +++ b/plugins/badges-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-badges-backend +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + - @backstage/catalog-client@0.3.16 + ## 0.1.7 ### Patch Changes diff --git a/plugins/badges-backend/api-report.md b/plugins/badges-backend/api-report.md index 1523d685b4..19f5a956e4 100644 --- a/plugins/badges-backend/api-report.md +++ b/plugins/badges-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 { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { Entity } from '@backstage/catalog-model'; @@ -12,65 +11,71 @@ import { PluginEndpointDiscovery } from '@backstage/backend-common'; // @public (undocumented) export interface Badge { - color?: string; - description?: string; - kind?: 'entity'; - label: string; - labelColor?: string; - link?: string; - message: string; - style?: BadgeStyle; + color?: string; + description?: string; + kind?: 'entity'; + label: string; + labelColor?: string; + link?: string; + message: string; + style?: BadgeStyle; } // @public (undocumented) -export const BADGE_STYLES: readonly ["plastic", "flat", "flat-square", "for-the-badge", "social"]; +export const BADGE_STYLES: readonly [ + 'plastic', + 'flat', + 'flat-square', + 'for-the-badge', + 'social', +]; // @public (undocumented) export type BadgeBuilder = { - getBadges(): Promise; - createBadgeJson(options: BadgeOptions): Promise; - createBadgeSvg(options: BadgeOptions): Promise; + getBadges(): Promise; + createBadgeJson(options: BadgeOptions): Promise; + createBadgeSvg(options: BadgeOptions): Promise; }; // @public (undocumented) export interface BadgeContext { - // (undocumented) - badgeUrl: string; - // (undocumented) - config: Config; - // (undocumented) - entity?: Entity; + // (undocumented) + badgeUrl: string; + // (undocumented) + config: Config; + // (undocumented) + entity?: Entity; } // @public (undocumented) export interface BadgeFactories { - // (undocumented) - [id: string]: BadgeFactory; + // (undocumented) + [id: string]: BadgeFactory; } // @public (undocumented) export interface BadgeFactory { - // (undocumented) - createBadge(context: BadgeContext): Badge; + // (undocumented) + createBadge(context: BadgeContext): Badge; } // @public (undocumented) export type BadgeInfo = { - id: string; + id: string; }; // @public (undocumented) export type BadgeOptions = { - badgeInfo: BadgeInfo; - context: BadgeContext; + badgeInfo: BadgeInfo; + context: BadgeContext; }; // @public (undocumented) export type BadgeSpec = { - id: string; - badge: Badge; - url: string; - markdown: string; + id: string; + badge: Badge; + url: string; + markdown: string; }; // @public (undocumented) @@ -84,30 +89,28 @@ export function createRouter(options: RouterOptions): Promise; // @public (undocumented) export class DefaultBadgeBuilder implements BadgeBuilder { - constructor(factories: BadgeFactories); - // (undocumented) - createBadgeJson(options: BadgeOptions): Promise; - // (undocumented) - createBadgeSvg(options: BadgeOptions): Promise; - // (undocumented) - getBadges(): Promise; - } + constructor(factories: BadgeFactories); + // (undocumented) + createBadgeJson(options: BadgeOptions): Promise; + // (undocumented) + createBadgeSvg(options: BadgeOptions): Promise; + // (undocumented) + getBadges(): Promise; +} // @public (undocumented) export interface RouterOptions { - // (undocumented) - badgeBuilder?: BadgeBuilder; - // (undocumented) - badgeFactories?: BadgeFactories; - // (undocumented) - catalog?: CatalogApi; - // (undocumented) - config: Config; - // (undocumented) - discovery: PluginEndpointDiscovery; + // (undocumented) + badgeBuilder?: BadgeBuilder; + // (undocumented) + badgeFactories?: BadgeFactories; + // (undocumented) + catalog?: CatalogApi; + // (undocumented) + config: Config; + // (undocumented) + discovery: PluginEndpointDiscovery; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/badges-backend/package.json b/plugins/badges-backend/package.json index 99bdb25a04..68b4cdc9fb 100644 --- a/plugins/badges-backend/package.json +++ b/plugins/badges-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges-backend", - "version": "0.1.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,9 +30,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", - "@backstage/catalog-client": "^0.3.13", - "@backstage/catalog-model": "^0.8.3", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", "@types/express": "^4.17.6", diff --git a/plugins/badges/CHANGELOG.md b/plugins/badges/CHANGELOG.md index 914c29deec..e855201853 100644 --- a/plugins/badges/CHANGELOG.md +++ b/plugins/badges/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-badges +## 0.2.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.2.3 ### Patch Changes diff --git a/plugins/badges/api-report.md b/plugins/badges/api-report.md index 1dbb256fd1..7e70f65b0e 100644 --- a/plugins/badges/api-report.md +++ b/plugins/badges/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 { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -10,12 +11,13 @@ import { BackstagePlugin } from '@backstage/core-plugin-api'; export const badgesPlugin: BackstagePlugin<{}, {}>; // @public (undocumented) -export const EntityBadgesDialog: ({ open, onClose }: { - open: boolean; - onClose?: (() => any) | undefined; +export const EntityBadgesDialog: ({ + open, + onClose, +}: { + open: boolean; + onClose?: (() => any) | undefined; }) => JSX.Element; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/badges/package.json b/plugins/badges/package.json index cebc5332e2..67e2ef90e3 100644 --- a/plugins/badges/package.json +++ b/plugins/badges/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-badges", - "version": "0.2.3", + "version": "0.2.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,11 +20,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -37,7 +37,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/bitrise/CHANGELOG.md b/plugins/bitrise/CHANGELOG.md index aaf6e8658c..ab9d842f1f 100644 --- a/plugins/bitrise/CHANGELOG.md +++ b/plugins/bitrise/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-bitrise +## 0.1.6 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.1.5 ### Patch Changes diff --git a/plugins/bitrise/api-report.md b/plugins/bitrise/api-report.md index 15c3c74c99..66adb0ec68 100644 --- a/plugins/bitrise/api-report.md +++ b/plugins/bitrise/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 { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; @@ -16,7 +17,5 @@ export const EntityBitriseContent: () => JSX.Element; // @public (undocumented) export const isBitriseAvailable: (entity: Entity) => boolean; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/bitrise/package.json b/plugins/bitrise/package.json index 450ddaca85..a73fa64797 100644 --- a/plugins/bitrise/package.json +++ b/plugins/bitrise/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-bitrise", - "version": "0.1.5", + "version": "0.1.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -20,10 +20,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -40,7 +40,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-backend-module-ldap/CHANGELOG.md b/plugins/catalog-backend-module-ldap/CHANGELOG.md index 3806a6b5d3..bd090ff847 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.2.0 + +### Minor Changes + +- b055ef88a: Add extension points to the `LdapOrgReaderProcessor` to make it possible to do more advanced modifications + of the ingested users and groups. + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-backend@0.12.0 + ## 0.1.1 ### Patch Changes diff --git a/plugins/catalog-backend-module-ldap/api-report.md b/plugins/catalog-backend-module-ldap/api-report.md index 446dc2df52..f18ace927c 100644 --- a/plugins/catalog-backend-module-ldap/api-report.md +++ b/plugins/catalog-backend-module-ldap/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 { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Client } from 'ldapjs'; @@ -17,79 +16,159 @@ import { SearchOptions } from 'ldapjs'; import { UserEntity } from '@backstage/catalog-model'; // @public (undocumented) -export function defaultGroupTransformer(vendor: LdapVendor, config: GroupConfig, entry: SearchEntry): Promise; +export function defaultGroupTransformer( + vendor: LdapVendor, + config: GroupConfig, + entry: SearchEntry, +): Promise; // @public (undocumented) -export function defaultUserTransformer(vendor: LdapVendor, config: UserConfig, entry: SearchEntry): Promise; +export function defaultUserTransformer( + vendor: LdapVendor, + config: UserConfig, + entry: SearchEntry, +): Promise; // @public -export type GroupTransformer = (vendor: LdapVendor, config: GroupConfig, group: SearchEntry) => Promise; - -// @public -export const LDAP_DN_ANNOTATION = "backstage.io/ldap-dn"; - -// @public -export const LDAP_RDN_ANNOTATION = "backstage.io/ldap-rdn"; - -// @public -export const LDAP_UUID_ANNOTATION = "backstage.io/ldap-uuid"; - -// @public -export class LdapClient { - constructor(client: Client); - // (undocumented) - static create(logger: Logger_2, target: string, bind?: BindConfig): Promise; - getRootDSE(): Promise; - getVendor(): Promise; - search(dn: string, options: SearchOptions): Promise; - } - -// @public -export class LdapOrgReaderProcessor implements CatalogProcessor { - constructor(options: { - providers: LdapProviderConfig[]; - logger: Logger_2; - groupTransformer?: GroupTransformer; - userTransformer?: UserTransformer; - }); - // (undocumented) - static fromConfig(config: Config, options: { - logger: Logger_2; - groupTransformer?: GroupTransformer; - userTransformer?: UserTransformer; - }): LdapOrgReaderProcessor; - // (undocumented) - readLocation(location: LocationSpec, _optional: boolean, emit: CatalogProcessorEmit): Promise; - } - -// @public -export type LdapProviderConfig = { - target: string; - bind?: BindConfig; - users: UserConfig; - groups: GroupConfig; +export type GroupConfig = { + dn: string; + options: SearchOptions; + set?: { + [path: string]: JsonValue; + }; + map: { + rdn: string; + name: string; + description: string; + type: string; + displayName: string; + email?: string; + picture?: string; + memberOf: string; + members: string; + }; }; // @public -export function mapStringAttr(entry: SearchEntry, vendor: LdapVendor, attributeName: string | undefined, setter: (value: string) => void): void; +export type GroupTransformer = ( + vendor: LdapVendor, + config: GroupConfig, + group: SearchEntry, +) => Promise; + +// @public +export const LDAP_DN_ANNOTATION = 'backstage.io/ldap-dn'; + +// @public +export const LDAP_RDN_ANNOTATION = 'backstage.io/ldap-rdn'; + +// @public +export const LDAP_UUID_ANNOTATION = 'backstage.io/ldap-uuid'; + +// @public +export class LdapClient { + constructor(client: Client); + // (undocumented) + static create( + logger: Logger_2, + target: string, + bind?: BindConfig, + ): Promise; + getRootDSE(): Promise; + getVendor(): Promise; + search(dn: string, options: SearchOptions): Promise; +} + +// @public +export class LdapOrgReaderProcessor implements CatalogProcessor { + constructor(options: { + providers: LdapProviderConfig[]; + logger: Logger_2; + groupTransformer?: GroupTransformer; + userTransformer?: UserTransformer; + }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + groupTransformer?: GroupTransformer; + userTransformer?: UserTransformer; + }, + ): LdapOrgReaderProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public +export type LdapProviderConfig = { + target: string; + bind?: BindConfig; + users: UserConfig; + groups: GroupConfig; +}; + +// @public +export type LdapVendor = { + dnAttributeName: string; + uuidAttributeName: string; + decodeStringAttribute: (entry: SearchEntry, name: string) => string[]; +}; + +// @public +export function mapStringAttr( + entry: SearchEntry, + vendor: LdapVendor, + attributeName: string | undefined, + setter: (value: string) => void, +): void; // @public export function readLdapConfig(config: Config): LdapProviderConfig[]; // @public -export function readLdapOrg(client: LdapClient, userConfig: UserConfig, groupConfig: GroupConfig, options: { +export function readLdapOrg( + client: LdapClient, + userConfig: UserConfig, + groupConfig: GroupConfig, + options: { groupTransformer?: GroupTransformer; userTransformer?: UserTransformer; logger: Logger_2; -}): Promise<{ - users: UserEntity[]; - groups: GroupEntity[]; + }, +): Promise<{ + users: UserEntity[]; + groups: GroupEntity[]; }>; // @public -export type UserTransformer = (vendor: LdapVendor, config: UserConfig, user: SearchEntry) => Promise; +export type UserConfig = { + dn: string; + options: SearchOptions; + set?: { + [path: string]: JsonValue; + }; + map: { + rdn: string; + name: string; + description?: string; + displayName: string; + email: string; + picture?: string; + memberOf: string; + }; +}; +// @public +export type UserTransformer = ( + vendor: LdapVendor, + config: UserConfig, + user: SearchEntry, +) => Promise; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/catalog-backend-module-ldap/package.json b/plugins/catalog-backend-module-ldap/package.json index 4d840b7f48..27969ca463 100644 --- a/plugins/catalog-backend-module-ldap/package.json +++ b/plugins/catalog-backend-module-ldap/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-ldap", - "version": "0.1.1", + "version": "0.2.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,9 +28,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.3", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/plugin-catalog-backend": "^0.11.0", + "@backstage/plugin-catalog-backend": "^0.12.0", "@types/ldapjs": "^1.0.10", "ldapjs": "^2.2.0", "lodash": "^4.17.15", diff --git a/plugins/catalog-backend-module-ldap/src/ldap/index.ts b/plugins/catalog-backend-module-ldap/src/ldap/index.ts index a9f127a3a7..70500299ae 100644 --- a/plugins/catalog-backend-module-ldap/src/ldap/index.ts +++ b/plugins/catalog-backend-module-ldap/src/ldap/index.ts @@ -17,7 +17,8 @@ export { LdapClient } from './client'; export { mapStringAttr } from './util'; export { readLdapConfig } from './config'; -export type { LdapProviderConfig } from './config'; +export type { LdapProviderConfig, GroupConfig, UserConfig } from './config'; +export type { LdapVendor } from './vendors'; export { LDAP_DN_ANNOTATION, LDAP_RDN_ANNOTATION, diff --git a/plugins/catalog-backend-module-msgraph/CHANGELOG.md b/plugins/catalog-backend-module-msgraph/CHANGELOG.md index 6de8b0ec07..1f23da520f 100644 --- a/plugins/catalog-backend-module-msgraph/CHANGELOG.md +++ b/plugins/catalog-backend-module-msgraph/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-backend-module-msgraph +## 0.2.1 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-backend@0.12.0 + ## 0.2.0 ### Minor Changes diff --git a/plugins/catalog-backend-module-msgraph/api-report.md b/plugins/catalog-backend-module-msgraph/api-report.md index c755514b48..1fae2731ad 100644 --- a/plugins/catalog-backend-module-msgraph/api-report.md +++ b/plugins/catalog-backend-module-msgraph/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 { CatalogProcessor } from '@backstage/plugin-catalog-backend'; import { CatalogProcessorEmit } from '@backstage/plugin-catalog-backend'; import { Config } from '@backstage/config'; @@ -15,108 +14,143 @@ import * as msal from '@azure/msal-node'; import { UserEntity } from '@backstage/catalog-model'; // @public (undocumented) -export function defaultGroupTransformer(group: MicrosoftGraph.Group, groupPhoto?: string): Promise; +export function defaultGroupTransformer( + group: MicrosoftGraph.Group, + groupPhoto?: string, +): Promise; // @public (undocumented) -export function defaultOrganizationTransformer(organization: MicrosoftGraph.Organization): Promise; +export function defaultOrganizationTransformer( + organization: MicrosoftGraph.Organization, +): Promise; // @public (undocumented) -export function defaultUserTransformer(user: MicrosoftGraph.User, userPhoto?: string): Promise; +export function defaultUserTransformer( + user: MicrosoftGraph.User, + userPhoto?: string, +): Promise; // @public (undocumented) -export type GroupTransformer = (group: MicrosoftGraph.Group, groupPhoto?: string) => Promise; +export type GroupTransformer = ( + group: MicrosoftGraph.Group, + groupPhoto?: string, +) => Promise; // @public -export const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION = "graph.microsoft.com/group-id"; +export const MICROSOFT_GRAPH_GROUP_ID_ANNOTATION = + 'graph.microsoft.com/group-id'; // @public -export const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION = "graph.microsoft.com/tenant-id"; +export const MICROSOFT_GRAPH_TENANT_ID_ANNOTATION = + 'graph.microsoft.com/tenant-id'; // @public -export const MICROSOFT_GRAPH_USER_ID_ANNOTATION = "graph.microsoft.com/user-id"; +export const MICROSOFT_GRAPH_USER_ID_ANNOTATION = 'graph.microsoft.com/user-id'; // @public (undocumented) export class MicrosoftGraphClient { - constructor(baseUrl: string, pca: msal.ConfidentialClientApplication); - // (undocumented) - static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient; - // (undocumented) - getGroupMembers(groupId: string): AsyncIterable; - // (undocumented) - getGroupPhoto(groupId: string, sizeId?: string): Promise; - // (undocumented) - getGroupPhotoWithSizeLimit(groupId: string, maxSize: number): Promise; - // (undocumented) - getGroups(query?: ODataQuery): AsyncIterable; - // (undocumented) - getOrganization(tenantId: string): Promise; - // (undocumented) - getUserPhoto(userId: string, sizeId?: string): Promise; - // (undocumented) - getUserPhotoWithSizeLimit(userId: string, maxSize: number): Promise; - // (undocumented) - getUserProfile(userId: string): Promise; - // (undocumented) - getUsers(query?: ODataQuery): AsyncIterable; - // (undocumented) - requestApi(path: string, query?: ODataQuery): Promise; - // (undocumented) - requestCollection(path: string, query?: ODataQuery): AsyncIterable; - // (undocumented) - requestRaw(url: string): Promise; + constructor(baseUrl: string, pca: msal.ConfidentialClientApplication); + // (undocumented) + static create(config: MicrosoftGraphProviderConfig): MicrosoftGraphClient; + // (undocumented) + getGroupMembers(groupId: string): AsyncIterable; + // (undocumented) + getGroupPhoto(groupId: string, sizeId?: string): Promise; + // (undocumented) + getGroupPhotoWithSizeLimit( + groupId: string, + maxSize: number, + ): Promise; + // (undocumented) + getGroups(query?: ODataQuery): AsyncIterable; + // (undocumented) + getOrganization(tenantId: string): Promise; + // (undocumented) + getUserPhoto(userId: string, sizeId?: string): Promise; + // (undocumented) + getUserPhotoWithSizeLimit( + userId: string, + maxSize: number, + ): Promise; + // (undocumented) + getUserProfile(userId: string): Promise; + // (undocumented) + getUsers(query?: ODataQuery): AsyncIterable; + // (undocumented) + requestApi(path: string, query?: ODataQuery): Promise; + // (undocumented) + requestCollection(path: string, query?: ODataQuery): AsyncIterable; + // (undocumented) + requestRaw(url: string): Promise; } // @public export class MicrosoftGraphOrgReaderProcessor implements CatalogProcessor { - constructor(options: { - providers: MicrosoftGraphProviderConfig[]; - logger: Logger_2; - groupTransformer?: GroupTransformer; - }); - // (undocumented) - static fromConfig(config: Config, options: { - logger: Logger_2; - groupTransformer?: GroupTransformer; - }): MicrosoftGraphOrgReaderProcessor; - // (undocumented) - readLocation(location: LocationSpec, _optional: boolean, emit: CatalogProcessorEmit): Promise; + constructor(options: { + providers: MicrosoftGraphProviderConfig[]; + logger: Logger_2; + groupTransformer?: GroupTransformer; + }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + groupTransformer?: GroupTransformer; + }, + ): MicrosoftGraphOrgReaderProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; } // @public export type MicrosoftGraphProviderConfig = { - target: string; - authority?: string; - tenantId: string; - clientId: string; - clientSecret: string; - userFilter?: string; - groupFilter?: string; + target: string; + authority?: string; + tenantId: string; + clientId: string; + clientSecret: string; + userFilter?: string; + groupFilter?: string; }; // @public (undocumented) export function normalizeEntityName(name: string): string; // @public (undocumented) -export type OrganizationTransformer = (organization: MicrosoftGraph.Organization) => Promise; +export type OrganizationTransformer = ( + organization: MicrosoftGraph.Organization, +) => Promise; // @public (undocumented) -export function readMicrosoftGraphConfig(config: Config): MicrosoftGraphProviderConfig[]; +export function readMicrosoftGraphConfig( + config: Config, +): MicrosoftGraphProviderConfig[]; // @public (undocumented) -export function readMicrosoftGraphOrg(client: MicrosoftGraphClient, tenantId: string, options: { +export function readMicrosoftGraphOrg( + client: MicrosoftGraphClient, + tenantId: string, + options: { userFilter?: string; groupFilter?: string; groupTransformer?: GroupTransformer; logger: Logger_2; -}): Promise<{ - users: UserEntity[]; - groups: GroupEntity[]; + }, +): Promise<{ + users: UserEntity[]; + groups: GroupEntity[]; }>; // @public (undocumented) -export type UserTransformer = (user: MicrosoftGraph.User, userPhoto?: string) => Promise; - +export type UserTransformer = ( + user: MicrosoftGraph.User, + userPhoto?: string, +) => Promise; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/catalog-backend-module-msgraph/package.json b/plugins/catalog-backend-module-msgraph/package.json index 1c1886d802..3d24b9efd6 100644 --- a/plugins/catalog-backend-module-msgraph/package.json +++ b/plugins/catalog-backend-module-msgraph/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend-module-msgraph", - "version": "0.2.0", + "version": "0.2.1", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,9 +29,9 @@ }, "dependencies": { "@azure/msal-node": "^1.1.0", - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/plugin-catalog-backend": "^0.11.0", + "@backstage/plugin-catalog-backend": "^0.12.0", "@microsoft/microsoft-graph-types": "^1.25.0", "cross-fetch": "^3.0.6", "lodash": "^4.17.15", @@ -40,7 +40,7 @@ "qs": "^6.9.4" }, "devDependencies": { - "@backstage/backend-common": "^0.8.4", + "@backstage/backend-common": "^0.8.5", "@backstage/cli": "^0.7.3", "@backstage/test-utils": "^0.1.14", "@types/lodash": "^4.14.151", diff --git a/plugins/catalog-backend/CHANGELOG.md b/plugins/catalog-backend/CHANGELOG.md index 174f279b11..bc8de4878d 100644 --- a/plugins/catalog-backend/CHANGELOG.md +++ b/plugins/catalog-backend/CHANGELOG.md @@ -1,5 +1,82 @@ # @backstage/plugin-catalog-backend +## 0.12.0 + +### Minor Changes + +- 60e830222: Support for `Template` kinds with version `backstage.io/v1alpha1` has now been removed. This means that the old method of running templates with `Preparers`, `Templaters` and `Publishers` has also been removed. If you had any logic in these abstractions, they should now be moved to `actions` instead, and you can find out more about those in the [documentation](https://backstage.io/docs/features/software-templates/writing-custom-actions) + + If you need any help migrating existing templates, there's a [migration guide](https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2). Reach out to us on Discord in the #support channel if you're having problems. + + The `scaffolder-backend` now no longer requires these `Preparers`, `Templaters`, and `Publishers` to be passed in, now all it needs is the `containerRunner`. + + Please update your `packages/backend/src/plugins/scaffolder.ts` like the following + + ```diff + - import { + - DockerContainerRunner, + - SingleHostDiscovery, + - } from '@backstage/backend-common'; + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { CatalogClient } from '@backstage/catalog-client'; + - import { + - CookieCutter, + - CreateReactAppTemplater, + - createRouter, + - Preparers, + - Publishers, + - Templaters, + - } from '@backstage/plugin-scaffolder-backend'; + + import { createRouter } from '@backstage/plugin-scaffolder-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import type { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + config, + database, + reader, + + discovery, + }: PluginEnvironment): Promise { + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + - const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const templaters = new Templaters(); + + - templaters.register('cookiecutter', cookiecutterTemplater); + - templaters.register('cra', craTemplater); + - + - const preparers = await Preparers.fromConfig(config, { logger }); + - const publishers = await Publishers.fromConfig(config, { logger }); + + - const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + - preparers, + - templaters, + - publishers, + + containerRunner, + logger, + config, + database, + + ``` + +### Patch Changes + +- f7134c368: bump sqlite3 to 5.0.1 +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- 2d41b6993: Make use of the new `readUrl` method on `UrlReader` from `@backstage/backend-common`. +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + - @backstage/plugin-search-backend-node@0.3.0 + - @backstage/catalog-client@0.3.16 + ## 0.11.0 ### Minor Changes diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md new file mode 100644 index 0000000000..ae7559f5d1 --- /dev/null +++ b/plugins/catalog-backend/api-report.md @@ -0,0 +1,881 @@ +## API Report File for "@backstage/plugin-catalog-backend" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { Account } from 'aws-sdk/clients/organizations'; +import { BitbucketIntegration } from '@backstage/integration'; +import { Config } from '@backstage/config'; +import { DocumentCollator } from '@backstage/search-common'; +import { Entity } from '@backstage/catalog-model'; +import { EntityName } from '@backstage/catalog-model'; +import { EntityPolicy } from '@backstage/catalog-model'; +import { EntityRelationSpec } from '@backstage/catalog-model'; +import express from 'express'; +import { IndexableDocument } from '@backstage/search-common'; +import { JsonObject } from '@backstage/config'; +import { JsonValue } from '@backstage/config'; +import { Knex } from 'knex'; +import { Location as Location_2 } from '@backstage/catalog-model'; +import { LocationSpec } from '@backstage/catalog-model'; +import { Logger as Logger_2 } from 'winston'; +import { Organizations } from 'aws-sdk'; +import { PluginDatabaseManager } from '@backstage/backend-common'; +import { PluginEndpointDiscovery } from '@backstage/backend-common'; +import { ResourceEntityV1alpha1 } from '@backstage/catalog-model'; +import { ScmIntegrationRegistry } from '@backstage/integration'; +import { ScmIntegrations } from '@backstage/integration'; +import { UrlReader } from '@backstage/backend-common'; +import { Validators } from '@backstage/catalog-model'; + +// @public (undocumented) +export type AddLocationResult = { + location: Location_2; + entities: Entity[]; +}; + +// @public (undocumented) +export type AnalyzeLocationRequest = { + location: LocationSpec; +}; + +// @public (undocumented) +export type AnalyzeLocationResponse = { + existingEntityFiles: AnalyzeLocationExistingEntity[]; + generateEntities: AnalyzeLocationGenerateEntity[]; +}; + +// @public (undocumented) +export class AnnotateLocationEntityProcessor implements CatalogProcessor { + constructor(options: Options_2); + // (undocumented) + preProcessEntity( + entity: Entity, + location: LocationSpec, + _: CatalogProcessorEmit, + originLocation: LocationSpec, + ): Promise; +} + +// @public (undocumented) +export class AnnotateScmSlugEntityProcessor implements CatalogProcessor { + constructor(opts: { scmIntegrationRegistry: ScmIntegrationRegistry }); + // (undocumented) + static fromConfig(config: Config): AnnotateScmSlugEntityProcessor; + // (undocumented) + preProcessEntity(entity: Entity, location: LocationSpec): Promise; +} + +// @public +export class AwsOrganizationCloudAccountProcessor implements CatalogProcessor { + constructor(options: { + provider: AwsOrganizationProviderConfig; + logger: Logger_2; + }); + // (undocumented) + extractInformationFromArn( + arn: string, + ): { + accountId: string; + organizationId: string; + }; + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + }, + ): AwsOrganizationCloudAccountProcessor; + // (undocumented) + getAwsAccounts(): Promise; + // (undocumented) + logger: Logger_2; + // (undocumented) + mapAccountToComponent(account: Account): ResourceEntityV1alpha1; + // (undocumented) + normalizeName(name: string): string; + // (undocumented) + organizations: Organizations; + // (undocumented) + provider: AwsOrganizationProviderConfig; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public (undocumented) +export class BitbucketDiscoveryProcessor implements CatalogProcessor { + constructor(options: { + integrations: ScmIntegrationRegistry; + parser?: BitbucketRepositoryParser; + logger: Logger_2; + }); + // (undocumented) + static fromConfig( + config: Config, + options: { + parser?: BitbucketRepositoryParser; + logger: Logger_2; + }, + ): BitbucketDiscoveryProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public (undocumented) +export type BitbucketRepositoryParser = (options: { + integration: BitbucketIntegration; + target: string; + logger: Logger_2; +}) => AsyncIterable; + +// @public (undocumented) +export class BuiltinKindsEntityProcessor implements CatalogProcessor { + // (undocumented) + postProcessEntity( + entity: Entity, + _location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise; + // (undocumented) + validateEntityKind(entity: Entity): Promise; +} + +// @public +export class CatalogBuilder { + constructor(env: CatalogEnvironment); + addEntityPolicy(...policies: EntityPolicy[]): CatalogBuilder; + addProcessor(...processors: CatalogProcessor[]): CatalogBuilder; + build(): Promise<{ + entitiesCatalog: EntitiesCatalog; + locationsCatalog: LocationsCatalog; + higherOrderOperation: HigherOrderOperation; + locationAnalyzer: LocationAnalyzer; + }>; + // (undocumented) + static create(env: CatalogEnvironment): Promise; + replaceEntityPolicies(policies: EntityPolicy[]): CatalogBuilder; + replaceProcessors(processors: CatalogProcessor[]): CatalogBuilder; + setEntityDataParser(parser: CatalogProcessorParser): CatalogBuilder; + setFieldFormatValidators(validators: Partial): CatalogBuilder; + setPlaceholderResolver( + key: string, + resolver: PlaceholderResolver, + ): CatalogBuilder; +} + +// @public (undocumented) +export interface CatalogEntityDocument extends IndexableDocument { + // (undocumented) + componentType: string; + // (undocumented) + kind: string; + // (undocumented) + lifecycle: string; + // (undocumented) + namespace: string; + // (undocumented) + owner: string; +} + +// @public (undocumented) +export interface CatalogProcessingOrchestrator { + // (undocumented) + process(request: EntityProcessingRequest): Promise; +} + +// @public (undocumented) +export type CatalogProcessor = { + readLocation?( + location: LocationSpec, + optional: boolean, + emit: CatalogProcessorEmit, + parser: CatalogProcessorParser, + ): Promise; + preProcessEntity?( + entity: Entity, + location: LocationSpec, + emit: CatalogProcessorEmit, + originLocation: LocationSpec, + ): Promise; + validateEntityKind?(entity: Entity): Promise; + postProcessEntity?( + entity: Entity, + location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise; + handleError?( + error: Error, + location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise; +}; + +// @public (undocumented) +export type CatalogProcessorEmit = (generated: CatalogProcessorResult) => void; + +// @public (undocumented) +export type CatalogProcessorEntityResult = { + type: 'entity'; + entity: Entity; + location: LocationSpec; +}; + +// @public (undocumented) +export type CatalogProcessorErrorResult = { + type: 'error'; + error: Error; + location: LocationSpec; +}; + +// @public (undocumented) +export type CatalogProcessorLocationResult = { + type: 'location'; + location: LocationSpec; + optional: boolean; +}; + +// @public +export type CatalogProcessorParser = (options: { + data: Buffer; + location: LocationSpec; +}) => AsyncIterable; + +// @public (undocumented) +export type CatalogProcessorRelationResult = { + type: 'relation'; + relation: EntityRelationSpec; + entityRef?: string; +}; + +// @public (undocumented) +export type CatalogProcessorResult = + | CatalogProcessorLocationResult + | CatalogProcessorEntityResult + | CatalogProcessorRelationResult + | CatalogProcessorErrorResult; + +// @public (undocumented) +export class CodeOwnersProcessor implements CatalogProcessor { + constructor(options: { + integrations: ScmIntegrations; + logger: Logger_2; + reader: UrlReader; + }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + reader: UrlReader; + }, + ): CodeOwnersProcessor; + // (undocumented) + preProcessEntity(entity: Entity, location: LocationSpec): Promise; +} + +// @public +export class CommonDatabase implements Database { + constructor(database: Knex, logger: Logger_2); + // (undocumented) + addEntities( + txOpaque: Transaction, + request: DbEntityRequest[], + ): Promise; + // (undocumented) + addLocation( + txOpaque: Transaction, + location: Location_2, + ): Promise; + // (undocumented) + addLocationUpdateLogEvent( + locationId: string, + status: DatabaseLocationUpdateLogStatus, + entityName?: string | string[], + message?: string, + ): Promise; + // (undocumented) + entities( + txOpaque: Transaction, + request?: DbEntitiesRequest, + ): Promise; + // (undocumented) + entityByName( + txOpaque: Transaction, + name: EntityName, + ): Promise; + // (undocumented) + entityByUid( + txOpaque: Transaction, + uid: string, + ): Promise; + // (undocumented) + location(id: string): Promise; + // (undocumented) + locationHistory(id: string): Promise; + // (undocumented) + locations(): Promise; + // (undocumented) + removeEntityByUid(txOpaque: Transaction, uid: string): Promise; + // (undocumented) + removeLocation(txOpaque: Transaction, id: string): Promise; + // (undocumented) + setRelations( + txOpaque: Transaction, + originatingEntityId: string, + relations: EntityRelationSpec[], + ): Promise; + // (undocumented) + transaction(fn: (tx: Transaction) => Promise): Promise; + // (undocumented) + updateEntity( + txOpaque: Transaction, + request: DbEntityRequest, + matchingEtag?: string, + matchingGeneration?: number, + ): Promise; +} + +// @public (undocumented) +export function createNextRouter( + options: RouterOptions_2, +): Promise; + +// @public (undocumented) +export function createRouter(options: RouterOptions): Promise; + +// @public +export type Database = { + transaction(fn: (tx: Transaction) => Promise): Promise; + addEntities( + tx: Transaction, + request: DbEntityRequest[], + ): Promise; + updateEntity( + tx: Transaction, + request: DbEntityRequest, + matchingEtag?: string, + matchingGeneration?: number, + ): Promise; + entities( + tx: Transaction, + request?: DbEntitiesRequest, + ): Promise; + entityByName( + tx: Transaction, + name: EntityName, + ): Promise; + entityByUid( + tx: Transaction, + uid: string, + ): Promise; + removeEntityByUid(tx: Transaction, uid: string): Promise; + setRelations( + tx: Transaction, + entityUid: string, + relations: EntityRelationSpec[], + ): Promise; + addLocation(tx: Transaction, location: Location_2): Promise; + removeLocation(tx: Transaction, id: string): Promise; + location(id: string): Promise; + locations(): Promise; + locationHistory(id: string): Promise; + addLocationUpdateLogEvent( + locationId: string, + status: DatabaseLocationUpdateLogStatus, + entityName?: string | string[], + message?: string, + ): Promise; +}; + +// @public (undocumented) +export class DatabaseEntitiesCatalog implements EntitiesCatalog { + constructor(database: Database, logger: Logger_2); + // (undocumented) + batchAddOrUpdateEntities( + requests: EntityUpsertRequest[], + options?: { + locationId?: string; + dryRun?: boolean; + outputEntities?: boolean; + }, + ): Promise; + // (undocumented) + entities(request?: EntitiesRequest): Promise; + // (undocumented) + removeEntityByUid(uid: string): Promise; +} + +// @public (undocumented) +export class DatabaseLocationsCatalog implements LocationsCatalog { + constructor(database: Database); + // (undocumented) + addLocation(location: Location_2): Promise; + // (undocumented) + location(id: string): Promise; + // (undocumented) + locationHistory(id: string): Promise; + // (undocumented) + locations(): Promise; + // (undocumented) + logUpdateFailure( + locationId: string, + error?: Error, + entityName?: string, + ): Promise; + // (undocumented) + logUpdateSuccess( + locationId: string, + entityName?: string | string[], + ): Promise; + // (undocumented) + removeLocation(id: string): Promise; +} + +// @public (undocumented) +export class DatabaseManager { + // (undocumented) + static createDatabase( + knex: Knex, + options?: Partial, + ): Promise; + // (undocumented) + static createInMemoryDatabase(): Promise; + // (undocumented) + static createInMemoryDatabaseConnection(): Promise; + // (undocumented) + static createTestDatabase(): Promise; + // (undocumented) + static createTestDatabaseConnection(): Promise; +} + +// @public (undocumented) +export type DbEntityRequest = { + locationId?: string; + entity: Entity; + relations: EntityRelationSpec[]; +}; + +// @public (undocumented) +export type DbEntityResponse = { + locationId?: string; + entity: Entity; +}; + +// @public (undocumented) +export class DefaultCatalogCollator implements DocumentCollator { + constructor({ + discovery, + locationTemplate, + }: { + discovery: PluginEndpointDiscovery; + locationTemplate?: string; + }); + // (undocumented) + protected applyArgsToFormat( + format: string, + args: Record, + ): string; + // (undocumented) + protected discovery: PluginEndpointDiscovery; + // (undocumented) + execute(): Promise; + // (undocumented) + protected locationTemplate: string; + // (undocumented) + readonly type: string; +} + +// @public (undocumented) +export class DefaultCatalogProcessingOrchestrator + implements CatalogProcessingOrchestrator { + constructor(options: { + processors: CatalogProcessor[]; + integrations: ScmIntegrationRegistry; + logger: Logger_2; + parser: CatalogProcessorParser; + policy: EntityPolicy; + }); + // (undocumented) + process(request: EntityProcessingRequest): Promise; +} + +// @public +export function durationText(startTimestamp: [number, number]): string; + +// @public (undocumented) +export type EntitiesCatalog = { + entities(request?: EntitiesRequest): Promise; + removeEntityByUid(uid: string): Promise; + batchAddOrUpdateEntities( + requests: EntityUpsertRequest[], + options?: { + locationId?: string; + dryRun?: boolean; + outputEntities?: boolean; + }, + ): Promise; +}; + +// @public +export type EntitiesSearchFilter = { + key: string; + matchValueIn?: string[]; +}; + +// @public (undocumented) +function entity( + atLocation: LocationSpec, + newEntity: Entity, +): CatalogProcessorResult; + +// @public +export type EntityFilter = { + anyOf: { + allOf: EntitiesSearchFilter[]; + }[]; +}; + +// @public +export type EntityPagination = { + limit?: number; + offset?: number; + after?: string; +}; + +// @public (undocumented) +export type EntityProcessingRequest = { + entity: Entity; + state: Map; +}; + +// @public (undocumented) +export type EntityProcessingResult = + | { + ok: true; + state: Map; + completedEntity: Entity; + deferredEntities: DeferredEntity[]; + relations: EntityRelationSpec[]; + errors: Error[]; + } + | { + ok: false; + errors: Error[]; + }; + +// @public (undocumented) +export class FileReaderProcessor implements CatalogProcessor { + // (undocumented) + readLocation( + location: LocationSpec, + optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public (undocumented) +function generalError( + atLocation: LocationSpec, + message: string, +): CatalogProcessorResult; + +// @public +export class GithubDiscoveryProcessor implements CatalogProcessor { + constructor(options: { integrations: ScmIntegrations; logger: Logger_2 }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + }, + ): GithubDiscoveryProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @alpha +export class GithubMultiOrgReaderProcessor implements CatalogProcessor { + constructor(options: { + integrations: ScmIntegrations; + logger: Logger_2; + orgs: GithubMultiOrgConfig; + }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + }, + ): GithubMultiOrgReaderProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public +export class GithubOrgReaderProcessor implements CatalogProcessor { + constructor(options: { integrations: ScmIntegrations; logger: Logger_2 }); + // (undocumented) + static fromConfig( + config: Config, + options: { + logger: Logger_2; + }, + ): GithubOrgReaderProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public (undocumented) +export type HigherOrderOperation = { + addLocation( + spec: LocationSpec, + options?: { + dryRun?: boolean; + }, + ): Promise; + refreshAllLocations(): Promise; +}; + +// @public +export class HigherOrderOperations implements HigherOrderOperation { + constructor( + entitiesCatalog: EntitiesCatalog, + locationsCatalog: LocationsCatalog, + locationReader: LocationReader, + logger: Logger_2, + ); + addLocation( + spec: LocationSpec, + options?: { + dryRun?: boolean; + }, + ): Promise; + refreshAllLocations(): Promise; +} + +// @public (undocumented) +function inputError( + atLocation: LocationSpec, + message: string, +): CatalogProcessorResult; + +// @public (undocumented) +function location_2( + newLocation: LocationSpec, + optional: boolean, +): CatalogProcessorResult; + +// @public (undocumented) +export type LocationAnalyzer = { + analyzeLocation( + location: AnalyzeLocationRequest, + ): Promise; +}; + +// @public (undocumented) +export class LocationEntityProcessor implements CatalogProcessor { + constructor(options: Options_3); + // (undocumented) + postProcessEntity( + entity: Entity, + location: LocationSpec, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public (undocumented) +export type LocationReader = { + read(location: LocationSpec): Promise; +}; + +// @public +export class LocationReaders implements LocationReader { + constructor(options: Options); + // (undocumented) + read(location: LocationSpec): Promise; +} + +// @public (undocumented) +export type LocationsCatalog = { + addLocation(location: Location_2): Promise; + removeLocation(id: string): Promise; + locations(): Promise; + location(id: string): Promise; + locationHistory(id: string): Promise; + logUpdateSuccess( + locationId: string, + entityName?: string | string[], + ): Promise; + logUpdateFailure( + locationId: string, + error?: Error, + entityName?: string, + ): Promise; +}; + +// @public +export class NextCatalogBuilder { + constructor(env: CatalogEnvironment_2); + addEntityPolicy(...policies: EntityPolicy[]): NextCatalogBuilder; + addEntityProvider(...providers: EntityProvider[]): NextCatalogBuilder; + addProcessor(...processors: CatalogProcessor[]): NextCatalogBuilder; + build(): Promise<{ + entitiesCatalog: EntitiesCatalog; + locationsCatalog: LocationsCatalog; + locationAnalyzer: LocationAnalyzer; + processingEngine: CatalogProcessingEngine; + locationService: LocationService; + }>; + replaceEntityPolicies(policies: EntityPolicy[]): NextCatalogBuilder; + replaceProcessors(processors: CatalogProcessor[]): NextCatalogBuilder; + setEntityDataParser(parser: CatalogProcessorParser): NextCatalogBuilder; + setFieldFormatValidators(validators: Partial): NextCatalogBuilder; + setPlaceholderResolver( + key: string, + resolver: PlaceholderResolver, + ): NextCatalogBuilder; + setRefreshIntervalSeconds(seconds: number): NextCatalogBuilder; +} + +// @public (undocumented) +function notFoundError( + atLocation: LocationSpec, + message: string, +): CatalogProcessorResult; + +// @public (undocumented) +export function parseEntityYaml( + data: Buffer, + location: LocationSpec, +): Iterable; + +// @public +export class PlaceholderProcessor implements CatalogProcessor { + constructor(options: Options_4); + // (undocumented) + preProcessEntity(entity: Entity, location: LocationSpec): Promise; +} + +// @public (undocumented) +export type PlaceholderResolver = ( + params: ResolverParams, +) => Promise; + +// @public (undocumented) +export type ReadLocationEntity = { + location: LocationSpec; + entity: Entity; + relations: EntityRelationSpec[]; +}; + +// @public (undocumented) +export type ReadLocationError = { + location: LocationSpec; + error: Error; +}; + +// @public (undocumented) +export type ReadLocationResult = { + entities: ReadLocationEntity[]; + errors: ReadLocationError[]; +}; + +// @public +export type RecursivePartial = { + [P in keyof T]?: T[P] extends (infer U)[] + ? RecursivePartial[] + : T[P] extends object + ? RecursivePartial + : T[P]; +}; + +// @public (undocumented) +function relation(spec: EntityRelationSpec): CatalogProcessorResult; + +declare namespace results { + export { + notFoundError, + inputError, + generalError, + location_2 as location, + entity, + relation, + }; +} +export { results }; + +// @public (undocumented) +export interface RouterOptions { + // (undocumented) + config: Config; + // (undocumented) + entitiesCatalog?: EntitiesCatalog; + // (undocumented) + higherOrderOperation?: HigherOrderOperation; + // (undocumented) + locationAnalyzer?: LocationAnalyzer; + // (undocumented) + locationsCatalog?: LocationsCatalog; + // (undocumented) + locationService?: LocationService; + // (undocumented) + logger: Logger_2; +} + +// @public +export function runPeriodically(fn: () => any, delayMs: number): () => void; + +// @public (undocumented) +export class StaticLocationProcessor implements StaticLocationProcessor { + constructor(staticLocations: LocationSpec[]); + // (undocumented) + static fromConfig(config: Config): StaticLocationProcessor; + // (undocumented) + readLocation( + location: LocationSpec, + _optional: boolean, + emit: CatalogProcessorEmit, + ): Promise; +} + +// @public +export type Transaction = { + rollback(): Promise; +}; + +// @public (undocumented) +export class UrlReaderProcessor implements CatalogProcessor { + constructor(options: Options_5); + // (undocumented) + readLocation( + location: LocationSpec, + optional: boolean, + emit: CatalogProcessorEmit, + parser: CatalogProcessorParser, + ): Promise; +} + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/catalog-backend/package.json b/plugins/catalog-backend/package.json index 13cd430d01..a3d0ff14da 100644 --- a/plugins/catalog-backend/package.json +++ b/plugins/catalog-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-backend", - "version": "0.11.0", + "version": "0.12.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,13 +29,13 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.4", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", - "@backstage/plugin-search-backend-node": "^0.2.2", + "@backstage/integration": "^0.5.8", + "@backstage/plugin-search-backend-node": "^0.3.0", "@backstage/search-common": "^0.1.2", "@octokit/graphql": "^4.5.8", "@types/express": "^4.17.6", @@ -46,8 +46,8 @@ "express": "^4.17.1", "express-promise-router": "^4.1.0", "fast-json-stable-stringify": "^2.1.0", - "fs-extra": "^10.0.0", - "git-url-parse": "^11.4.4", + "fs-extra": "9.1.0", + "git-url-parse": "~11.4.4", "glob": "^7.1.6", "knex": "^0.95.1", "lodash": "^4.17.15", @@ -61,7 +61,7 @@ "yup": "^0.29.3" }, "devDependencies": { - "@backstage/backend-test-utils": "^0.1.3", + "@backstage/backend-test-utils": "^0.1.4", "@backstage/cli": "^0.7.3", "@backstage/test-utils": "^0.1.14", "@types/core-js": "^2.5.4", diff --git a/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.test.ts b/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.test.ts index ccf4493dc6..4e6c646073 100644 --- a/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.test.ts +++ b/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.test.ts @@ -29,7 +29,7 @@ describe('resolveCodeOwner', () => { describe('normalizeCodeOwner', () => { it('should remove the @ symbol', () => { - expect(normalizeCodeOwner('@yoda')).toBe('yoda'); + expect(normalizeCodeOwner('@yoda')).toBe('User:yoda'); }); it('should remove org from org/team format', () => { diff --git a/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.ts b/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.ts index 886f3160b3..00645f0c9d 100644 --- a/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.ts +++ b/plugins/catalog-backend/src/ingestion/processors/codeowners/resolve.ts @@ -18,6 +18,10 @@ import * as codeowners from 'codeowners-utils'; import { CodeOwnersEntry } from 'codeowners-utils'; import { filter, get, head, pipe, reverse } from 'lodash/fp'; +const USER_PATTERN = /^@.*/; +const GROUP_PATTERN = /^@.*\/.*/; +const EMAIL_PATTERN = /^.*@.*\..*$/; + export function resolveCodeOwner( contents: string, pattern = '*', @@ -35,11 +39,11 @@ export function resolveCodeOwner( } export function normalizeCodeOwner(owner: string) { - if (owner.match(/^@.*\/.*/)) { + if (owner.match(GROUP_PATTERN)) { return owner.split('/')[1]; - } else if (owner.match(/^@.*/)) { - return owner.substring(1); - } else if (owner.match(/^.*@.*\..*$/)) { + } else if (owner.match(USER_PATTERN)) { + return `User:${owner.substring(1)}`; + } else if (owner.match(EMAIL_PATTERN)) { return owner.split('@')[0]; } diff --git a/plugins/catalog-graphql/CHANGELOG.md b/plugins/catalog-graphql/CHANGELOG.md index 1641327823..c10aa98b53 100644 --- a/plugins/catalog-graphql/CHANGELOG.md +++ b/plugins/catalog-graphql/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-catalog-graphql +## 0.2.11 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + ## 0.2.10 ### Patch Changes diff --git a/plugins/catalog-graphql/api-report.md b/plugins/catalog-graphql/api-report.md index 40e8a19b66..67beee09da 100644 --- a/plugins/catalog-graphql/api-report.md +++ b/plugins/catalog-graphql/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 { Config } from '@backstage/config'; import { GraphQLModule } from '@graphql-modules/core'; import { Logger as Logger_2 } from 'winston'; @@ -13,13 +12,11 @@ export function createModule(options: ModuleOptions): Promise; // @public (undocumented) export interface ModuleOptions { - // (undocumented) - config: Config; - // (undocumented) - logger: Logger_2; + // (undocumented) + config: Config; + // (undocumented) + logger: Logger_2; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/catalog-graphql/package.json b/plugins/catalog-graphql/package.json index d14ae5df42..039ac931f3 100644 --- a/plugins/catalog-graphql/package.json +++ b/plugins/catalog-graphql/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-graphql", - "version": "0.2.10", + "version": "0.2.11", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,8 +30,8 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.2", - "@backstage/catalog-model": "^0.8.2", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@graphql-modules/core": "^0.7.17", "apollo-server": "^2.16.1", diff --git a/plugins/catalog-import/CHANGELOG.md b/plugins/catalog-import/CHANGELOG.md index 67a512df55..fb9ae20305 100644 --- a/plugins/catalog-import/CHANGELOG.md +++ b/plugins/catalog-import/CHANGELOG.md @@ -1,5 +1,18 @@ # @backstage/plugin-catalog-import +## 0.5.12 + +### Patch Changes + +- 43a4ef644: More helpful error message when trying to import by folder from non-github +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/catalog-client@0.3.16 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.5.11 ### Patch Changes diff --git a/plugins/catalog-import/api-report.md b/plugins/catalog-import/api-report.md index f388c24a55..808d8f1909 100644 --- a/plugins/catalog-import/api-report.md +++ b/plugins/catalog-import/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -27,36 +28,50 @@ import { UseFormMethods } from 'react-hook-form'; import { UseFormOptions } from 'react-hook-form'; // @public (undocumented) -export type AnalyzeResult = { - type: 'locations'; - locations: Array<{ +export type AnalyzeResult = + | { + type: 'locations'; + locations: Array<{ target: string; entities: EntityName[]; - }>; -} | { - type: 'repository'; - url: string; - integrationType: string; - generatedEntities: PartialEntity[]; -}; + }>; + } + | { + type: 'repository'; + url: string; + integrationType: string; + generatedEntities: PartialEntity[]; + }; // @public (undocumented) -export const AutocompleteTextField: ({ name, options, required, control, errors, rules, loading, loadingText, helperText, errorHelperText, textFieldProps, }: Props_4) => JSX.Element; +export const AutocompleteTextField: ({ + name, + options, + required, + control, + errors, + rules, + loading, + loadingText, + helperText, + errorHelperText, + textFieldProps, +}: Props_4) => JSX.Element; // @public (undocumented) export interface CatalogImportApi { - // (undocumented) - analyzeUrl(url: string): Promise; - // (undocumented) - submitPullRequest(options: { - repositoryUrl: string; - fileContent: string; - title: string; - body: string; - }): Promise<{ - link: string; - location: string; - }>; + // (undocumented) + analyzeUrl(url: string): Promise; + // (undocumented) + submitPullRequest(options: { + repositoryUrl: string; + fileContent: string; + title: string; + body: string; + }): Promise<{ + link: string; + location: string; + }>; } // @public (undocumented) @@ -64,67 +79,111 @@ export const catalogImportApiRef: ApiRef; // @public (undocumented) export class CatalogImportClient implements CatalogImportApi { - constructor(options: { - discoveryApi: DiscoveryApi; - githubAuthApi: OAuthApi; - identityApi: IdentityApi; - scmIntegrationsApi: ScmIntegrationRegistry; - catalogApi: CatalogApi; - }); - // (undocumented) - analyzeUrl(url: string): Promise; - // (undocumented) - submitPullRequest({ repositoryUrl, fileContent, title, body, }: { - repositoryUrl: string; - fileContent: string; - title: string; - body: string; - }): Promise<{ - link: string; - location: string; - }>; + constructor(options: { + discoveryApi: DiscoveryApi; + githubAuthApi: OAuthApi; + identityApi: IdentityApi; + scmIntegrationsApi: ScmIntegrationRegistry; + catalogApi: CatalogApi; + }); + // (undocumented) + analyzeUrl(url: string): Promise; + // (undocumented) + submitPullRequest({ + repositoryUrl, + fileContent, + title, + body, + }: { + repositoryUrl: string; + fileContent: string; + title: string; + body: string; + }): Promise<{ + link: string; + location: string; + }>; } // @public (undocumented) export const CatalogImportPage: (opts: StepperProviderOpts) => JSX.Element; // @public (undocumented) -const catalogImportPlugin: BackstagePlugin<{ +const catalogImportPlugin: BackstagePlugin< + { importPage: RouteRef; -}, {}>; - -export { catalogImportPlugin } - -export { catalogImportPlugin as plugin } + }, + {} +>; +export { catalogImportPlugin }; +export { catalogImportPlugin as plugin }; // @public -export function defaultGenerateStepper(flow: ImportFlows, defaults: StepperProvider): StepperProvider; +export function defaultGenerateStepper( + flow: ImportFlows, + defaults: StepperProvider, +): StepperProvider; // @public (undocumented) -export const EntityListComponent: ({ locations, collapsed, locationListItemIcon, onItemClick, firstListItem, withLinks, }: Props_2) => JSX.Element; +export const EntityListComponent: ({ + locations, + collapsed, + locationListItemIcon, + onItemClick, + firstListItem, + withLinks, +}: Props_2) => JSX.Element; // @public (undocumented) -export const ImportStepper: ({ initialUrl, generateStepper, variant, opts, }: Props) => JSX.Element; +export const ImportStepper: ({ + initialUrl, + generateStepper, + variant, + opts, +}: Props) => JSX.Element; // @public -export const PreparePullRequestForm: >({ defaultValues, onSubmit, render, }: Props_5) => JSX.Element; +export const PreparePullRequestForm: < + TFieldValues extends Record +>({ + defaultValues, + onSubmit, + render, +}: Props_5) => JSX.Element; // @public (undocumented) -export const PreviewCatalogInfoComponent: ({ repositoryUrl, entities, classes, }: Props_6) => JSX.Element; +export const PreviewCatalogInfoComponent: ({ + repositoryUrl, + entities, + classes, +}: Props_6) => JSX.Element; // @public (undocumented) -export const PreviewPullRequestComponent: ({ title, description, classes, }: Props_7) => JSX.Element; +export const PreviewPullRequestComponent: ({ + title, + description, + classes, +}: Props_7) => JSX.Element; // @public (undocumented) export const Router: (opts: StepperProviderOpts) => JSX.Element; // @public -export const StepInitAnalyzeUrl: ({ onAnalysis, analysisUrl, disablePullRequest, }: Props_3) => JSX.Element; +export const StepInitAnalyzeUrl: ({ + onAnalysis, + analysisUrl, + disablePullRequest, +}: Props_3) => JSX.Element; // @public (undocumented) -export const StepPrepareCreatePullRequest: ({ analyzeResult, onPrepare, onGoBack, renderFormFields, defaultTitle, defaultBody, }: Props_8) => JSX.Element; - +export const StepPrepareCreatePullRequest: ({ + analyzeResult, + onPrepare, + onGoBack, + renderFormFields, + defaultTitle, + defaultBody, +}: Props_8) => JSX.Element; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/catalog-import/package.json b/plugins/catalog-import/package.json index 2b18c2e57a..3b3f441e8a 100644 --- a/plugins/catalog-import/package.json +++ b/plugins/catalog-import/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-import", - "version": "0.5.11", + "version": "0.5.12", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,20 +30,20 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.14", - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/integration": "^0.5.6", + "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "@octokit/rest": "^18.5.3", "@types/react": "^16.9", - "git-url-parse": "^11.4.4", + "git-url-parse": "~11.4.4", "js-base64": "^3.6.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -56,7 +56,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-react/CHANGELOG.md b/plugins/catalog-react/CHANGELOG.md index c7363dda54..bffefc4936 100644 --- a/plugins/catalog-react/CHANGELOG.md +++ b/plugins/catalog-react/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-catalog-react +## 0.2.6 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/catalog-client@0.3.16 + ## 0.2.5 ### Patch Changes diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md new file mode 100644 index 0000000000..4cc6a017aa --- /dev/null +++ b/plugins/catalog-react/api-report.md @@ -0,0 +1,673 @@ +## API Report File for "@backstage/plugin-catalog-react" + +> 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 { AsyncState } from 'react-use/lib/useAsync'; +import { CatalogApi } from '@backstage/catalog-client'; +import { ComponentEntity } from '@backstage/catalog-model'; +import { Context } from 'react'; +import { Entity } from '@backstage/catalog-model'; +import { EntityName } from '@backstage/catalog-model'; +import { LinkProps } from '@backstage/core-components'; +import { PropsWithChildren } from 'react'; +import { default as React_2 } from 'react'; +import { ReactNode } from 'react'; +import { RouteRef } from '@backstage/core-plugin-api'; +import { ScmIntegrationRegistry } from '@backstage/integration'; +import { SystemEntity } from '@backstage/catalog-model'; +import { TableColumn } from '@backstage/core-components'; +import { UserEntity } from '@backstage/catalog-model'; + +export { CatalogApi }; + +// @public (undocumented) +export const catalogApiRef: ApiRef; + +// @public (undocumented) +export const catalogRouteRef: RouteRef; + +// @public (undocumented) +function createDomainColumn(): TableColumn; + +// @public (undocumented) +function createEntityRefColumn({ + defaultKind, +}: { + defaultKind?: string; +}): TableColumn; + +// @public (undocumented) +function createEntityRelationColumn({ + title, + relation, + defaultKind, + filter: entityFilter, +}: { + title: string; + relation: string; + defaultKind?: string; + filter?: { + kind: string; + }; +}): TableColumn; + +// @public (undocumented) +function createMetadataDescriptionColumn(): TableColumn; + +// @public (undocumented) +function createOwnerColumn(): TableColumn; + +// @public (undocumented) +function createSpecLifecycleColumn(): TableColumn; + +// @public (undocumented) +function createSpecTypeColumn(): TableColumn; + +// @public (undocumented) +function createSystemColumn(): TableColumn; + +// @public (undocumented) +export type DefaultEntityFilters = { + kind?: EntityKindFilter; + type?: EntityTypeFilter; + user?: UserListFilter; + owners?: EntityOwnerFilter; + lifecycles?: EntityLifecycleFilter; + tags?: EntityTagFilter; + text?: EntityTextFilter; +}; + +// @public (undocumented) +export const EntityContext: Context; + +// @public (undocumented) +export type EntityFilter = { + getCatalogFilters?: () => Record; + filterEntity?: (entity: Entity) => boolean; + toQueryValue?: () => string | string[]; +}; + +// @public (undocumented) +export class EntityKindFilter implements EntityFilter { + constructor(value: string); + // (undocumented) + getCatalogFilters(): Record; + // (undocumented) + toQueryValue(): string; + // (undocumented) + readonly value: string; +} + +// @public (undocumented) +export const EntityKindPicker: ({ + initialFilter, + hidden, +}: EntityKindFilterProps) => JSX.Element | null; + +// @public (undocumented) +export class EntityLifecycleFilter implements EntityFilter { + constructor(values: string[]); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + toQueryValue(): string[]; + // (undocumented) + readonly values: string[]; +} + +// @public (undocumented) +export const EntityLifecyclePicker: () => JSX.Element | null; + +// @public (undocumented) +export const EntityListContext: React_2.Context< + EntityListContextProps | undefined +>; + +// @public (undocumented) +export const EntityListProvider: ({ + children, +}: PropsWithChildren<{}>) => JSX.Element; + +// @public (undocumented) +export class EntityOwnerFilter implements EntityFilter { + constructor(values: string[]); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + toQueryValue(): string[]; + // (undocumented) + readonly values: string[]; +} + +// @public (undocumented) +export const EntityOwnerPicker: () => JSX.Element | null; + +// @public (undocumented) +export const EntityProvider: ({ + entity, + children, +}: EntityProviderProps) => JSX.Element; + +// @public (undocumented) +export const EntityRefLink: React_2.ForwardRefExoticComponent< + Pick< + EntityRefLinkProps, + | 'replace' + | 'media' + | 'hidden' + | 'dir' + | 'slot' + | 'style' + | 'title' + | 'color' + | 'underline' + | 'display' + | 'translate' + | 'prefix' + | 'children' + | 'key' + | 'id' + | 'classes' + | 'defaultChecked' + | 'defaultValue' + | 'suppressContentEditableWarning' + | 'suppressHydrationWarning' + | 'accessKey' + | 'className' + | 'contentEditable' + | 'contextMenu' + | 'draggable' + | 'lang' + | 'placeholder' + | 'spellCheck' + | 'tabIndex' + | 'radioGroup' + | 'role' + | 'about' + | 'datatype' + | 'inlist' + | 'property' + | 'resource' + | 'typeof' + | 'vocab' + | 'autoCapitalize' + | 'autoCorrect' + | 'autoSave' + | 'itemProp' + | 'itemScope' + | 'itemType' + | 'itemID' + | 'itemRef' + | 'results' + | 'security' + | 'unselectable' + | 'inputMode' + | 'is' + | 'aria-activedescendant' + | 'aria-atomic' + | 'aria-autocomplete' + | 'aria-busy' + | 'aria-checked' + | 'aria-colcount' + | 'aria-colindex' + | 'aria-colspan' + | 'aria-controls' + | 'aria-current' + | 'aria-describedby' + | 'aria-details' + | 'aria-disabled' + | 'aria-dropeffect' + | 'aria-errormessage' + | 'aria-expanded' + | 'aria-flowto' + | 'aria-grabbed' + | 'aria-haspopup' + | 'aria-hidden' + | 'aria-invalid' + | 'aria-keyshortcuts' + | 'aria-label' + | 'aria-labelledby' + | 'aria-level' + | 'aria-live' + | 'aria-modal' + | 'aria-multiline' + | 'aria-multiselectable' + | 'aria-orientation' + | 'aria-owns' + | 'aria-placeholder' + | 'aria-posinset' + | 'aria-pressed' + | 'aria-readonly' + | 'aria-relevant' + | 'aria-required' + | 'aria-roledescription' + | 'aria-rowcount' + | 'aria-rowindex' + | 'aria-rowspan' + | 'aria-selected' + | 'aria-setsize' + | 'aria-sort' + | 'aria-valuemax' + | 'aria-valuemin' + | 'aria-valuenow' + | 'aria-valuetext' + | 'dangerouslySetInnerHTML' + | 'onCopy' + | 'onCopyCapture' + | 'onCut' + | 'onCutCapture' + | 'onPaste' + | 'onPasteCapture' + | 'onCompositionEnd' + | 'onCompositionEndCapture' + | 'onCompositionStart' + | 'onCompositionStartCapture' + | 'onCompositionUpdate' + | 'onCompositionUpdateCapture' + | 'onFocus' + | 'onFocusCapture' + | 'onBlur' + | 'onBlurCapture' + | 'onChange' + | 'onChangeCapture' + | 'onBeforeInput' + | 'onBeforeInputCapture' + | 'onInput' + | 'onInputCapture' + | 'onReset' + | 'onResetCapture' + | 'onSubmit' + | 'onSubmitCapture' + | 'onInvalid' + | 'onInvalidCapture' + | 'onLoad' + | 'onLoadCapture' + | 'onError' + | 'onErrorCapture' + | 'onKeyDown' + | 'onKeyDownCapture' + | 'onKeyPress' + | 'onKeyPressCapture' + | 'onKeyUp' + | 'onKeyUpCapture' + | 'onAbort' + | 'onAbortCapture' + | 'onCanPlay' + | 'onCanPlayCapture' + | 'onCanPlayThrough' + | 'onCanPlayThroughCapture' + | 'onDurationChange' + | 'onDurationChangeCapture' + | 'onEmptied' + | 'onEmptiedCapture' + | 'onEncrypted' + | 'onEncryptedCapture' + | 'onEnded' + | 'onEndedCapture' + | 'onLoadedData' + | 'onLoadedDataCapture' + | 'onLoadedMetadata' + | 'onLoadedMetadataCapture' + | 'onLoadStart' + | 'onLoadStartCapture' + | 'onPause' + | 'onPauseCapture' + | 'onPlay' + | 'onPlayCapture' + | 'onPlaying' + | 'onPlayingCapture' + | 'onProgress' + | 'onProgressCapture' + | 'onRateChange' + | 'onRateChangeCapture' + | 'onSeeked' + | 'onSeekedCapture' + | 'onSeeking' + | 'onSeekingCapture' + | 'onStalled' + | 'onStalledCapture' + | 'onSuspend' + | 'onSuspendCapture' + | 'onTimeUpdate' + | 'onTimeUpdateCapture' + | 'onVolumeChange' + | 'onVolumeChangeCapture' + | 'onWaiting' + | 'onWaitingCapture' + | 'onAuxClick' + | 'onAuxClickCapture' + | 'onClick' + | 'onClickCapture' + | 'onContextMenu' + | 'onContextMenuCapture' + | 'onDoubleClick' + | 'onDoubleClickCapture' + | 'onDrag' + | 'onDragCapture' + | 'onDragEnd' + | 'onDragEndCapture' + | 'onDragEnter' + | 'onDragEnterCapture' + | 'onDragExit' + | 'onDragExitCapture' + | 'onDragLeave' + | 'onDragLeaveCapture' + | 'onDragOver' + | 'onDragOverCapture' + | 'onDragStart' + | 'onDragStartCapture' + | 'onDrop' + | 'onDropCapture' + | 'onMouseDown' + | 'onMouseDownCapture' + | 'onMouseEnter' + | 'onMouseLeave' + | 'onMouseMove' + | 'onMouseMoveCapture' + | 'onMouseOut' + | 'onMouseOutCapture' + | 'onMouseOver' + | 'onMouseOverCapture' + | 'onMouseUp' + | 'onMouseUpCapture' + | 'onSelect' + | 'onSelectCapture' + | 'onTouchCancel' + | 'onTouchCancelCapture' + | 'onTouchEnd' + | 'onTouchEndCapture' + | 'onTouchMove' + | 'onTouchMoveCapture' + | 'onTouchStart' + | 'onTouchStartCapture' + | 'onPointerDown' + | 'onPointerDownCapture' + | 'onPointerMove' + | 'onPointerMoveCapture' + | 'onPointerUp' + | 'onPointerUpCapture' + | 'onPointerCancel' + | 'onPointerCancelCapture' + | 'onPointerEnter' + | 'onPointerEnterCapture' + | 'onPointerLeave' + | 'onPointerLeaveCapture' + | 'onPointerOver' + | 'onPointerOverCapture' + | 'onPointerOut' + | 'onPointerOutCapture' + | 'onGotPointerCapture' + | 'onGotPointerCaptureCapture' + | 'onLostPointerCapture' + | 'onLostPointerCaptureCapture' + | 'onScroll' + | 'onScrollCapture' + | 'onWheel' + | 'onWheelCapture' + | 'onAnimationStart' + | 'onAnimationStartCapture' + | 'onAnimationEnd' + | 'onAnimationEndCapture' + | 'onAnimationIteration' + | 'onAnimationIterationCapture' + | 'onTransitionEnd' + | 'onTransitionEndCapture' + | 'component' + | 'variant' + | 'innerRef' + | 'download' + | 'href' + | 'hrefLang' + | 'ping' + | 'rel' + | 'target' + | 'type' + | 'referrerPolicy' + | 'noWrap' + | 'gutterBottom' + | 'paragraph' + | 'align' + | 'variantMapping' + | 'state' + | 'TypographyClasses' + | 'entityRef' + | 'defaultKind' + > & + React_2.RefAttributes +>; + +// @public (undocumented) +export const EntityRefLinks: ({ + entityRefs, + defaultKind, + ...linkProps +}: EntityRefLinksProps) => JSX.Element; + +// @public (undocumented) +export const entityRoute: RouteRef<{ + name: string; + kind: string; + namespace: string; +}>; + +// @public (undocumented) +export function entityRouteParams( + entity: Entity, +): { + readonly kind: string; + readonly namespace: string; + readonly name: string; +}; + +// @public (undocumented) +export const entityRouteRef: RouteRef<{ + name: string; + kind: string; + namespace: string; +}>; + +// @public (undocumented) +export const EntitySearchBar: () => JSX.Element; + +// @public (undocumented) +export type EntitySourceLocation = { + locationTargetUrl: string; + integrationType?: string; +}; + +// @public (undocumented) +export function EntityTable({ + entities, + title, + emptyContent, + variant, + columns, +}: Props): JSX.Element; + +// @public (undocumented) +export namespace EntityTable { + var // (undocumented) + columns: typeof columnFactories; + var // (undocumented) + systemEntityColumns: TableColumn[]; + var // (undocumented) + componentEntityColumns: TableColumn[]; +} + +// @public (undocumented) +export class EntityTagFilter implements EntityFilter { + constructor(values: string[]); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + toQueryValue(): string[]; + // (undocumented) + readonly values: string[]; +} + +// @public (undocumented) +export const EntityTagPicker: () => JSX.Element | null; + +// @public (undocumented) +export class EntityTextFilter implements EntityFilter { + constructor(value: string); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + readonly value: string; +} + +// @public (undocumented) +export class EntityTypeFilter implements EntityFilter { + constructor(value: string | string[]); + // (undocumented) + getCatalogFilters(): Record; + // (undocumented) + getTypes(): string[]; + // (undocumented) + toQueryValue(): string[]; + // (undocumented) + readonly value: string | string[]; +} + +// @public (undocumented) +export const EntityTypePicker: () => JSX.Element | null; + +// @public (undocumented) +export function formatEntityRefTitle( + entityRef: Entity | EntityName, + opts?: { + defaultKind?: string; + }, +): string; + +// @public (undocumented) +export function getEntityMetadataEditUrl(entity: Entity): string | undefined; + +// @public (undocumented) +export function getEntityMetadataViewUrl(entity: Entity): string | undefined; + +// @public +export function getEntityRelations( + entity: Entity | undefined, + relationType: string, + filter?: { + kind: string; + }, +): EntityName[]; + +// @public (undocumented) +export function getEntitySourceLocation( + entity: Entity, + scmIntegrationsApi: ScmIntegrationRegistry, +): EntitySourceLocation | undefined; + +// @public +export function isOwnerOf(owner: Entity, owned: Entity): boolean; + +// @public (undocumented) +export const MockEntityListContextProvider: ({ + children, + value, +}: React_2.PropsWithChildren<{ + value: Partial; +}>) => JSX.Element; + +// @public (undocumented) +export function reduceCatalogFilters( + filters: EntityFilter[], +): Record; + +// @public (undocumented) +export function reduceEntityFilters( + filters: EntityFilter[], +): (entity: Entity) => boolean; + +// @public (undocumented) +export const rootRoute: RouteRef; + +// @public +export function useEntity(): { + entity: T; + loading: boolean; + error: Error | undefined; +}; + +// @public +export const useEntityCompoundName: () => { + kind: string; + namespace: string; + name: string; +}; + +// @public (undocumented) +export const useEntityFromUrl: () => EntityLoadingStatus; + +// @public (undocumented) +export function useEntityListProvider< + EntityFilters extends DefaultEntityFilters = DefaultEntityFilters +>(): EntityListContextProps; + +// @public +export function useEntityTypeFilter(): EntityTypeReturn; + +// @public +export function useOwnUser(): AsyncState; + +// @public (undocumented) +export function useRelatedEntities( + entity: Entity, + { + type, + kind, + }: { + type?: string; + kind?: string; + }, +): { + entities: Entity[] | undefined; + loading: boolean; + error: Error | undefined; +}; + +// @public (undocumented) +export class UserListFilter implements EntityFilter { + constructor( + value: UserListFilterKind, + user: UserEntity | undefined, + isStarredEntity: (entity: Entity) => boolean, + ); + // (undocumented) + filterEntity(entity: Entity): boolean; + // (undocumented) + readonly isStarredEntity: (entity: Entity) => boolean; + // (undocumented) + toQueryValue(): string; + // (undocumented) + readonly user: UserEntity | undefined; + // (undocumented) + readonly value: UserListFilterKind; +} + +// @public (undocumented) +export type UserListFilterKind = 'owned' | 'starred' | 'all'; + +// @public (undocumented) +export const UserListPicker: ({ + initialFilter, + availableFilters, +}: UserListPickerProps) => JSX.Element; + +// @public (undocumented) +export const useStarredEntities: () => { + starredEntities: Set; + toggleStarredEntity: (entity: Entity) => void; + isStarredEntity: (entity: Entity) => boolean; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/catalog-react/package.json b/plugins/catalog-react/package.json index 8c760a16d0..c65e06df29 100644 --- a/plugins/catalog-react/package.json +++ b/plugins/catalog-react/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-catalog-react", - "version": "0.2.5", + "version": "0.2.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -28,17 +28,18 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/core-app-api": "^0.1.4", - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", "@material-ui/lab": "4.0.0-alpha.45", "@types/react": "^16.9", "lodash": "^4.17.15", + "qs": "^6.9.4", "react": "^16.13.1", "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", @@ -46,7 +47,7 @@ }, "devDependencies": { "@backstage/cli": "^0.7.3", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx index 8faa86c3ca..d6404fcf07 100644 --- a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.test.tsx @@ -17,7 +17,7 @@ import { render } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; -import { EntityKindFilter } from '../../types'; +import { EntityKindFilter } from '../../filters'; import { EntityKindPicker } from './EntityKindPicker'; describe('', () => { @@ -37,4 +37,23 @@ describe('', () => { kind: new EntityKindFilter('component'), }); }); + + it('respects the query parameter filter value', () => { + const updateFilters = jest.fn(); + const queryParameters = { kind: 'API' }; + render( + + , + ); + + expect(updateFilters).toHaveBeenLastCalledWith({ + kind: new EntityKindFilter('API'), + }); + }); }); diff --git a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.tsx b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.tsx index db695d58cc..6f9119c404 100644 --- a/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.tsx +++ b/plugins/catalog-react/src/components/EntityKindPicker/EntityKindPicker.tsx @@ -17,7 +17,7 @@ import React, { useEffect, useState } from 'react'; import { Alert } from '@material-ui/lab'; import { useEntityListProvider } from '../../hooks'; -import { EntityKindFilter } from '../../types'; +import { EntityKindFilter } from '../../filters'; type EntityKindFilterProps = { initialFilter?: string; @@ -28,8 +28,10 @@ export const EntityKindPicker = ({ initialFilter, hidden, }: EntityKindFilterProps) => { - const [selectedKind] = useState(initialFilter); - const { updateFilters } = useEntityListProvider(); + const { updateFilters, queryParameters } = useEntityListProvider(); + const [selectedKind] = useState( + [queryParameters.kind].flat()[0] ?? initialFilter, + ); useEffect(() => { updateFilters({ diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx index 8e8bb5aeba..f7d915ef5b 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.test.tsx @@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; -import { EntityLifecycleFilter } from '../../types'; +import { EntityLifecycleFilter } from '../../filters'; import { EntityLifecyclePicker } from './EntityLifecyclePicker'; const sampleEntities: Entity[] = [ @@ -91,6 +91,27 @@ describe('', () => { ]); }); + it('respects the query parameter filter value', () => { + const updateFilters = jest.fn(); + const queryParameters = { lifecycles: ['experimental'] }; + render( + + + , + ); + + expect(updateFilters).toHaveBeenLastCalledWith({ + lifecycles: new EntityLifecycleFilter(['experimental']), + }); + }); + it('adds lifecycles to filters', () => { const updateFilters = jest.fn(); const rendered = render( @@ -104,7 +125,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + lifecycles: undefined, + }); fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); fireEvent.click(rendered.getByText('production')); @@ -127,7 +150,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + lifecycles: new EntityLifecycleFilter(['production']), + }); fireEvent.click(rendered.getByTestId('lifecycle-picker-expand')); expect(rendered.getByLabelText('production')).toBeChecked(); diff --git a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx index a5fe16ae40..9a899e161d 100644 --- a/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx +++ b/plugins/catalog-react/src/components/EntityLifecyclePicker/EntityLifecyclePicker.tsx @@ -26,15 +26,38 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox'; import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { Autocomplete } from '@material-ui/lab'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; -import { EntityLifecycleFilter } from '../../types'; +import { EntityLifecycleFilter } from '../../filters'; const icon = ; const checkedIcon = ; export const EntityLifecyclePicker = () => { - const { updateFilters, backendEntities, filters } = useEntityListProvider(); + const { + updateFilters, + backendEntities, + filters, + queryParameters, + } = useEntityListProvider(); + + const queryParamLifecycles = [queryParameters.lifecycles] + .flat() + .filter(Boolean) as string[]; + const [selectedLifecycles, setSelectedLifecycles] = useState( + queryParamLifecycles.length + ? queryParamLifecycles + : filters.lifecycles?.values ?? [], + ); + + useEffect(() => { + updateFilters({ + lifecycles: selectedLifecycles.length + ? new EntityLifecycleFilter(selectedLifecycles) + : undefined, + }); + }, [selectedLifecycles, updateFilters]); + const availableLifecycles = useMemo( () => [ @@ -49,22 +72,14 @@ export const EntityLifecyclePicker = () => { if (!availableLifecycles.length) return null; - const onChange = (lifecycles: string[]) => { - updateFilters({ - lifecycles: lifecycles.length - ? new EntityLifecycleFilter(lifecycles) - : undefined, - }); - }; - return ( Lifecycle multiple options={availableLifecycles} - value={filters.lifecycles?.values ?? []} - onChange={(_: object, value: string[]) => onChange(value)} + value={selectedLifecycles} + onChange={(_: object, value: string[]) => setSelectedLifecycles(value)} renderOption={(option, { selected }) => ( ', () => { ]); }); + it('respects the query parameter filter value', () => { + const updateFilters = jest.fn(); + const queryParameters = { owners: ['another-owner'] }; + render( + + + , + ); + + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new EntityOwnerFilter(['another-owner']), + }); + }); + it('adds owners to filters', () => { const updateFilters = jest.fn(); const rendered = render( @@ -134,7 +155,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: undefined, + }); fireEvent.click(rendered.getByTestId('owner-picker-expand')); fireEvent.click(rendered.getByText('some-owner')); @@ -157,7 +180,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + owners: new EntityOwnerFilter(['some-owner']), + }); fireEvent.click(rendered.getByTestId('owner-picker-expand')); expect(rendered.getByLabelText('some-owner')).toBeChecked(); diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 665529fddb..90d94e32b5 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -26,9 +26,9 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox'; import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { Autocomplete } from '@material-ui/lab'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; -import { EntityOwnerFilter } from '../../types'; +import { EntityOwnerFilter } from '../../filters'; import { getEntityRelations } from '../../utils'; import { formatEntityRefTitle } from '../EntityRefLink'; @@ -36,7 +36,28 @@ const icon = ; const checkedIcon = ; export const EntityOwnerPicker = () => { - const { updateFilters, backendEntities, filters } = useEntityListProvider(); + const { + updateFilters, + backendEntities, + filters, + queryParameters, + } = useEntityListProvider(); + + const queryParamOwners = [queryParameters.owners] + .flat() + .filter(Boolean) as string[]; + const [selectedOwners, setSelectedOwners] = useState( + queryParamOwners.length ? queryParamOwners : filters.owners?.values ?? [], + ); + + useEffect(() => { + updateFilters({ + owners: selectedOwners.length + ? new EntityOwnerFilter(selectedOwners) + : undefined, + }); + }, [selectedOwners, updateFilters]); + const availableOwners = useMemo( () => [ @@ -55,20 +76,14 @@ export const EntityOwnerPicker = () => { if (!availableOwners.length) return null; - const onChange = (owners: string[]) => { - updateFilters({ - owners: owners.length ? new EntityOwnerFilter(owners) : undefined, - }); - }; - return ( Owner multiple options={availableOwners} - value={filters.owners?.values ?? []} - onChange={(_: object, value: string[]) => onChange(value)} + value={selectedOwners} + onChange={(_: object, value: string[]) => setSelectedOwners(value)} renderOption={(option, { selected }) => ( { + it('should display search value and execute set callback', async () => { + const updateFilters = jest.fn(); + + const filters: DefaultEntityFilters = { + text: new EntityTextFilter('hello'), + }; + + const { getByDisplayValue } = render( + + + , + ); + + const searchInput = getByDisplayValue('hello'); + expect(searchInput).toBeInTheDocument(); + + fireEvent.change(searchInput, { target: { value: 'world' } }); + await waitFor(() => expect(updateFilters.mock.calls.length).toBe(1)); + expect(updateFilters).toHaveBeenCalledWith({ + text: new EntityTextFilter('world'), + }); + + fireEvent.change(searchInput, { target: { value: '' } }); + await waitFor(() => expect(updateFilters.mock.calls.length).toBe(2)); + expect(updateFilters).toHaveBeenCalledWith({ + text: undefined, + }); + }); +}); diff --git a/plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.tsx b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx similarity index 76% rename from plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.tsx rename to plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx index 0a7488195c..9fd30b5182 100644 --- a/plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.tsx +++ b/plugins/catalog-react/src/components/EntitySearchBar/EntitySearchBar.tsx @@ -13,22 +13,21 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; + import { FormControl, + IconButton, + Input, InputAdornment, makeStyles, Toolbar, - Input, - IconButton, } from '@material-ui/core'; -import Search from '@material-ui/icons/Search'; import Clear from '@material-ui/icons/Clear'; - -interface Props { - search: string; - setSearch: Function; -} +import Search from '@material-ui/icons/Search'; +import React, { useState } from 'react'; +import { useDebounce } from 'react-use'; +import { useEntityListProvider } from '../../hooks/useEntityListProvider'; +import { EntityTextFilter } from '../../filters'; const useStyles = makeStyles(_theme => ({ searchToolbar: { @@ -37,8 +36,22 @@ const useStyles = makeStyles(_theme => ({ }, })); -const SearchToolbar = ({ search, setSearch }: Props) => { +export const EntitySearchBar = () => { const styles = useStyles(); + + const { filters, updateFilters } = useEntityListProvider(); + const [search, setSearch] = useState(filters.text?.value ?? ''); + + useDebounce( + () => { + updateFilters({ + text: search.length ? new EntityTextFilter(search) : undefined, + }); + }, + 250, + [search, updateFilters], + ); + return ( @@ -70,5 +83,3 @@ const SearchToolbar = ({ search, setSearch }: Props) => { ); }; - -export default SearchToolbar; diff --git a/plugins/scaffolder/src/filter/index.ts b/plugins/catalog-react/src/components/EntitySearchBar/index.ts similarity index 57% rename from plugins/scaffolder/src/filter/index.ts rename to plugins/catalog-react/src/components/EntitySearchBar/index.ts index bed464222f..044b8ee416 100644 --- a/plugins/scaffolder/src/filter/index.ts +++ b/plugins/catalog-react/src/components/EntitySearchBar/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2021 Spotify AB * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,15 +14,4 @@ * limitations under the License. */ -export { EntityFilterGroupsProvider } from './EntityFilterGroupsProvider'; -export type { - EntityFilterFn, - FilterGroup, - FilterGroupState, - FilterGroupStates, - FilterGroupStatesError, - FilterGroupStatesLoading, - FilterGroupStatesReady, -} from './types'; -export { useEntityFilterGroup } from './useEntityFilterGroup'; -export { useFilteredEntities } from './useFilteredEntities'; +export { EntitySearchBar } from './EntitySearchBar'; diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx index 9d65221b76..770c187b4e 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.test.tsx @@ -18,7 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { fireEvent, render } from '@testing-library/react'; import React from 'react'; import { MockEntityListContextProvider } from '../../testUtils/providers'; -import { EntityTagFilter } from '../../types'; +import { EntityTagFilter } from '../../filters'; import { EntityTagPicker } from './EntityTagPicker'; const taggedEntities: Entity[] = [ @@ -79,6 +79,27 @@ describe('', () => { ]); }); + it('respects the query parameter filter value', () => { + const updateFilters = jest.fn(); + const queryParameters = { tags: ['tag3'] }; + render( + + + , + ); + + expect(updateFilters).toHaveBeenLastCalledWith({ + tags: new EntityTagFilter(['tag3']), + }); + }); + it('adds tags to filters', () => { const updateFilters = jest.fn(); const rendered = render( @@ -92,7 +113,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + tags: undefined, + }); fireEvent.click(rendered.getByTestId('tag-picker-expand')); fireEvent.click(rendered.getByText('tag1')); @@ -115,7 +138,9 @@ describe('', () => { , ); - expect(updateFilters).not.toHaveBeenCalled(); + expect(updateFilters).toHaveBeenLastCalledWith({ + tags: new EntityTagFilter(['tag1']), + }); fireEvent.click(rendered.getByTestId('tag-picker-expand')); expect(rendered.getByLabelText('tag1')).toBeChecked(); diff --git a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx index bd92b68478..b7fa8f1186 100644 --- a/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx +++ b/plugins/catalog-react/src/components/EntityTagPicker/EntityTagPicker.tsx @@ -26,15 +26,34 @@ import CheckBoxIcon from '@material-ui/icons/CheckBox'; import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; import ExpandMoreIcon from '@material-ui/icons/ExpandMore'; import { Autocomplete } from '@material-ui/lab'; -import React, { useMemo } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { useEntityListProvider } from '../../hooks/useEntityListProvider'; -import { EntityTagFilter } from '../../types'; +import { EntityTagFilter } from '../../filters'; const icon = ; const checkedIcon = ; export const EntityTagPicker = () => { - const { updateFilters, backendEntities, filters } = useEntityListProvider(); + const { + updateFilters, + backendEntities, + filters, + queryParameters, + } = useEntityListProvider(); + + const queryParamTags = [queryParameters.tags] + .flat() + .filter(Boolean) as string[]; + const [selectedTags, setSelectedTags] = useState( + queryParamTags.length ? queryParamTags : filters.tags?.values ?? [], + ); + + useEffect(() => { + updateFilters({ + tags: selectedTags.length ? new EntityTagFilter(selectedTags) : undefined, + }); + }, [selectedTags, updateFilters]); + const availableTags = useMemo( () => [ @@ -49,20 +68,14 @@ export const EntityTagPicker = () => { if (!availableTags.length) return null; - const onChange = (tags: string[]) => { - updateFilters({ - tags: tags.length ? new EntityTagFilter(tags) : undefined, - }); - }; - return ( Tags multiple options={availableTags} - value={filters.tags?.values ?? []} - onChange={(_: object, value: string[]) => onChange(value)} + value={selectedTags} + onChange={(_: object, value: string[]) => setSelectedTags(value)} renderOption={(option, { selected }) => ( ', () => { fireEvent.click(rendered.getByText('Service')); expect(updateFilters).toHaveBeenLastCalledWith({ - type: new EntityTypeFilter('service'), + type: new EntityTypeFilter(['service']), }); fireEvent.click(input); diff --git a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx index 7b7845b8bc..88538d1f65 100644 --- a/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx +++ b/plugins/catalog-react/src/components/EntityTypePicker/EntityTypePicker.tsx @@ -15,7 +15,7 @@ */ import React, { useEffect } from 'react'; -import { capitalize } from 'lodash'; +import capitalize from 'lodash/capitalize'; import { Box } from '@material-ui/core'; import { useEntityTypeFilter } from '../../hooks/useEntityTypeFilter'; @@ -24,7 +24,12 @@ import { Select } from '@backstage/core-components'; export const EntityTypePicker = () => { const alertApi = useApi(alertApiRef); - const { error, types, selectedType, setType } = useEntityTypeFilter(); + const { + error, + availableTypes, + selectedTypes, + setSelectedTypes, + } = useEntityTypeFilter(); useEffect(() => { if (error) { @@ -35,13 +40,11 @@ export const EntityTypePicker = () => { } }, [error, alertApi]); - if (!types || error) { - return null; - } + if (!availableTypes || error) return null; const items = [ { value: 'all', label: 'All' }, - ...types.map((type: string) => ({ + ...availableTypes.map((type: string) => ({ value: type, label: capitalize(type), })), @@ -52,8 +55,10 @@ export const EntityTypePicker = () => { = { - baseUrl?: string; - logger: Logger_2; - logStream: Writable; - token?: string | undefined; - workspacePath: string; - input: Input; - output(name: string, value: JsonValue): void; - createTemporaryDirectory(): Promise; + baseUrl?: string; + logger: Logger_2; + logStream: Writable; + token?: string | undefined; + workspacePath: string; + input: Input; + output(name: string, value: JsonValue): void; + createTemporaryDirectory(): Promise; }; // @public export class CatalogEntityClient { - constructor(catalogClient: CatalogApi); - findTemplate(templateName: string, options?: { - token?: string; - }): Promise; + constructor(catalogClient: CatalogApi); + findTemplate( + templateName: string, + options?: { + token?: string; + }, + ): Promise; } // @public (undocumented) export const createBuiltinActions: (options: { - reader: UrlReader; - integrations: ScmIntegrations; - catalogClient: CatalogApi; - containerRunner: ContainerRunner; - config: Config; + reader: UrlReader; + integrations: ScmIntegrations; + catalogClient: CatalogApi; + containerRunner: ContainerRunner; + config: Config; }) => TemplateAction[]; // @public (undocumented) export function createCatalogRegisterAction(options: { - catalogClient: CatalogApi; - integrations: ScmIntegrations; + catalogClient: CatalogApi; + integrations: ScmIntegrations; }): TemplateAction; // @public (undocumented) @@ -63,15 +67,21 @@ export function createDebugLogAction(): TemplateAction; // @public (undocumented) export function createFetchCookiecutterAction(options: { - reader: UrlReader; - integrations: ScmIntegrations; - containerRunner: ContainerRunner; + reader: UrlReader; + integrations: ScmIntegrations; + containerRunner: ContainerRunner; }): TemplateAction; // @public (undocumented) export function createFetchPlainAction(options: { - reader: UrlReader; - integrations: ScmIntegrations; + reader: UrlReader; + integrations: ScmIntegrations; +}): TemplateAction; + +// @public (undocumented) +export function createFetchTemplateAction(options: { + reader: UrlReader; + integrations: ScmIntegrations; }): TemplateAction; // @public (undocumented) @@ -82,14 +92,14 @@ export const createFilesystemRenameAction: () => TemplateAction; // @public (undocumented) export function createPublishAzureAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; + integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) export function createPublishBitbucketAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; + integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public @@ -97,81 +107,98 @@ export function createPublishFileAction(): TemplateAction; // @public (undocumented) export function createPublishGithubAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; + integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) -export const createPublishGithubPullRequestAction: ({ integrations, clientFactory, }: CreateGithubPullRequestActionOptions) => TemplateAction; +export const createPublishGithubPullRequestAction: ({ + integrations, + clientFactory, +}: CreateGithubPullRequestActionOptions) => TemplateAction; // @public (undocumented) export function createPublishGitlabAction(options: { - integrations: ScmIntegrationRegistry; - config: Config; + integrations: ScmIntegrationRegistry; + config: Config; }): TemplateAction; // @public (undocumented) export function createRouter(options: RouterOptions): Promise; // @public (undocumented) -export const createTemplateAction: | undefined; -}>>(templateAction: TemplateAction) => TemplateAction; +export const createTemplateAction: < + Input extends Partial<{ + [name: string]: JsonValue | Partial | undefined; + }> +>( + templateAction: TemplateAction, +) => TemplateAction; // @public (undocumented) -export function fetchContents({ reader, integrations, baseUrl, fetchUrl, outputPath, }: { - reader: UrlReader; - integrations: ScmIntegrations; - baseUrl?: string; - fetchUrl?: JsonValue; - outputPath: string; +export function fetchContents({ + reader, + integrations, + baseUrl, + fetchUrl, + outputPath, +}: { + reader: UrlReader; + integrations: ScmIntegrations; + baseUrl?: string; + fetchUrl?: JsonValue; + outputPath: string; }): Promise; // @public (undocumented) export interface RouterOptions { - // (undocumented) - actions?: TemplateAction[]; - // (undocumented) - catalogClient: CatalogApi; - // (undocumented) - config: Config; - // (undocumented) - containerRunner: ContainerRunner; - // (undocumented) - database: PluginDatabaseManager; - // (undocumented) - logger: Logger_2; - // (undocumented) - reader: UrlReader; - // (undocumented) - taskWorkers?: number; + // (undocumented) + actions?: TemplateAction[]; + // (undocumented) + catalogClient: CatalogApi; + // (undocumented) + config: Config; + // (undocumented) + containerRunner: ContainerRunner; + // (undocumented) + database: PluginDatabaseManager; + // (undocumented) + logger: Logger_2; + // (undocumented) + reader: UrlReader; + // (undocumented) + taskWorkers?: number; } // @public (undocumented) -export const runCommand: ({ command, args, logStream, }: RunCommandOptions) => Promise; +export const runCommand: ({ + command, + args, + logStream, +}: RunCommandOptions) => Promise; // @public (undocumented) export type TemplateAction = { - id: string; - description?: string; - schema?: { - input?: Schema; - output?: Schema; - }; - handler: (ctx: ActionContext) => Promise; + id: string; + description?: string; + schema?: { + input?: Schema; + output?: Schema; + }; + handler: (ctx: ActionContext) => Promise; }; // @public (undocumented) export class TemplateActionRegistry { - // (undocumented) - get(actionId: string): TemplateAction; - // (undocumented) - list(): TemplateAction[]; - // (undocumented) - register(action: TemplateAction): void; + // (undocumented) + get(actionId: string): TemplateAction; + // (undocumented) + list(): TemplateAction[]; + // (undocumented) + register( + action: TemplateAction, + ): void; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/scaffolder-backend/fixtures/test-nested-template/public/react-logo192.png b/plugins/scaffolder-backend/fixtures/test-nested-template/public/react-logo192.png new file mode 100644 index 0000000000..fc44b0a379 Binary files /dev/null and b/plugins/scaffolder-backend/fixtures/test-nested-template/public/react-logo192.png differ diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 7173f865c5..d31e6c0d5f 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder-backend", - "version": "0.12.4", + "version": "0.13.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,12 +29,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.4", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@gitbeaker/core": "^30.2.0", "@gitbeaker/node": "^30.2.0", "@octokit/rest": "^18.5.3", @@ -46,17 +46,19 @@ "cross-fetch": "^3.0.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "fs-extra": "^10.0.0", - "git-url-parse": "^11.4.4", + "fs-extra": "10.0.0", + "git-url-parse": "~11.4.4", "globby": "^11.0.0", "handlebars": "^4.7.6", "helmet": "^4.0.0", + "isbinaryfile": "^4.0.8", "isomorphic-git": "^1.8.0", "jsonschema": "^1.2.6", "knex": "^0.95.1", "lodash": "^4.17.21", "luxon": "^1.26.0", "morgan": "^1.10.0", + "nunjucks": "^3.2.3", "octokit-plugin-create-pull-request": "^3.9.3", "uuid": "^8.2.0", "winston": "^3.2.1", @@ -69,6 +71,7 @@ "@types/fs-extra": "^9.0.1", "@types/git-url-parse": "^9.0.0", "@types/mock-fs": "^4.13.0", + "@types/nunjucks": "^3.1.4", "@types/supertest": "^2.0.8", "jest-when": "^3.1.0", "mock-fs": "^4.13.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts index 08d7bbf824..6541625035 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/createBuiltinActions.ts @@ -24,7 +24,11 @@ import { } from './catalog'; import { createDebugLogAction } from './debug'; -import { createFetchCookiecutterAction, createFetchPlainAction } from './fetch'; +import { + createFetchCookiecutterAction, + createFetchPlainAction, + createFetchTemplateAction, +} from './fetch'; import { createFilesystemDeleteAction, createFilesystemRenameAction, @@ -62,6 +66,10 @@ export const createBuiltinActions = (options: { integrations, containerRunner, }), + createFetchTemplateAction({ + integrations, + reader, + }), createPublishGithubAction({ integrations, config, diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts index 9b160b1dc3..557190dbbc 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/debug/log.ts @@ -26,7 +26,7 @@ export function createDebugLogAction() { return createTemplateAction<{ message?: string; listWorkspace?: boolean }>({ id: 'debug:log', description: - 'Writes a message into the log or list all files in the workspace.', + 'Writes a message into the log or lists all files in the workspace.', schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts index 2bf22ce4fd..048b2bba64 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/cookiecutter.ts @@ -136,7 +136,7 @@ export function createFetchCookiecutterAction(options: { }>({ id: 'fetch:cookiecutter', description: - 'Downloads a template from the given URL into the workspace, and runs cookiecutter on it.', + "Downloads a template from the given URL into the workspace, and runs cookiecutter on it. This action is deprecated in favor of 'fetch:template'. See https://backstage.io/docs/features/software-templates/builtin-actions#migrating-from-fetch-cookiecutter-to-fetch-template for more details.", schema: { input: { type: 'object', diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts index fa6d23c032..fea32df39c 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/index.ts @@ -16,4 +16,5 @@ export { createFetchPlainAction } from './plain'; export { createFetchCookiecutterAction } from './cookiecutter'; +export { createFetchTemplateAction } from './template'; export { fetchContents } from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts new file mode 100644 index 0000000000..bd2beae47a --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.test.ts @@ -0,0 +1,295 @@ +/* + * 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 os from 'os'; +import { join as joinPath, resolve as resolvePath } from 'path'; +import fs from 'fs-extra'; +import mockFs from 'mock-fs'; +import { getVoidLogger, UrlReader } from '@backstage/backend-common'; +import { ScmIntegrations } from '@backstage/integration'; +import { PassThrough } from 'stream'; +import { fetchContents } from './helpers'; +import { ActionContext, TemplateAction } from '../../types'; +import { createFetchTemplateAction, FetchTemplateInput } from './template'; + +jest.mock('./helpers', () => ({ + fetchContents: jest.fn(), +})); + +const aBinaryFile = fs.readFileSync( + resolvePath( + 'src', + '../fixtures/test-nested-template/public/react-logo192.png', + ), +); + +const mockFetchContents = fetchContents as jest.MockedFunction< + typeof fetchContents +>; + +describe('fetch:template', () => { + let action: TemplateAction; + + const workspacePath = os.tmpdir(); + const createTemporaryDirectory: jest.MockedFunction< + ActionContext['createTemporaryDirectory'] + > = jest.fn(() => + Promise.resolve( + joinPath(workspacePath, `${createTemporaryDirectory.mock.calls.length}`), + ), + ); + + const logger = getVoidLogger(); + + const mockContext = (inputPatch: Partial = {}) => ({ + baseUrl: 'base-url', + input: { + url: './skeleton', + targetPath: './target', + values: { + test: 'value', + }, + ...inputPatch, + }, + output: jest.fn(), + logStream: new PassThrough(), + logger, + workspacePath, + createTemporaryDirectory, + }); + + beforeEach(() => { + mockFs(); + + action = createFetchTemplateAction({ + reader: (Symbol('UrlReader') as unknown) as UrlReader, + integrations: (Symbol('Integrations') as unknown) as ScmIntegrations, + }); + }); + + afterEach(() => { + mockFs.restore(); + }); + + it(`returns a TemplateAction with the id 'fetch:template'`, () => { + expect(action.id).toEqual('fetch:template'); + }); + + describe('handler', () => { + it('throws if output directory is outside the workspace', async () => { + await expect(() => + action.handler(mockContext({ targetPath: '../' })), + ).rejects.toThrowError( + /relative path is not allowed to refer to a directory outside its parent/i, + ); + }); + + it('throws if copyWithoutRender parameter is not an array', async () => { + await expect(() => + action.handler( + mockContext({ copyWithoutRender: ('abc' as unknown) as string[] }), + ), + ).rejects.toThrowError(/copyWithoutRender must be an array/i); + }); + + describe('with valid input', () => { + let context: ActionContext; + + beforeEach(async () => { + context = mockContext({ + values: { + name: 'test-project', + count: 1234, + itemList: ['first', 'second', 'third'], + }, + }); + + mockFetchContents.mockImplementation(({ outputPath }) => { + mockFs({ + [outputPath]: { + 'empty-dir-${{ values.count }}': {}, + 'static.txt': 'static content', + '${{ values.name }}.txt': 'static content', + subdir: { + 'templated-content.txt': + '${{ values.name }}: ${{ values.count }}', + }, + '.${{ values.name }}': '${{ values.itemList | dump }}', + 'a-binary-file.png': aBinaryFile, + }, + }); + + return Promise.resolve(); + }); + + await action.handler(context); + }); + + it('uses fetchContents to retrieve the template content', () => { + expect(mockFetchContents).toHaveBeenCalledWith( + expect.objectContaining({ + baseUrl: context.baseUrl, + fetchUrl: context.input.url, + }), + ); + }); + + it('copies files with no templating in names or content successfully', async () => { + await expect( + fs.readFile(`${workspacePath}/target/static.txt`, 'utf-8'), + ).resolves.toEqual('static content'); + }); + + it('copies files with templated names successfully', async () => { + await expect( + fs.readFile(`${workspacePath}/target/test-project.txt`, 'utf-8'), + ).resolves.toEqual('static content'); + }); + + it('copies files with templated content successfully', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/subdir/templated-content.txt`, + 'utf-8', + ), + ).resolves.toEqual('test-project: 1234'); + }); + + it('processes dotfiles', async () => { + await expect( + fs.readFile(`${workspacePath}/target/.test-project`, 'utf-8'), + ).resolves.toEqual('["first","second","third"]'); + }); + + it('copies empty directories', async () => { + await expect( + fs.readdir(`${workspacePath}/target/empty-dir-1234`, 'utf-8'), + ).resolves.toEqual([]); + }); + + it('copies binary files as-is without processing them', async () => { + await expect( + fs.readFile(`${workspacePath}/target/a-binary-file.png`), + ).resolves.toEqual(aBinaryFile); + }); + }); + + describe('copyWithoutRender', () => { + let context: ActionContext; + + beforeEach(async () => { + context = mockContext({ + values: { + name: 'test-project', + count: 1234, + }, + copyWithoutRender: ['.unprocessed'], + }); + + mockFetchContents.mockImplementation(({ outputPath }) => { + mockFs({ + [outputPath]: { + processed: { + 'templated-content-${{ values.name }}.txt': + '${{ values.count }}', + }, + '.unprocessed': { + 'templated-content-${{ values.name }}.txt': + '${{ values.count }}', + }, + }, + }); + + return Promise.resolve(); + }); + + await action.handler(context); + }); + + it('ignores template syntax in files matched in copyWithoutRender', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/.unprocessed/templated-content-\${{ values.name }}.txt`, + 'utf-8', + ), + ).resolves.toEqual('${{ values.count }}'); + }); + + it('processes files not matched in copyWithoutRender', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/processed/templated-content-test-project.txt`, + 'utf-8', + ), + ).resolves.toEqual('1234'); + }); + }); + }); + + describe('cookiecutter compatibility mode', () => { + let context: ActionContext; + + beforeEach(async () => { + context = mockContext({ + values: { + name: 'test-project', + count: 1234, + itemList: ['first', 'second', 'third'], + }, + cookiecutterCompat: true, + }); + + mockFetchContents.mockImplementation(({ outputPath }) => { + mockFs({ + [outputPath]: { + '{{ cookiecutter.name }}.txt': 'static content', + subdir: { + 'templated-content.txt': + '{{ cookiecutter.name }}: {{ cookiecutter.count }}', + }, + '{{ cookiecutter.name }}.json': + '{{ cookiecutter.itemList | jsonify }}', + }, + }); + + return Promise.resolve(); + }); + + await action.handler(context); + }); + + it('copies files with cookiecutter-style templated names successfully', async () => { + await expect( + fs.readFile(`${workspacePath}/target/test-project.txt`, 'utf-8'), + ).resolves.toEqual('static content'); + }); + + it('copies files with cookiecutter-style templated content successfully', async () => { + await expect( + fs.readFile( + `${workspacePath}/target/subdir/templated-content.txt`, + 'utf-8', + ), + ).resolves.toEqual('test-project: 1234'); + }); + + it('includes the jsonify filter', async () => { + await expect( + fs.readFile(`${workspacePath}/target/test-project.json`, 'utf-8'), + ).resolves.toEqual('["first","second","third"]'); + }); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts new file mode 100644 index 0000000000..87e67f76be --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/fetch/template.ts @@ -0,0 +1,239 @@ +/* + * 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 { resolve as resolvePath } from 'path'; +import { resolveSafeChildPath, UrlReader } from '@backstage/backend-common'; +import { InputError } from '@backstage/errors'; +import { ScmIntegrations } from '@backstage/integration'; +import { fetchContents } from './helpers'; +import { createTemplateAction } from '../../createTemplateAction'; +import globby from 'globby'; +import nunjucks from 'nunjucks'; +import fs from 'fs-extra'; +import { isBinaryFile } from 'isbinaryfile'; + +/* + * Maximise compatibility with Jinja (and therefore cookiecutter) + * using nunjucks jinja compat mode. Since this method mutates + * the global nunjucks instance, we can't enable this per-template, + * or only for templates with cookiecutter compat enabled, so the + * next best option is to explicitly enable it globally and allow + * folks to rely on jinja compatibility behaviour in fetch:template + * templates if they wish. + * + * cf. https://mozilla.github.io/nunjucks/api.html#installjinjacompat + */ +nunjucks.installJinjaCompat(); + +export type FetchTemplateInput = { + url: string; + targetPath?: string; + values: any; + copyWithoutRender?: string[]; + cookiecutterCompat?: boolean; +}; + +export function createFetchTemplateAction(options: { + reader: UrlReader; + integrations: ScmIntegrations; +}) { + const { reader, integrations } = options; + + return createTemplateAction({ + id: 'fetch:template', + description: + "Downloads a skeleton, templates variables into file and directory names and content, and places the result in the workspace, or optionally in a subdirectory specified by the 'targetPath' input option.", + schema: { + input: { + type: 'object', + required: ['url'], + properties: { + url: { + title: 'Fetch URL', + description: + 'Relative path or absolute URL pointing to the directory tree to fetch', + type: 'string', + }, + targetPath: { + title: 'Target Path', + description: + 'Target path within the working directory to download the contents to. Defaults to the working directory root.', + type: 'string', + }, + values: { + title: 'Template Values', + description: 'Values to pass on to the templating engine', + type: 'object', + }, + copyWithoutRender: { + title: 'Copy Without Render', + description: + 'An array of glob patterns. Any files or directories which match are copied without being processed as templates.', + type: 'array', + items: { + type: 'string', + }, + }, + cookiecutterCompat: { + title: 'Cookiecutter compatibility mode', + description: + 'Enable features to maximise compatibility with templates built for fetch:cookiecutter', + type: 'boolean', + }, + }, + }, + }, + async handler(ctx) { + ctx.logger.info('Fetching template content from remote URL'); + + const workDir = await ctx.createTemporaryDirectory(); + const templateDir = resolvePath(workDir, 'template'); + + const targetPath = ctx.input.targetPath ?? './'; + const outputDir = resolveSafeChildPath(ctx.workspacePath, targetPath); + + if ( + ctx.input.copyWithoutRender && + !Array.isArray(ctx.input.copyWithoutRender) + ) { + throw new InputError( + 'Fetch action input copyWithoutRender must be an Array', + ); + } + + await fetchContents({ + reader, + integrations, + baseUrl: ctx.baseUrl, + fetchUrl: ctx.input.url, + outputPath: templateDir, + }); + + ctx.logger.info('Listing files and directories in template'); + const allEntriesInTemplate = await globby(`**/*`, { + cwd: templateDir, + dot: true, + onlyFiles: false, + markDirectories: true, + }); + + const nonTemplatedEntries = new Set( + ( + await Promise.all( + (ctx.input.copyWithoutRender || []).map(pattern => + globby(pattern, { + cwd: templateDir, + dot: true, + onlyFiles: false, + markDirectories: true, + }), + ), + ) + ).flat(), + ); + + // Create a templater + const templater = nunjucks.configure({ + ...(ctx.input.cookiecutterCompat + ? {} + : { + tags: { + // TODO(mtlewis/orkohunter): Document Why we are changing the literals? Not here, but on scaffolder docs. ADR? + variableStart: '${{', + variableEnd: '}}', + }, + }), + // We don't want this builtin auto-escaping, since uses HTML escape sequences + // like `"` - the correct way to escape strings in our case depends on + // the file type. + autoescape: false, + }); + + if (ctx.input.cookiecutterCompat) { + // The "jsonify" filter built into cookiecutter is common + // in fetch:cookiecutter templates, so when compat mode + // is enabled we alias the "dump" filter from nunjucks as + // jsonify. Dump accepts an optional `spaces` parameter + // which enables indented output, but when this parameter + // is not supplied it works identically to jsonify. + // + // cf. https://cookiecutter.readthedocs.io/en/latest/advanced/template_extensions.html?highlight=jsonify#jsonify-extension + // cf. https://mozilla.github.io/nunjucks/templating.html#dump + templater.addFilter('jsonify', templater.getFilter('dump')); + } + + // Cookiecutter prefixes all parameters in templates with + // `cookiecutter.`. To replicate this, we wrap our parameters + // in an object with a `cookiecutter` property when compat + // mode is enabled. + const { cookiecutterCompat, values } = ctx.input; + const context = { + [cookiecutterCompat ? 'cookiecutter' : 'values']: values, + }; + + ctx.logger.info( + `Processing ${allEntriesInTemplate.length} template files/directories with input values`, + ctx.input.values, + ); + + for (const location of allEntriesInTemplate) { + const shouldCopyWithoutRender = nonTemplatedEntries.has(location); + + const outputPath = resolvePath( + outputDir, + shouldCopyWithoutRender + ? location + : templater.renderString(location, context), + ); + + if (shouldCopyWithoutRender) { + ctx.logger.info( + `Copying file/directory ${location} without processing since it matches a pattern in "copyWithoutRender".`, + ); + } + + if (location.endsWith('/')) { + ctx.logger.info( + `Writing directory ${location} to template output path.`, + ); + await fs.ensureDir(outputPath); + } else { + const inputFilePath = resolvePath(templateDir, location); + + if (await isBinaryFile(inputFilePath)) { + ctx.logger.info( + `Copying binary file ${location} to template output path.`, + ); + await fs.copy(inputFilePath, outputPath); + } else { + ctx.logger.info( + `Writing file ${location} to template output path.`, + ); + const inputFileContents = await fs.readFile(inputFilePath, 'utf-8'); + await fs.outputFile( + outputPath, + shouldCopyWithoutRender + ? inputFileContents + : templater.renderString(inputFileContents, context), + ); + } + } + } + + ctx.logger.info(`Template result written to ${outputDir}`); + }, + }); +} diff --git a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts index 32527afd5d..2ba30bb3db 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.test.ts @@ -67,8 +67,7 @@ describe('createPublishGithubPullRequestAction', () => { beforeEach(() => { input = { - owner: 'myorg', - repo: 'myrepo', + repoUrl: 'github.com?owner=myorg&repo=myrepo', title: 'Create my new app', branchName: 'new-app', description: 'This PR is really good', @@ -127,8 +126,7 @@ describe('createPublishGithubPullRequestAction', () => { beforeEach(() => { input = { - owner: 'myorg', - repo: 'myrepo', + repoUrl: 'github.com?owner=myorg&repo=myrepo', title: 'Create my new app', branchName: 'new-app', description: 'This PR is really good', 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 18ceeca3c9..1c46e80d03 100644 --- a/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts +++ b/plugins/scaffolder-backend/src/scaffolder/actions/builtin/publish/githubPullRequest.ts @@ -49,10 +49,7 @@ export type GithubPullRequestActionInput = { title: string; branchName: string; description: string; - owner?: string; - repo?: string; - repoUrl?: string; - host?: string; + repoUrl: string; targetPath?: string; sourcePath?: string; }; @@ -119,18 +116,13 @@ export const createPublishGithubPullRequestAction = ({ id: 'publish:github:pull-request', schema: { input: { - required: ['owner', 'repo', 'title', 'description', 'branchName'], + required: ['repoUrl', 'title', 'description', 'branchName'], type: 'object', properties: { - owner: { + repoUrl: { + title: 'Repository Location', + description: `Accepts the format 'github.com?repo=reponame&owner=owner' where 'reponame' is the repository name and 'owner' is an organization or username`, type: 'string', - title: 'Repository owner', - description: 'The owner of the target repository', - }, - repo: { - type: 'string', - title: 'Repository', - description: 'The github repository to create the file in', }, branchName: { type: 'string', @@ -173,8 +165,6 @@ export const createPublishGithubPullRequestAction = ({ }, }, async handler(ctx) { - let { owner, repo } = ctx.input; - let host = 'github.com'; const { repoUrl, branchName, @@ -184,18 +174,7 @@ export const createPublishGithubPullRequestAction = ({ sourcePath, } = ctx.input; - if (repoUrl) { - const parsed = parseRepoUrl(repoUrl); - host = parsed.host; - owner = parsed.owner; - repo = parsed.repo; - } - - if (!host || !owner || !repo) { - throw new InputError( - 'must provide either valid repo URL or owner and repo as parameters', - ); - } + const { owner, repo, host } = parseRepoUrl(repoUrl); const client = await clientFactory({ integrations, host, owner, repo }); const fileRoot = sourcePath diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts index 72163a50b4..b5e8d19e62 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.test.ts @@ -203,6 +203,39 @@ describe('TaskWorker', () => { expect((event?.body?.output as JsonObject).result).toBe('winning'); }); + it('should execute steps conditionally with eq helper', async () => { + const broker = new StorageTaskBroker(storage, logger); + const taskWorker = new TaskWorker({ + logger, + workingDirectory: os.tmpdir(), + actionRegistry, + taskBroker: broker, + }); + + const { taskId } = await broker.dispatch({ + steps: [ + { id: 'test', name: 'test', action: 'test-action' }, + { + id: 'conditional', + name: 'conditional', + action: 'test-action', + if: '{{ eq steps.test.output.testOutput "winning" }}', + }, + ], + output: { + result: '{{ steps.conditional.output.testOutput }}', + }, + values: {}, + }); + + const task = await broker.claim(); + await taskWorker.runOneTask(task); + + const { events } = await storage.listEvents({ taskId }); + const event = events.find(e => e.type === 'completion'); + expect((event?.body?.output as JsonObject).result).toBe('winning'); + }); + it('should skip steps conditionally', async () => { const broker = new StorageTaskBroker(storage, logger); const taskWorker = new TaskWorker({ diff --git a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts index d6067280c3..b4ba936719 100644 --- a/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts +++ b/plugins/scaffolder-backend/src/scaffolder/tasks/TaskWorker.ts @@ -56,6 +56,8 @@ export class TaskWorker { this.handlebars.registerHelper('json', obj => JSON.stringify(obj)); this.handlebars.registerHelper('not', value => !isTruthy(value)); + + this.handlebars.registerHelper('eq', (a, b) => a === b); } start() { diff --git a/plugins/scaffolder/CHANGELOG.md b/plugins/scaffolder/CHANGELOG.md index 5a3231a133..53a2043456 100644 --- a/plugins/scaffolder/CHANGELOG.md +++ b/plugins/scaffolder/CHANGELOG.md @@ -1,5 +1,89 @@ # @backstage/plugin-scaffolder +## 0.10.0 + +### Minor Changes + +- 60e830222: Support for `Template` kinds with version `backstage.io/v1alpha1` has now been removed. This means that the old method of running templates with `Preparers`, `Templaters` and `Publishers` has also been removed. If you had any logic in these abstractions, they should now be moved to `actions` instead, and you can find out more about those in the [documentation](https://backstage.io/docs/features/software-templates/writing-custom-actions) + + If you need any help migrating existing templates, there's a [migration guide](https://backstage.io/docs/features/software-templates/migrating-from-v1alpha1-to-v1beta2). Reach out to us on Discord in the #support channel if you're having problems. + + The `scaffolder-backend` now no longer requires these `Preparers`, `Templaters`, and `Publishers` to be passed in, now all it needs is the `containerRunner`. + + Please update your `packages/backend/src/plugins/scaffolder.ts` like the following + + ```diff + - import { + - DockerContainerRunner, + - SingleHostDiscovery, + - } from '@backstage/backend-common'; + + import { DockerContainerRunner } from '@backstage/backend-common'; + import { CatalogClient } from '@backstage/catalog-client'; + - import { + - CookieCutter, + - CreateReactAppTemplater, + - createRouter, + - Preparers, + - Publishers, + - Templaters, + - } from '@backstage/plugin-scaffolder-backend'; + + import { createRouter } from '@backstage/plugin-scaffolder-backend'; + import Docker from 'dockerode'; + import { Router } from 'express'; + import type { PluginEnvironment } from '../types'; + + export default async function createPlugin({ + config, + database, + reader, + + discovery, + }: PluginEnvironment): Promise { + const dockerClient = new Docker(); + const containerRunner = new DockerContainerRunner({ dockerClient }); + + - const cookiecutterTemplater = new CookieCutter({ containerRunner }); + - const craTemplater = new CreateReactAppTemplater({ containerRunner }); + - const templaters = new Templaters(); + + - templaters.register('cookiecutter', cookiecutterTemplater); + - templaters.register('cra', craTemplater); + - + - const preparers = await Preparers.fromConfig(config, { logger }); + - const publishers = await Publishers.fromConfig(config, { logger }); + + - const discovery = SingleHostDiscovery.fromConfig(config); + const catalogClient = new CatalogClient({ discoveryApi: discovery }); + + return await createRouter({ + - preparers, + - templaters, + - publishers, + + containerRunner, + logger, + config, + database, + + ``` + +### Patch Changes + +- 02b962394: Added a `context` parameter to validator functions, letting them have access to + the API holder. + + If you have implemented custom validators and use `createScaffolderFieldExtension`, + your `validation` function can now optionally accept a third parameter, + `context: { apiHolder: ApiHolder }`. + +- 6841e0113: fix minor version of git-url-parse as 11.5.x introduced a bug for Bitbucket Server +- 0adfae5c8: add support for uiSchema on dependent form fields +- bd764f78a: Pass through the `idToken` in `Authorization` Header for `listActions` request +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/catalog-client@0.3.16 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.9.10 ### Patch Changes diff --git a/plugins/scaffolder/api-report.md b/plugins/scaffolder/api-report.md index 7982ad997e..2f9d10b7ac 100644 --- a/plugins/scaffolder/api-report.md +++ b/plugins/scaffolder/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 { ApiHolder } from '@backstage/core-plugin-api'; import { ApiRef } from '@backstage/core-plugin-api'; @@ -23,21 +24,29 @@ import { RouteRef } from '@backstage/core-plugin-api'; import { ScmIntegrationRegistry } from '@backstage/integration'; // @public (undocumented) -export function createScaffolderFieldExtension(options: FieldExtensionOptions): Extension<() => null>; +export function createScaffolderFieldExtension( + options: FieldExtensionOptions, +): Extension<() => null>; // @public (undocumented) -export type CustomFieldValidator = ((data: T, field: FieldValidation) => void) | ((data: T, field: FieldValidation, context: { - apiHolder: ApiHolder; -}) => void); +export type CustomFieldValidator = + | ((data: T, field: FieldValidation) => void) + | (( + data: T, + field: FieldValidation, + context: { + apiHolder: ApiHolder; + }, + ) => void); // @public (undocumented) export const EntityPickerFieldExtension: () => null; // @public (undocumented) export type FieldExtensionOptions = { - name: string; - component: (props: FieldProps) => JSX.Element | null; - validation?: CustomFieldValidator; + name: string; + component: (props: FieldProps) => JSX.Element | null; + validation?: CustomFieldValidator; }; // @public (undocumented) @@ -48,26 +57,33 @@ export const RepoUrlPickerFieldExtension: () => null; // @public (undocumented) export interface ScaffolderApi { - // (undocumented) - getIntegrationsList(options: { - allowedHosts: string[]; - }): Promise<{ - type: string; - title: string; - host: string; - }[]>; - // (undocumented) - getTask(taskId: string): Promise; - // (undocumented) - getTemplateParameterSchema(templateName: EntityName): Promise; - // (undocumented) - listActions(): Promise; - scaffold(templateName: string, values: Record): Promise; - // (undocumented) - streamLogs({ taskId, after, }: { - taskId: string; - after?: number; - }): Observable; + // (undocumented) + getIntegrationsList(options: { + allowedHosts: string[]; + }): Promise< + { + type: string; + title: string; + host: string; + }[] + >; + // (undocumented) + getTask(taskId: string): Promise; + // (undocumented) + getTemplateParameterSchema( + templateName: EntityName, + ): Promise; + // (undocumented) + listActions(): Promise; + scaffold(templateName: string, values: Record): Promise; + // (undocumented) + streamLogs({ + taskId, + after, + }: { + taskId: string; + after?: number; + }): Observable; } // @public (undocumented) @@ -75,31 +91,38 @@ export const scaffolderApiRef: ApiRef; // @public (undocumented) export class ScaffolderClient implements ScaffolderApi { - constructor(options: { - discoveryApi: DiscoveryApi; - identityApi: IdentityApi; - scmIntegrationsApi: ScmIntegrationRegistry; - }); - // (undocumented) - getIntegrationsList(options: { - allowedHosts: string[]; - }): Promise<{ - type: string; - title: string; - host: string; - }[]>; - // (undocumented) - getTask(taskId: string): Promise; - // (undocumented) - getTemplateParameterSchema(templateName: EntityName): Promise; - // (undocumented) - listActions(): Promise; - scaffold(templateName: string, values: Record): Promise; - // (undocumented) - streamLogs({ taskId, after, }: { - taskId: string; - after?: number; - }): Observable; + constructor(options: { + discoveryApi: DiscoveryApi; + identityApi: IdentityApi; + scmIntegrationsApi: ScmIntegrationRegistry; + }); + // (undocumented) + getIntegrationsList(options: { + allowedHosts: string[]; + }): Promise< + { + type: string; + title: string; + host: string; + }[] + >; + // (undocumented) + getTask(taskId: string): Promise; + // (undocumented) + getTemplateParameterSchema( + templateName: EntityName, + ): Promise; + // (undocumented) + listActions(): Promise; + scaffold(templateName: string, values: Record): Promise; + // (undocumented) + streamLogs({ + taskId, + after, + }: { + taskId: string; + after?: number; + }): Observable; } // @public (undocumented) @@ -109,17 +132,16 @@ export const ScaffolderFieldExtensions: React_2.ComponentType; export const ScaffolderPage: () => JSX.Element; // @public (undocumented) -const scaffolderPlugin: BackstagePlugin<{ +const scaffolderPlugin: BackstagePlugin< + { root: RouteRef; -}, { + }, + { registerComponent: ExternalRouteRef; -}>; - -export { scaffolderPlugin as plugin } - -export { scaffolderPlugin } - + } +>; +export { scaffolderPlugin as plugin }; +export { scaffolderPlugin }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/scaffolder/package.json b/plugins/scaffolder/package.json index 88d973377d..24b48e7fd1 100644 --- a/plugins/scaffolder/package.json +++ b/plugins/scaffolder/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-scaffolder", - "version": "0.9.10", + "version": "0.10.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,15 +30,15 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-client": "^0.3.15", - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.5", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -47,10 +47,11 @@ "@rjsf/material-ui": "^3.0.0", "@types/react": "^16.9", "classnames": "^2.2.6", - "git-url-parse": "^11.4.4", + "git-url-parse": "~11.4.4", "humanize-duration": "^3.25.1", "immer": "^9.0.1", "json-schema": "^0.3.0", + "lodash": "^4.17.21", "luxon": "^1.25.0", "react": "^16.13.1", "react-dom": "^16.13.1", @@ -64,7 +65,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.test.tsx b/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.test.tsx deleted file mode 100644 index 3b85125da0..0000000000 --- a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.test.tsx +++ /dev/null @@ -1,109 +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 { CatalogApi } from '@backstage/catalog-client'; -import { Entity } from '@backstage/catalog-model'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; -import { render } from '@testing-library/react'; -import React from 'react'; -import { EntityFilterGroupsProvider } from '../../filter'; -import { ResultsFilter } from './ResultsFilter'; - -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; -import { - IdentityApi, - identityApiRef, - storageApiRef, -} from '@backstage/core-plugin-api'; - -describe('Results Filter', () => { - const catalogApi: Partial = { - getEntities: () => - Promise.resolve({ - items: [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity1', - tags: ['java'], - }, - spec: { - owner: 'tools@example.com', - type: 'service', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity2', - }, - spec: { - owner: 'not-tools@example.com', - type: 'service', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity3', - tags: ['java', 'test'], - }, - spec: { - owner: 'tools@example.com', - type: 'service', - }, - }, - ] as Entity[], - }), - }; - - const identityApi: Partial = { - getUserId: () => 'tools@example.com', - }; - - const renderWrapped = (children: React.ReactNode) => - render( - wrapInTestApp( - - {children}, - , - ), - ); - - it('should render all available categories', async () => { - const categories = ['test', 'java']; - const { findByText } = renderWrapped( - , - ); - for (const category of categories) { - expect( - await findByText( - category.charAt(0).toLocaleUpperCase('en-US') + category.slice(1), - ), - ).toBeInTheDocument(); - } - }); -}); diff --git a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx b/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx deleted file mode 100644 index 3386b03cf7..0000000000 --- a/plugins/scaffolder/src/components/ResultsFilter/ResultsFilter.tsx +++ /dev/null @@ -1,121 +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 { - Button, - Checkbox, - Divider, - List, - ListItem, - ListItemText, - makeStyles, - Theme, - Typography, -} from '@material-ui/core'; -import React, { useContext } from 'react'; -import { filterGroupsContext } from '../../filter/context'; - -const useStyles = makeStyles(theme => ({ - filterBox: { - display: 'flex', - margin: theme.spacing(2, 0, 0, 0), - }, - filterBoxTitle: { - margin: theme.spacing(1, 0, 0, 1), - fontWeight: 'bold', - flex: 1, - }, - title: { - margin: theme.spacing(1, 0, 0, 1), - textTransform: 'uppercase', - fontSize: 12, - fontWeight: 'bold', - }, - checkbox: { - padding: theme.spacing(0, 1, 0, 1), - }, -})); - -type Props = { - availableCategories: string[]; -}; - -/** - * The additional results filter in the sidebar. - */ -export const ResultsFilter = ({ availableCategories }: Props) => { - const classes = useStyles(); - - const context = useContext(filterGroupsContext); - if (!context) { - throw new Error(`Must be used inside an EntityFilterGroupsProvider`); - } - - const { selectedCategories, setSelectedCategories } = context; - - return ( - <> -

- - Refine Results - {' '} - -
- - - Categories - - - {availableCategories.map(category => { - const labelId = `checkbox-list-label-${category}`; - return ( - - setSelectedCategories( - selectedCategories.includes(category) - ? selectedCategories.filter( - selectedCategory => selectedCategory !== category, - ) - : [...selectedCategories, category], - ) - } - > - - - - ); - })} - - - ); -}; diff --git a/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx deleted file mode 100644 index 5875646fae..0000000000 --- a/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.test.tsx +++ /dev/null @@ -1,271 +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 React from 'react'; -import { MockStorageApi, wrapInTestApp } from '@backstage/test-utils'; -import { fireEvent, render, waitFor } from '@testing-library/react'; -import { CatalogApi } from '@backstage/catalog-client'; -import { Entity } from '@backstage/catalog-model'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { EntityFilterGroupsProvider } from '../../filter'; -import { ButtonGroup, ScaffolderFilter } from './ScaffolderFilter'; - -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; -import { - IdentityApi, - identityApiRef, - storageApiRef, -} from '@backstage/core-plugin-api'; - -describe('Catalog Filter', () => { - const catalogApi: Partial = { - getEntities: () => - Promise.resolve({ - items: [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity1', - }, - spec: { - owner: 'tools@example.com', - type: 'service', - }, - }, - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { - name: 'Entity2', - }, - spec: { - owner: 'not-tools@example.com', - type: 'service', - }, - }, - ] as Entity[], - }), - }; - - const identityApi: Partial = { - getUserId: () => 'tools@example.com', - }; - - const renderWrapped = (children: React.ReactNode) => - render( - wrapInTestApp( - - {children}, - , - ), - ); - - describe('filter groups', () => { - it('should render the different groups', async () => { - const mockGroups: ButtonGroup[] = [ - { name: 'Test Group 1', items: [] }, - { name: 'Test Group 2', items: [] }, - ]; - const { findByText } = renderWrapped( - , - ); - for (const group of mockGroups) { - expect(await findByText(group.name)).toBeInTheDocument(); - } - }); - }); - - describe('filter items', () => { - it('should render the different items and their names', async () => { - const mockGroups: ButtonGroup[] = [ - { - name: 'Test Group 1', - items: [ - { - id: 'all', - label: 'First Label', - filterFn: () => true, - }, - { - id: 'starred', - label: 'Second Label', - filterFn: () => false, - }, - ], - }, - ]; - - const { findByText } = renderWrapped( - , - ); - - for (const item of mockGroups[0].items) { - expect(await findByText(item.label)).toBeInTheDocument(); - } - }); - - it('selects the first item if no desired initial one is set', async () => { - const mockGroups: ButtonGroup[] = [ - { - name: 'Test Group 1', - items: [ - { - id: 'all', - label: 'First Label', - filterFn: () => true, - }, - { - id: 'starred', - label: 'Second Label', - filterFn: () => false, - }, - ], - }, - ]; - - const onChange = jest.fn(); - - renderWrapped( - , - ); - - await waitFor(() => { - expect(onChange).toHaveBeenLastCalledWith({ - id: 'all', - label: 'First Label', - }); - }); - }); - - it('selects the initial item', async () => { - const mockGroups: ButtonGroup[] = [ - { - name: 'Test Group 1', - items: [ - { - id: 'all', - label: 'First Label', - filterFn: () => true, - }, - { - id: 'starred', - label: 'Second Label', - filterFn: () => false, - }, - ], - }, - ]; - - const onChange = jest.fn(); - - renderWrapped( - , - ); - - await waitFor(() => { - expect(onChange).toHaveBeenLastCalledWith({ - id: 'starred', - label: 'Second Label', - }); - }); - }); - - it('can change the selected item', async () => { - const mockGroups: ButtonGroup[] = [ - { - name: 'Test Group 1', - items: [ - { - id: 'all', - label: 'First Label', - filterFn: () => true, - }, - { - id: 'starred', - label: 'Second Label', - filterFn: () => false, - }, - ], - }, - ]; - - const onChange = jest.fn(); - - const { findByText } = renderWrapped( - , - ); - - await waitFor(() => { - expect(onChange).toHaveBeenLastCalledWith({ - id: 'all', - label: 'First Label', - }); - }); - - fireEvent.click(await findByText('Second Label')); - - await waitFor(() => { - expect(onChange).toHaveBeenLastCalledWith({ - id: 'starred', - label: 'Second Label', - }); - }); - }); - - it('displays match counts properly', async () => { - const mockGroups: ButtonGroup[] = [ - { - name: 'Test Group 1', - items: [ - { - id: 'owned', - label: 'First Label', - filterFn: entity => entity.spec?.owner === 'tools@example.com', - }, - ], - }, - ]; - - const { findByText } = renderWrapped( - , - ); - - expect(await findByText('1')).toBeInTheDocument(); - }); - }); -}); diff --git a/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx b/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx deleted file mode 100644 index 2637a1f95c..0000000000 --- a/plugins/scaffolder/src/components/ScaffolderFilter/ScaffolderFilter.tsx +++ /dev/null @@ -1,211 +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 { Entity } from '@backstage/catalog-model'; -import { - Card, - List, - ListItemIcon, - ListItemSecondaryAction, - ListItemText, - makeStyles, - MenuItem, - Theme, - Typography, -} from '@material-ui/core'; -import React, { - useCallback, - useEffect, - useMemo, - useRef, - useState, -} from 'react'; -import { FilterGroup, useEntityFilterGroup } from '../../filter'; -import { IconComponent } from '@backstage/core-plugin-api'; - -export type ButtonGroup = { - name: string; - items: { - id: string; - label: string; - icon?: IconComponent; - filterFn: (entity: Entity) => boolean; - }[]; -}; - -const useStyles = makeStyles(theme => ({ - root: { - backgroundColor: 'rgba(0, 0, 0, .11)', - boxShadow: 'none', - }, - title: { - margin: theme.spacing(1, 0, 0, 1), - textTransform: 'uppercase', - fontSize: 12, - fontWeight: 'bold', - }, - listIcon: { - minWidth: 30, - color: theme.palette.text.primary, - }, - menuItem: { - minHeight: theme.spacing(6), - }, - groupWrapper: { - margin: theme.spacing(1, 1, 2, 1), - }, - menuTitle: { - fontWeight: 500, - }, -})); - -type OnChangeCallback = (item: { id: string; label: string }) => void; - -type Props = { - buttonGroups: ButtonGroup[]; - initiallySelected: string; - onChange?: OnChangeCallback; -}; - -/** - * The main filter group in the sidebar, toggling owned/starred/all. - */ -export const ScaffolderFilter = ({ - buttonGroups, - onChange, - initiallySelected, -}: Props) => { - const classes = useStyles(); - const { currentFilter, setCurrentFilter, getFilterCount } = useFilter( - buttonGroups, - initiallySelected, - ); - - const onChangeRef = useRef(); - useEffect(() => { - onChangeRef.current = onChange; - }, [onChange]); - - const setCurrent = useCallback( - (item: { id: string; label: string }) => { - setCurrentFilter(item.id); - onChangeRef.current?.({ id: item.id, label: item.label }); - }, - [setCurrentFilter], - ); - - // Make one initial onChange to inform the surroundings about the selected - // item - useEffect(() => { - const items = buttonGroups.flatMap(g => g.items); - const item = items.find(i => i.id === initiallySelected) || items[0]; - if (item) { - onChangeRef.current?.({ id: item.id, label: item.label }); - } - // intentionally only happens on startup - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - - return ( - - {buttonGroups.map(group => ( - - - {group.name} - - - - {group.items.map(item => ( - setCurrent(item)} - selected={item.id === currentFilter} - className={classes.menuItem} - > - {item.icon && ( - - - - )} - - - {item.label} - - - - {getFilterCount(item.id) ?? '-'} - - - ))} - - - - ))} - - ); -}; - -function useFilter( - buttonGroups: ButtonGroup[], - initiallySelected: string, -): { - currentFilter: string; - setCurrentFilter: (filterId: string) => void; - getFilterCount: (filterId: string) => number | undefined; -} { - const [currentFilter, setCurrentFilter] = useState(initiallySelected); - - const filterGroup = useMemo( - () => ({ - filters: Object.fromEntries( - buttonGroups.flatMap(g => g.items).map(i => [i.id, i.filterFn]), - ), - }), - [buttonGroups], - ); - - const { setSelectedFilters, state } = useEntityFilterGroup( - 'primary-sidebar', - filterGroup, - [initiallySelected], - ); - - const setCurrent = useCallback( - (filterId: string) => { - setCurrentFilter(filterId); - setSelectedFilters([filterId]); - }, - [setCurrentFilter, setSelectedFilters], - ); - - const getFilterCount = useCallback( - (filterId: string) => { - if (state.type !== 'ready') { - return undefined; - } - return state.state.filters[filterId].matchCount; - }, - [state], - ); - - return { - currentFilter, - setCurrentFilter: setCurrent, - getFilterCount, - }; -} diff --git a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx index 65386b60b3..3fed099500 100644 --- a/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx +++ b/plugins/scaffolder/src/components/ScaffolderPage/ScaffolderPage.tsx @@ -14,32 +14,28 @@ * limitations under the License. */ -import { EntityMeta, TemplateEntityV1beta2 } from '@backstage/catalog-model'; import { Content, ContentHeader, Header, - ItemCardGrid, Lifecycle, - WarningPanel, Page, - Progress, SupportButton, } from '@backstage/core-components'; -import { useStarredEntities } from '@backstage/plugin-catalog-react'; -import { Button, Link, makeStyles, Typography } from '@material-ui/core'; -import StarIcon from '@material-ui/icons/Star'; -import React, { useEffect, useMemo, useState } from 'react'; +import { useRouteRef } from '@backstage/core-plugin-api'; +import { + EntityKindPicker, + EntityListProvider, + EntitySearchBar, + EntityTagPicker, + UserListPicker, +} from '@backstage/plugin-catalog-react'; +import { Button, makeStyles } from '@material-ui/core'; +import React from 'react'; import { Link as RouterLink } from 'react-router-dom'; -import { EntityFilterGroupsProvider, useFilteredEntities } from '../../filter'; import { registerComponentRouteRef } from '../../routes'; -import { ResultsFilter } from '../ResultsFilter/ResultsFilter'; -import { ScaffolderFilter } from '../ScaffolderFilter'; -import { ButtonGroup } from '../ScaffolderFilter/ScaffolderFilter'; -import SearchToolbar from '../SearchToolbar/SearchToolbar'; -import { TemplateCard } from '../TemplateCard'; - -import { configApiRef, useApi, useRouteRef } from '@backstage/core-plugin-api'; +import { TemplateList } from '../TemplateList'; +import { TemplateTypePicker } from '../TemplateTypePicker'; const useStyles = makeStyles(theme => ({ contentWrapper: { @@ -52,63 +48,9 @@ const useStyles = makeStyles(theme => ({ export const ScaffolderPageContents = () => { const styles = useStyles(); - const { - loading, - error, - filteredEntities, - availableCategories, - } = useFilteredEntities(); - const configApi = useApi(configApiRef); - const orgName = configApi.getOptionalString('organization.name') ?? 'Company'; - const { isStarredEntity } = useStarredEntities(); - const filterGroups = useMemo( - () => [ - { - name: orgName, - items: [ - { - id: 'all', - label: 'All', - filterFn: () => true, - }, - ], - }, - { - name: 'Personal', - items: [ - { - id: 'starred', - label: 'Starred', - icon: StarIcon, - filterFn: isStarredEntity, - }, - ], - }, - ], - [isStarredEntity, orgName], - ); - const [search, setSearch] = useState(''); - const [matchingEntities, setMatchingEntities] = useState( - [] as TemplateEntityV1beta2[], - ); - - const matchesQuery = (metadata: EntityMeta, query: string) => - `${metadata.title}`.toLocaleUpperCase('en-US').includes(query) || - metadata.tags?.join('').toLocaleUpperCase('en-US').indexOf(query) !== -1; const registerComponentLink = useRouteRef(registerComponentRouteRef); - useEffect(() => { - if (search.length === 0) { - return setMatchingEntities(filteredEntities); - } - return setMatchingEntities( - filteredEntities.filter(template => - matchesQuery(template.metadata, search.toLocaleUpperCase('en-US')), - ), - ); - }, [search, filteredEntities]); - return (
{
- - +
- {loading && } - - {error && ( - - {error.message} - - )} - - {!error && - !loading && - matchingEntities && - !matchingEntities.length && ( - - No templates found that match your filter. Learn more about{' '} - - adding templates - - . - - )} - - - {matchingEntities && - matchingEntities?.length > 0 && - matchingEntities.map((template, i) => ( - - ))} - +
@@ -185,7 +102,7 @@ export const ScaffolderPageContents = () => { }; export const ScaffolderPage = () => ( - + - + ); diff --git a/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx new file mode 100644 index 0000000000..009342c379 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateList/TemplateList.tsx @@ -0,0 +1,62 @@ +/* + * 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 { TemplateEntityV1beta2 } from '@backstage/catalog-model'; +import { + ItemCardGrid, + Progress, + WarningPanel, +} from '@backstage/core-components'; +import { useEntityListProvider } from '@backstage/plugin-catalog-react'; +import { Link, Typography } from '@material-ui/core'; +import { TemplateCard } from '../TemplateCard'; + +export const TemplateList = () => { + const { loading, error, entities } = useEntityListProvider(); + return ( + <> + {loading && } + + {error && ( + + {error.message} + + )} + + {!error && !loading && !entities.length && ( + + No templates found that match your filter. Learn more about{' '} + + adding templates + + . + + )} + + + {entities && + entities?.length > 0 && + entities.map((template, i) => ( + + ))} + + + ); +}; diff --git a/plugins/scaffolder/src/components/ScaffolderFilter/index.ts b/plugins/scaffolder/src/components/TemplateList/index.ts similarity index 91% rename from plugins/scaffolder/src/components/ScaffolderFilter/index.ts rename to plugins/scaffolder/src/components/TemplateList/index.ts index 6fbadb81fa..b9ec700d74 100644 --- a/plugins/scaffolder/src/components/ScaffolderFilter/index.ts +++ b/plugins/scaffolder/src/components/TemplateList/index.ts @@ -13,5 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -export { ScaffolderFilter } from './ScaffolderFilter'; +export { TemplateList } from './TemplateList'; diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx new file mode 100644 index 0000000000..baeefc7d99 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.test.tsx @@ -0,0 +1,138 @@ +/* + * 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 { fireEvent } from '@testing-library/react'; +import { capitalize } from 'lodash'; +import { CatalogApi } from '@backstage/catalog-client'; +import { Entity } from '@backstage/catalog-model'; +import { TemplateTypePicker } from './TemplateTypePicker'; +import { + catalogApiRef, + EntityKindFilter, + MockEntityListContextProvider, +} from '@backstage/plugin-catalog-react'; +import { AlertApi, alertApiRef } from '@backstage/core-plugin-api'; +import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; +import { renderWithEffects } from '../../../../../packages/test-utils-core/src'; + +const entities: Entity[] = [ + { + apiVersion: '1', + kind: 'Template', + metadata: { + name: 'template-1', + }, + spec: { + type: 'service', + }, + }, + { + apiVersion: '1', + kind: 'Template', + metadata: { + name: 'template-2', + }, + spec: { + type: 'website', + }, + }, + { + apiVersion: '1', + kind: 'Template', + metadata: { + name: 'template-3', + }, + spec: { + type: 'library', + }, + }, +]; + +const apis = ApiRegistry.from([ + [ + catalogApiRef, + ({ + getEntities: jest + .fn() + .mockImplementation(() => Promise.resolve({ items: entities })), + } as unknown) as CatalogApi, + ], + [ + alertApiRef, + ({ + post: jest.fn(), + } as unknown) as AlertApi, + ], +]); + +describe('', () => { + it('renders available entity types', async () => { + const rendered = await renderWithEffects( + + + + + , + ); + expect(rendered.getByText('Categories')).toBeInTheDocument(); + + entities.forEach(entity => { + expect( + rendered.getByLabelText(capitalize(entity.spec!.type as string)), + ).toBeInTheDocument(); + }); + }); + + it('sets the selected type filters', async () => { + const rendered = await renderWithEffects( + + + + + , + ); + + expect(rendered.getByLabelText('Service')).not.toBeChecked(); + expect(rendered.getByLabelText('Website')).not.toBeChecked(); + + fireEvent.click(rendered.getByLabelText('Service')); + expect(rendered.getByLabelText('Service')).toBeChecked(); + expect(rendered.getByLabelText('Website')).not.toBeChecked(); + + fireEvent.click(rendered.getByLabelText('Website')); + expect(rendered.getByLabelText('Service')).toBeChecked(); + expect(rendered.getByLabelText('Website')).toBeChecked(); + + fireEvent.click(rendered.getByLabelText('Service')); + expect(rendered.getByLabelText('Service')).not.toBeChecked(); + expect(rendered.getByLabelText('Website')).toBeChecked(); + + fireEvent.click(rendered.getByLabelText('Website')); + expect(rendered.getByLabelText('Service')).not.toBeChecked(); + expect(rendered.getByLabelText('Website')).not.toBeChecked(); + }); +}); diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx new file mode 100644 index 0000000000..b547af6143 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateTypePicker/TemplateTypePicker.tsx @@ -0,0 +1,89 @@ +/* + * 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 capitalize from 'lodash/capitalize'; +import { Progress } from '@backstage/core-components'; +import { + Box, + Checkbox, + FormControlLabel, + FormGroup, + makeStyles, + Theme, + Typography, +} from '@material-ui/core'; +import { useEntityTypeFilter } from '@backstage/plugin-catalog-react'; +import { alertApiRef, useApi } from '@backstage/core-plugin-api'; + +const useStyles = makeStyles(theme => ({ + checkbox: { + padding: theme.spacing(1, 1, 1, 2), + }, +})); + +export const TemplateTypePicker = () => { + const classes = useStyles(); + const alertApi = useApi(alertApiRef); + const { + error, + loading, + availableTypes, + selectedTypes, + setSelectedTypes, + } = useEntityTypeFilter(); + + if (loading) return ; + + if (!availableTypes) return null; + + if (error) { + alertApi.post({ + message: `Failed to load entity types`, + severity: 'error', + }); + return null; + } + + function toggleSelection(type: string) { + setSelectedTypes( + selectedTypes.includes(type) + ? selectedTypes.filter(t => t !== type) + : [...selectedTypes, type], + ); + } + + return ( + + Categories + + {availableTypes.map(type => ( + toggleSelection(type)} + className={classes.checkbox} + /> + } + label={capitalize(type)} + key={type} + /> + ))} + + + ); +}; diff --git a/plugins/scaffolder/src/components/TemplateTypePicker/index.ts b/plugins/scaffolder/src/components/TemplateTypePicker/index.ts new file mode 100644 index 0000000000..2dcd091311 --- /dev/null +++ b/plugins/scaffolder/src/components/TemplateTypePicker/index.ts @@ -0,0 +1,17 @@ +/* + * 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 { TemplateTypePicker } from './TemplateTypePicker'; diff --git a/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx b/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx deleted file mode 100644 index a5be11380f..0000000000 --- a/plugins/scaffolder/src/filter/EntityFilterGroupsProvider.tsx +++ /dev/null @@ -1,263 +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 { TemplateEntityV1beta2 } from '@backstage/catalog-model'; -import { catalogApiRef } from '@backstage/plugin-catalog-react'; -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { useAsyncFn } from 'react-use'; -import { filterGroupsContext, FilterGroupsContext } from './context'; -import { - EntityFilterFn, - FilterGroup, - FilterGroupState, - FilterGroupStates, -} from './types'; -import { useApi } from '@backstage/core-plugin-api'; - -/** - * Implementation of the shared filter groups state. - */ -export const EntityFilterGroupsProvider = ({ - children, -}: { - children?: React.ReactNode; -}) => { - const state = useProvideEntityFilters(); - return ( - - {children} - - ); -}; - -// The hook that implements the actual context building -function useProvideEntityFilters(): FilterGroupsContext { - const catalogApi = useApi(catalogApiRef); - const [{ value: entities, error }, doReload] = useAsyncFn(async () => { - const response = await catalogApi.getEntities({ - filter: { kind: 'Template' }, - }); - return response.items as TemplateEntityV1beta2[]; - }); - - const filterGroups = useRef<{ - [filterGroupId: string]: FilterGroup; - }>({}); - const selectedFilterKeys = useRef<{ - [filterGroupId: string]: Set; - }>({}); - const selectedCategories = useRef([]); - const [filterGroupStates, setFilterGroupStates] = useState<{ - [filterGroupId: string]: FilterGroupStates; - }>({}); - const [filteredEntities, setFilteredEntities] = useState< - TemplateEntityV1beta2[] - >([]); - const [availableCategories, setAvailableCategories] = useState([]); - const [isCatalogEmpty, setCatalogEmpty] = useState(false); - - useEffect(() => { - doReload(); - }, [doReload]); - - const rebuild = useCallback(() => { - setFilterGroupStates( - buildStates( - filterGroups.current, - selectedFilterKeys.current, - selectedCategories.current, - entities, - error, - ), - ); - setFilteredEntities( - buildMatchingEntities( - filterGroups.current, - selectedFilterKeys.current, - selectedCategories.current, - entities, - ), - ); - setAvailableCategories(collectCategories(entities)); - setCatalogEmpty(entities !== undefined && entities.length === 0); - }, [entities, error]); - - const register = useCallback( - ( - filterGroupId: string, - filterGroup: FilterGroup, - initialSelectedFilterIds?: string[], - ) => { - filterGroups.current[filterGroupId] = filterGroup; - selectedFilterKeys.current[filterGroupId] = new Set( - initialSelectedFilterIds ?? [], - ); - rebuild(); - }, - [rebuild], - ); - - const unregister = useCallback( - (filterGroupId: string) => { - delete filterGroups.current[filterGroupId]; - delete selectedFilterKeys.current[filterGroupId]; - rebuild(); - }, - [rebuild], - ); - - const setGroupSelectedFilters = useCallback( - (filterGroupId: string, filters: string[]) => { - selectedFilterKeys.current[filterGroupId] = new Set(filters); - rebuild(); - }, - [rebuild], - ); - - const setSelectedCategories = useCallback( - (categories: string[]) => { - selectedCategories.current = categories; - rebuild(); - }, - [rebuild], - ); - - const reload = useCallback(async () => { - await doReload(); - }, [doReload]); - - return { - register, - unregister, - setGroupSelectedFilters, - setSelectedCategories, - reload, - selectedCategories: selectedCategories.current, - loading: !error && !entities, - error, - filterGroupStates, - filteredEntities, - availableCategories, - isCatalogEmpty, - }; -} - -// Given all filter groups and what filters are actually selected, along with -// the loading state for entities, generate the state of each individual filter -function buildStates( - filterGroups: { [filterGroupId: string]: FilterGroup }, - selectedFilterKeys: { [filterGroupId: string]: Set }, - selectedCategories: string[], - entities?: TemplateEntityV1beta2[], - error?: Error, -): { [filterGroupId: string]: FilterGroupStates } { - // On error - all entries are an error state - if (error) { - return Object.fromEntries( - Object.keys(filterGroups).map(filterGroupId => [ - filterGroupId, - { type: 'error', error }, - ]), - ); - } - - // On startup - all entries are a loading state - if (!entities) { - return Object.fromEntries( - Object.keys(filterGroups).map(filterGroupId => [ - filterGroupId, - { type: 'loading' }, - ]), - ); - } - - const result: { [filterGroupId: string]: FilterGroupStates } = {}; - for (const [filterGroupId, filterGroup] of Object.entries(filterGroups)) { - const otherMatchingEntities = buildMatchingEntities( - filterGroups, - selectedFilterKeys, - selectedCategories, - entities, - filterGroupId, - ); - const groupState: FilterGroupState = { filters: {} }; - for (const [filterId, filterFn] of Object.entries(filterGroup.filters)) { - const isSelected = !!selectedFilterKeys[filterGroupId]?.has(filterId); - const matchCount = otherMatchingEntities.filter(entity => - filterFn(entity), - ).length; - groupState.filters[filterId] = { isSelected, matchCount }; - } - result[filterGroupId] = { type: 'ready', state: groupState }; - } - - return result; -} - -// Given all entites, find all possible categories and provide them in a sorted list. -function collectCategories(entities?: TemplateEntityV1beta2[]): string[] { - const categories = new Set(); - (entities || []).forEach(e => { - if (e.spec?.type) { - categories.add(e.spec.type as string); - } - }); - return Array.from(categories).sort(); -} - -// Given all filter groups and what filters are actually selected, extract all -// entities that match all those filter groups. -function buildMatchingEntities( - filterGroups: { [filterGroupId: string]: FilterGroup }, - selectedFilterKeys: { [filterGroupId: string]: Set }, - selectedCategories: string[], - entities?: TemplateEntityV1beta2[], - excludeFilterGroupId?: string, -): TemplateEntityV1beta2[] { - // Build one filter fn per filter group - const allFilters: EntityFilterFn[] = []; - for (const [filterGroupId, filterGroup] of Object.entries(filterGroups)) { - if (excludeFilterGroupId === filterGroupId) { - continue; - } - - // Pick out all of the filter functions in the group that are actually - // selected - const groupFilters: EntityFilterFn[] = []; - for (const [filterId, filterFn] of Object.entries(filterGroup.filters)) { - if (!!selectedFilterKeys[filterGroupId]?.has(filterId)) { - groupFilters.push(filterFn); - } - } - - // Need to match any of the selected filters in the group - if there is - // any at all - if (groupFilters.length) { - allFilters.push(entity => groupFilters.some(fn => fn(entity))); - } - } - - // Filter by categories, if at least one category is selected. - if (selectedCategories.length > 0) { - allFilters.push(entity => - selectedCategories.some(c => entity.spec?.type === c), - ); - } - - // All filter groups that had any checked filters need to match. Note that - // every() always returns true for an empty array. - return entities?.filter(entity => allFilters.every(fn => fn(entity))) ?? []; -} diff --git a/plugins/scaffolder/src/filter/context.ts b/plugins/scaffolder/src/filter/context.ts deleted file mode 100644 index 385d3f6eb5..0000000000 --- a/plugins/scaffolder/src/filter/context.ts +++ /dev/null @@ -1,45 +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 { TemplateEntityV1beta2 } from '@backstage/catalog-model'; -import { createContext } from 'react'; -import { FilterGroup, FilterGroupStates } from './types'; - -export type FilterGroupsContext = { - register: ( - filterGroupId: string, - filterGroup: FilterGroup, - initialSelectedFilterIds?: string[], - ) => void; - unregister: (filterGroupId: string) => void; - setGroupSelectedFilters: (filterGroupId: string, filterIds: string[]) => void; - setSelectedCategories: (categories: string[]) => void; - reload: () => Promise; - selectedCategories: string[]; - loading: boolean; - error?: Error; - filterGroupStates: { [filterGroupId: string]: FilterGroupStates }; - filteredEntities: TemplateEntityV1beta2[]; - availableCategories: string[]; - isCatalogEmpty: boolean; -}; - -/** - * The context that maintains shared state for all visible filter groups. - */ -export const filterGroupsContext = createContext< - FilterGroupsContext | undefined ->(undefined); diff --git a/plugins/scaffolder/src/filter/types.ts b/plugins/scaffolder/src/filter/types.ts deleted file mode 100644 index 284ca608f1..0000000000 --- a/plugins/scaffolder/src/filter/types.ts +++ /dev/null @@ -1,53 +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 { Entity } from '@backstage/catalog-model'; - -export type EntityFilterFn = (entity: Entity) => boolean; - -export type FilterGroup = { - filters: { - [filterId: string]: EntityFilterFn; - }; -}; - -export type FilterGroupState = { - filters: { - [filterId: string]: { - isSelected: boolean; - matchCount: number; - }; - }; -}; - -export type FilterGroupStatesReady = { - type: 'ready'; - state: FilterGroupState; -}; - -export type FilterGroupStatesError = { - type: 'error'; - error: Error; -}; - -export type FilterGroupStatesLoading = { - type: 'loading'; -}; - -export type FilterGroupStates = - | FilterGroupStatesReady - | FilterGroupStatesError - | FilterGroupStatesLoading; diff --git a/plugins/scaffolder/src/filter/useEntityFilterGroup.test.tsx b/plugins/scaffolder/src/filter/useEntityFilterGroup.test.tsx deleted file mode 100644 index 5a03ecb425..0000000000 --- a/plugins/scaffolder/src/filter/useEntityFilterGroup.test.tsx +++ /dev/null @@ -1,124 +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 { catalogApiRef } from '@backstage/plugin-catalog-react'; -import { MockStorageApi } from '@backstage/test-utils'; -import { act, renderHook } from '@testing-library/react-hooks'; -import React from 'react'; -import { EntityFilterGroupsProvider } from './EntityFilterGroupsProvider'; -import { FilterGroup, FilterGroupStatesReady } from './types'; -import { useEntityFilterGroup } from './useEntityFilterGroup'; - -import { ApiProvider, ApiRegistry } from '@backstage/core-app-api'; -import { storageApiRef } from '@backstage/core-plugin-api'; - -describe('useEntityFilterGroup', () => { - let catalogApi: jest.Mocked; - let wrapper: ({ children }: { children?: React.ReactNode }) => JSX.Element; - - beforeEach(() => { - catalogApi = { - /* eslint-disable-next-line @typescript-eslint/no-unused-vars */ - addLocation: jest.fn(_a => new Promise(() => {})), - getEntities: jest.fn(), - getOriginLocationByEntity: jest.fn(), - getLocationByEntity: jest.fn(), - getLocationById: jest.fn(), - removeLocationById: jest.fn(), - removeEntityByUid: jest.fn(), - getEntityByName: jest.fn(), - }; - const apis = ApiRegistry.with(catalogApiRef, catalogApi).with( - storageApiRef, - MockStorageApi.create(), - ); - wrapper = ({ children }: { children?: React.ReactNode }) => ( - - {children} - - ); - }); - - it('works for an empty set of filters', async () => { - catalogApi.getEntities.mockResolvedValue({ items: [] }); - const group: FilterGroup = { filters: {} }; - const { result, waitFor } = renderHook( - () => useEntityFilterGroup('g1', group), - { wrapper }, - ); - - await waitFor(() => expect(result.current.state.type).toBe('ready')); - }); - - it('works for a single group', async () => { - catalogApi.getEntities.mockResolvedValue({ - items: [ - { - apiVersion: 'backstage.io/v1alpha1', - kind: 'Component', - metadata: { name: 'n' }, - }, - ], - }); - const group: FilterGroup = { - filters: { - f1: e => e.metadata.name === 'n', - f2: e => e.metadata.name !== 'n', - }, - }; - const { result, waitFor } = renderHook( - () => useEntityFilterGroup('g1', group), - { wrapper }, - ); - - await waitFor(() => expect(result.current.state.type).toEqual('ready')); - let state = result.current.state as FilterGroupStatesReady; - expect(state.state.filters.f1).toEqual({ - isSelected: false, - matchCount: 1, - }); - expect(state.state.filters.f2).toEqual({ - isSelected: false, - matchCount: 0, - }); - - act(() => result.current.setSelectedFilters(['f1'])); - - await waitFor(() => expect(result.current.state.type).toEqual('ready')); - state = result.current.state as FilterGroupStatesReady; - expect(state.state.filters.f1).toEqual({ - isSelected: true, - matchCount: 1, - }); - expect(state.state.filters.f2).toEqual({ - isSelected: false, - matchCount: 0, - }); - - act(() => result.current.setSelectedFilters(['f2'])); - - await waitFor(() => expect(result.current.state.type).toEqual('ready')); - state = result.current.state as FilterGroupStatesReady; - expect(state.state.filters.f1).toEqual({ - isSelected: false, - matchCount: 1, - }); - expect(state.state.filters.f2).toEqual({ - isSelected: true, - matchCount: 0, - }); - }); -}); diff --git a/plugins/scaffolder/src/filter/useEntityFilterGroup.ts b/plugins/scaffolder/src/filter/useEntityFilterGroup.ts deleted file mode 100644 index 13857724cf..0000000000 --- a/plugins/scaffolder/src/filter/useEntityFilterGroup.ts +++ /dev/null @@ -1,69 +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 { useCallback, useContext, useEffect, useMemo } from 'react'; -import { filterGroupsContext } from './context'; -import { FilterGroup, FilterGroupStates } from './types'; - -export type EntityFilterGroupOutput = { - state: FilterGroupStates; - setSelectedFilters: (filterIds: string[]) => void; -}; - -/** - * Hook that exposes the relevant data and operations for a single filter - * group. - */ -export const useEntityFilterGroup = ( - filterGroupId: string, - filterGroup: FilterGroup, - initialSelectedFilters?: string[], -): EntityFilterGroupOutput => { - const context = useContext(filterGroupsContext); - if (!context) { - throw new Error(`Must be used inside an EntityFilterGroupsProvider`); - } - const { - register, - unregister, - setGroupSelectedFilters, - filterGroupStates, - } = context; - - // Intentionally consider initial set only at mount time - // eslint-disable-next-line react-hooks/exhaustive-deps - const initialMemo = useMemo(() => initialSelectedFilters?.slice(), []); - - // Register the group on mount, and unregister on unmount - useEffect(() => { - register(filterGroupId, filterGroup, initialMemo); - return () => unregister(filterGroupId); - }, [register, unregister, filterGroupId, filterGroup, initialMemo]); - - const setSelectedFilters = useCallback( - (filters: string[]) => { - setGroupSelectedFilters(filterGroupId, filters); - }, - [setGroupSelectedFilters, filterGroupId], - ); - - let state = filterGroupStates[filterGroupId]; - if (!state) { - state = { type: 'loading' }; - } - - return { state, setSelectedFilters }; -}; diff --git a/plugins/scaffolder/src/filter/useFilteredEntities.ts b/plugins/scaffolder/src/filter/useFilteredEntities.ts deleted file mode 100644 index e091316d11..0000000000 --- a/plugins/scaffolder/src/filter/useFilteredEntities.ts +++ /dev/null @@ -1,37 +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 { useContext } from 'react'; -import { filterGroupsContext } from './context'; - -/** - * Hook that exposes the result of applying a set of filter groups. - */ -export function useFilteredEntities() { - const context = useContext(filterGroupsContext); - if (!context) { - throw new Error(`Must be used inside an EntityFilterGroupsProvider`); - } - - return { - loading: context.loading, - error: context.error, - filteredEntities: context.filteredEntities, - availableCategories: context.availableCategories, - isCatalogEmpty: context.isCatalogEmpty, - reload: context.reload, - }; -} diff --git a/plugins/search-backend-node/CHANGELOG.md b/plugins/search-backend-node/CHANGELOG.md index 9973191a24..1a231c252e 100644 --- a/plugins/search-backend-node/CHANGELOG.md +++ b/plugins/search-backend-node/CHANGELOG.md @@ -1,5 +1,26 @@ # @backstage/plugin-search-backend-node +## 0.3.0 + +### Minor Changes + +- 9f3ecb555: Build search queries using the query builder in `LunrSearchEngine`. This removes + the support for specifying custom queries with the lunr query syntax, but makes + sure that inputs are properly escaped. Supporting the full lunr syntax is still + possible by setting a custom query translator. + The interface of `LunrSearchEngine.setTranslator()` is changed to support + building lunr queries. + +### Patch Changes + +- 9f3ecb555: Enhance the search results of `LunrSearchEngine` to support a more natural + search experience. This is done by allowing typos (by using fuzzy search) and + supporting typeahead search (using wildcard queries to match incomplete words). +- 4176a60e5: Change search scheduler from starting indexing in a fixed interval (for example + every 60 seconds), to wait a fixed time between index runs. + This makes sure that no second index process for the same document type is + started when the previous one is still running. + ## 0.2.2 ### Patch Changes diff --git a/plugins/search-backend-node/api-report.md b/plugins/search-backend-node/api-report.md index ccb7475573..d327dee01b 100644 --- a/plugins/search-backend-node/api-report.md +++ b/plugins/search-backend-node/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 { DocumentCollator } from '@backstage/search-common'; import { DocumentDecorator } from '@backstage/search-common'; import { IndexableDocument } from '@backstage/search-common'; @@ -14,55 +13,52 @@ import { SearchResultSet } from '@backstage/search-common'; // @public (undocumented) export class IndexBuilder { - constructor({ logger, searchEngine }: IndexBuilderOptions); - addCollator({ collator, defaultRefreshIntervalSeconds, }: RegisterCollatorParameters): void; - addDecorator({ decorator }: RegisterDecoratorParameters): void; - build(): Promise<{ - scheduler: Scheduler; - }>; - // (undocumented) - getSearchEngine(): SearchEngine; - } + constructor({ logger, searchEngine }: IndexBuilderOptions); + addCollator({ + collator, + defaultRefreshIntervalSeconds, + }: RegisterCollatorParameters): void; + addDecorator({ decorator }: RegisterDecoratorParameters): void; + build(): Promise<{ + scheduler: Scheduler; + }>; + // (undocumented) + getSearchEngine(): SearchEngine; +} // @public (undocumented) export class LunrSearchEngine implements SearchEngine { - constructor({ logger }: { - logger: Logger_2; - }); - // (undocumented) - protected docStore: Record; - // (undocumented) - index(type: string, documents: IndexableDocument[]): void; - // (undocumented) - protected logger: Logger_2; - // (undocumented) - protected lunrIndices: Record; - // (undocumented) - query(query: SearchQuery): Promise; - // (undocumented) - setTranslator(translator: LunrQueryTranslator): void; - // (undocumented) - protected translator: QueryTranslator; + constructor({ logger }: { logger: Logger_2 }); + // (undocumented) + protected docStore: Record; + // (undocumented) + index(type: string, documents: IndexableDocument[]): Promise; + // (undocumented) + protected logger: Logger_2; + // (undocumented) + protected lunrIndices: Record; + // (undocumented) + query(query: SearchQuery): Promise; + // (undocumented) + setTranslator(translator: LunrQueryTranslator): void; + // (undocumented) + protected translator: QueryTranslator; } // @public export class Scheduler { - constructor({ logger }: { - logger: Logger_2; - }); - addToSchedule(task: Function, interval: number): void; - start(): void; - stop(): void; + constructor({ logger }: { logger: Logger_2 }); + addToSchedule(task: Function, interval: number): void; + start(): void; + stop(): void; } // @public export interface SearchEngine { - index(type: string, documents: IndexableDocument[]): void; - query(query: SearchQuery): Promise; - setTranslator(translator: QueryTranslator): void; + index(type: string, documents: IndexableDocument[]): Promise; + query(query: SearchQuery): Promise; + setTranslator(translator: QueryTranslator): void; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/search-backend-node/package.json b/plugins/search-backend-node/package.json index c8a8eef143..f889c10188 100644 --- a/plugins/search-backend-node/package.json +++ b/plugins/search-backend-node/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend-node", - "version": "0.2.2", + "version": "0.3.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -25,7 +25,7 @@ "@types/lunr": "^2.3.3" }, "devDependencies": { - "@backstage/backend-common": "^0.8.3", + "@backstage/backend-common": "^0.8.5", "@backstage/cli": "^0.7.2" }, "files": [ diff --git a/plugins/search-backend-node/src/IndexBuilder.ts b/plugins/search-backend-node/src/IndexBuilder.ts index 0cabb61f38..9bbbd73067 100644 --- a/plugins/search-backend-node/src/IndexBuilder.ts +++ b/plugins/search-backend-node/src/IndexBuilder.ts @@ -140,7 +140,7 @@ export class IndexBuilder { } // pushing documents to index to a configured search engine. - this.searchEngine.index(type, documents); + await this.searchEngine.index(type, documents); }, this.collators[type].refreshInterval * 1000); }); diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts index 22c775da8e..3d59ba4b6f 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.test.ts @@ -15,8 +15,9 @@ */ import { getVoidLogger } from '@backstage/backend-common'; -import { LunrSearchEngine } from './LunrSearchEngine'; +import lunr from 'lunr'; import { SearchEngine } from '../types'; +import { ConcreteLunrQuery, LunrSearchEngine } from './LunrSearchEngine'; /** * Just used to test the default translator shipped with LunrSearchEngine. @@ -44,7 +45,7 @@ describe('LunrSearchEngine', () => { testLunrSearchEngine.setTranslator(translatorSpy); // When: querying the search engine - testLunrSearchEngine.query({ + await testLunrSearchEngine.query({ term: 'testTerm', filters: {}, pageCursor: '', @@ -68,11 +69,35 @@ describe('LunrSearchEngine', () => { term: 'testTerm', filters: {}, pageCursor: '', - }); + }) as ConcreteLunrQuery; expect(actualTranslatedQuery).toMatchObject({ - documentTypes: ['*'], - lunrQueryString: '+testTerm', + documentTypes: undefined, + lunrQueryBuilder: expect.any(Function), + }); + + const query: jest.Mocked = { + allFields: [], + clauses: [], + term: jest.fn(), + clause: jest.fn(), + }; + + actualTranslatedQuery.lunrQueryBuilder.bind(query)(query); + + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 100, + usePipeline: true, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 10, + usePipeline: false, + wildcard: lunr.Query.wildcard.TRAILING, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 1, + usePipeline: false, + editDistance: 2, }); }); @@ -86,11 +111,39 @@ describe('LunrSearchEngine', () => { term: 'testTerm', filters: { kind: 'testKind' }, pageCursor: '', - }); + }) as ConcreteLunrQuery; expect(actualTranslatedQuery).toMatchObject({ - documentTypes: ['*'], - lunrQueryString: '+testTerm +kind:testKind', + documentTypes: undefined, + lunrQueryBuilder: expect.any(Function), + }); + + const query: jest.Mocked = { + allFields: ['kind'], + clauses: [], + term: jest.fn(), + clause: jest.fn(), + }; + + actualTranslatedQuery.lunrQueryBuilder.bind(query)(query); + + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 100, + usePipeline: true, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 10, + usePipeline: false, + wildcard: lunr.Query.wildcard.TRAILING, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 1, + usePipeline: false, + editDistance: 2, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testKind'), { + fields: ['kind'], + presence: lunr.Query.presence.REQUIRED, }); }); @@ -104,17 +157,78 @@ describe('LunrSearchEngine', () => { term: 'testTerm', filters: { kind: 'testKind', namespace: 'testNameSpace' }, pageCursor: '', - }); + }) as ConcreteLunrQuery; expect(actualTranslatedQuery).toMatchObject({ - documentTypes: ['*'], - lunrQueryString: '+testTerm +kind:testKind +namespace:testNameSpace', + documentTypes: undefined, + lunrQueryBuilder: expect.any(Function), }); + + const query: jest.Mocked = { + allFields: ['kind', 'namespace'], + clauses: [], + term: jest.fn(), + clause: jest.fn(), + }; + + actualTranslatedQuery.lunrQueryBuilder.bind(query)(query); + + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 100, + usePipeline: true, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 10, + usePipeline: false, + wildcard: lunr.Query.wildcard.TRAILING, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testTerm'), { + boost: 1, + usePipeline: false, + editDistance: 2, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testKind'), { + fields: ['kind'], + presence: lunr.Query.presence.REQUIRED, + }); + expect(query.term).toBeCalledWith(lunr.tokenizer('testNameSpace'), { + fields: ['namespace'], + presence: lunr.Query.presence.REQUIRED, + }); + }); + + it('should throw if translated query references missing field', async () => { + const inspectableSearchEngine = new LunrSearchEngineForTranslatorTests({ + logger: getVoidLogger(), + }); + const translatorUnderTest = inspectableSearchEngine.getTranslator(); + + const actualTranslatedQuery = translatorUnderTest({ + term: 'testTerm', + filters: { kind: 'testKind' }, + pageCursor: '', + }) as ConcreteLunrQuery; + + expect(actualTranslatedQuery).toMatchObject({ + documentTypes: undefined, + lunrQueryBuilder: expect.any(Function), + }); + + const query: jest.Mocked = { + allFields: [], + clauses: [], + term: jest.fn(), + clause: jest.fn(), + }; + + expect(() => + actualTranslatedQuery.lunrQueryBuilder.bind(query)(query), + ).toThrow(); }); }); describe('query', () => { - it('should perform search query', async () => { + it('should perform search query and return 0 results on empty index', async () => { const querySpy = jest.spyOn(testLunrSearchEngine, 'query'); // Perform search query and ensure the query func was invoked. @@ -145,11 +259,11 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 1 document - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ - term: 'testTerm', + term: 'unknown', filters: {}, pageCursor: '', }); @@ -158,6 +272,39 @@ describe('LunrSearchEngine', () => { expect(mockedSearchResult).toMatchObject({ results: [] }); }); + it('should perform search query and return all results on empty term', async () => { + const mockDocuments = [ + { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + ]; + + // Mock indexing of 1 document + await testLunrSearchEngine.index('test-index', mockDocuments); + + // Perform search query + const mockedSearchResult = await testLunrSearchEngine.query({ + term: '', + filters: {}, + pageCursor: '', + }); + + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + type: 'test-index', + }, + ], + }); + }); + it('should perform search query and return search results on match', async () => { const mockDocuments = [ { @@ -168,7 +315,7 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 1 document - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ @@ -177,6 +324,70 @@ describe('LunrSearchEngine', () => { pageCursor: '', }); + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + }, + ], + }); + }); + + it('should perform search query and return search results on partial match', async () => { + const mockDocuments = [ + { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + ]; + + // Mock indexing of 1 document + await testLunrSearchEngine.index('test-index', mockDocuments); + + // Perform search query + const mockedSearchResult = await testLunrSearchEngine.query({ + term: 'testTitle', + filters: {}, + pageCursor: '', + }); + + expect(mockedSearchResult).toMatchObject({ + results: [ + { + document: { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + }, + ], + }); + }); + + it('should perform search query and return search results on fuzzy match', async () => { + const mockDocuments = [ + { + title: 'testTitle', + text: 'testText', + location: 'test/location', + }, + ]; + + // Mock indexing of 1 document + await testLunrSearchEngine.index('test-index', mockDocuments); + + // Perform search query + const mockedSearchResult = await testLunrSearchEngine.query({ + term: 'testTitel', // Intentional typo + filters: {}, + pageCursor: '', + }); + // Should return 1 result as we are mocking the indexing of 1 document with match on the title field expect(mockedSearchResult).toMatchObject({ results: [ @@ -201,7 +412,7 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 1 document - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ @@ -234,7 +445,7 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 1 document - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ @@ -272,7 +483,7 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 2 documents - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ @@ -316,8 +527,8 @@ describe('LunrSearchEngine', () => { ]; // Mock 2 indices with 1 document each - testLunrSearchEngine.index('test-index', mockDocuments); - testLunrSearchEngine.index('test-index-2', mockDocuments2); + await testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index-2', mockDocuments2); // Perform search query scoped to "test-index-2" with a filter on the field "extraField" const mockedSearchResult = await testLunrSearchEngine.query({ term: 'testTitle', @@ -354,7 +565,7 @@ describe('LunrSearchEngine', () => { ]; // Mock indexing of 2 documents - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); // Perform search query const mockedSearchResult = await testLunrSearchEngine.query({ @@ -407,8 +618,8 @@ describe('LunrSearchEngine', () => { ]; // Mock 2 indices with 2 documents each - testLunrSearchEngine.index('test-index', mockDocuments); - testLunrSearchEngine.index('test-index-2', mockDocuments2); + await testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index-2', mockDocuments2); // Perform search query scoped to "test-index-2" const mockedSearchResult = await testLunrSearchEngine.query({ @@ -450,7 +661,7 @@ describe('LunrSearchEngine', () => { ]; // call index func and ensure the index func was invoked. - testLunrSearchEngine.index('test-index', mockDocuments); + await testLunrSearchEngine.index('test-index', mockDocuments); expect(indexSpy).toHaveBeenCalled(); expect(indexSpy).toHaveBeenCalledWith('test-index', [ { title: 'testTerm', text: 'testText', location: 'test/location' }, diff --git a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts index a1c12f019d..e832e49f85 100644 --- a/plugins/search-backend-node/src/engines/LunrSearchEngine.ts +++ b/plugins/search-backend-node/src/engines/LunrSearchEngine.ts @@ -15,17 +15,17 @@ */ import { - SearchQuery, IndexableDocument, + SearchQuery, SearchResultSet, } from '@backstage/search-common'; import lunr from 'lunr'; import { Logger } from 'winston'; -import { SearchEngine, QueryTranslator } from '../types'; +import { QueryTranslator, SearchEngine } from '../types'; -type ConcreteLunrQuery = { - lunrQueryString: string; - documentTypes: string[]; +export type ConcreteLunrQuery = { + lunrQueryBuilder: lunr.Index.QueryBuilder; + documentTypes?: string[]; }; type LunrResultEnvelope = { @@ -50,40 +50,62 @@ export class LunrSearchEngine implements SearchEngine { filters, types, }: SearchQuery): ConcreteLunrQuery => { - let lunrQueryFilters; - const lunrTerm = term ? `+${term}` : ''; - if (filters) { - lunrQueryFilters = Object.entries(filters) - .map(([field, value]) => { - // Require that the given field has the given value (with +). - if (['string', 'number', 'boolean'].includes(typeof value)) { - if (typeof value === 'string') { - return ` +${field}:${value.replace(':', '\\:')}`; - } - return ` +${field}:${value}`; - } - - // Illustrate how multi-value filters could work. - if (Array.isArray(value)) { - // But warn that Lurn supports this poorly. - this.logger.warn( - `Non-scalar filter value used for field ${field}. Consider using a different Search Engine for better results.`, - ); - return ` ${value.map(v => { - return `${field}:${v}`; - })}`; - } - - // Log a warning or something about unknown filter value - this.logger.warn(`Unknown filter type used on field ${field}`); - return ''; - }) - .join(''); - } - return { - lunrQueryString: `${lunrTerm}${lunrQueryFilters || ''}`, - documentTypes: types || ['*'], + lunrQueryBuilder: q => { + const termToken = lunr.tokenizer(term); + + // Support for typeahead seach is based on https://github.com/olivernn/lunr.js/issues/256#issuecomment-295407852 + // look for an exact match and apply a large positive boost + q.term(termToken, { + usePipeline: true, + boost: 100, + }); + // look for terms that match the beginning of this term and apply a + // medium boost + q.term(termToken, { + usePipeline: false, + boost: 10, + wildcard: lunr.Query.wildcard.TRAILING, + }); + // look for terms that match with an edit distance of 2 and apply a + // small boost + q.term(termToken, { + usePipeline: false, + editDistance: 2, + boost: 1, + }); + + if (filters) { + Object.entries(filters).forEach(([field, value]) => { + if (!q.allFields.includes(field)) { + // Throw for unknown field, as this will be a non match + throw new Error(`unrecognised field ${field}`); + } + + // Require that the given field has the given value + if (['string', 'number', 'boolean'].includes(typeof value)) { + q.term(lunr.tokenizer(value?.toString()), { + presence: lunr.Query.presence.REQUIRED, + fields: [field], + }); + } else if (Array.isArray(value)) { + // Illustrate how multi-value filters could work. + // But warn that Lurn supports this poorly. + this.logger.warn( + `Non-scalar filter value used for field ${field}. Consider using a different Search Engine for better results.`, + ); + q.term(lunr.tokenizer(value), { + presence: lunr.Query.presence.OPTIONAL, + fields: [field], + }); + } else { + // Log a warning or something about unknown filter value + this.logger.warn(`Unknown filter type used on field ${field}`); + } + }); + } + }, + documentTypes: types, }; }; @@ -91,7 +113,7 @@ export class LunrSearchEngine implements SearchEngine { this.translator = translator; } - index(type: string, documents: IndexableDocument[]): void { + async index(type: string, documents: IndexableDocument[]): Promise { const lunrBuilder = new lunr.Builder(); lunrBuilder.pipeline.add(lunr.trimmer, lunr.stopWordFilter, lunr.stemmer); @@ -117,19 +139,20 @@ export class LunrSearchEngine implements SearchEngine { this.lunrIndices[type] = lunrBuilder.build(); } - query(query: SearchQuery): Promise { - const { lunrQueryString, documentTypes } = this.translator( + async query(query: SearchQuery): Promise { + const { lunrQueryBuilder, documentTypes } = this.translator( query, ) as ConcreteLunrQuery; const results: LunrResultEnvelope[] = []; - if (documentTypes.length === 1 && documentTypes[0] === '*') { - // Iterate over all this.lunrIndex values. - Object.keys(this.lunrIndices).forEach(type => { + // Iterate over the filtered list of this.lunrIndex keys. + Object.keys(this.lunrIndices) + .filter(type => !documentTypes || documentTypes.includes(type)) + .forEach(type => { try { results.push( - ...this.lunrIndices[type].search(lunrQueryString).map(result => { + ...this.lunrIndices[type].query(lunrQueryBuilder).map(result => { return { result: result, type: type, @@ -139,36 +162,14 @@ export class LunrSearchEngine implements SearchEngine { } catch (err) { // if a field does not exist on a index, we can see that as a no-match if ( - err instanceof lunr.QueryParseError && + err instanceof Error && err.message.startsWith('unrecognised field') - ) + ) { return; + } + throw err; } }); - } else { - // Iterate over the filtered list of this.lunrIndex keys. - Object.keys(this.lunrIndices) - .filter(type => documentTypes.includes(type)) - .forEach(type => { - try { - results.push( - ...this.lunrIndices[type].search(lunrQueryString).map(result => { - return { - result: result, - type: type, - }; - }), - ); - } catch (err) { - // if a field does not exist on a index, we can see that as a no-match - if ( - err instanceof lunr.QueryParseError && - err.message.startsWith('unrecognised field') - ) - return; - } - }); - } // Sort results. results.sort((doc1, doc2) => { @@ -182,6 +183,6 @@ export class LunrSearchEngine implements SearchEngine { }), }; - return Promise.resolve(realResultSet); + return realResultSet; } } diff --git a/plugins/search-backend-node/src/engines/index.ts b/plugins/search-backend-node/src/engines/index.ts index 16516bf66e..7e6fb86bd4 100644 --- a/plugins/search-backend-node/src/engines/index.ts +++ b/plugins/search-backend-node/src/engines/index.ts @@ -15,3 +15,4 @@ */ export { LunrSearchEngine } from './LunrSearchEngine'; +export type { ConcreteLunrQuery } from './LunrSearchEngine'; diff --git a/plugins/search-backend-node/src/types.ts b/plugins/search-backend-node/src/types.ts index a3b828fb58..28dc1237ba 100644 --- a/plugins/search-backend-node/src/types.ts +++ b/plugins/search-backend-node/src/types.ts @@ -67,7 +67,7 @@ export interface SearchEngine { /** * Add the given documents to the SearchEngine index of the given type. */ - index(type: string, documents: IndexableDocument[]): void; + index(type: string, documents: IndexableDocument[]): Promise; /** * Perform a search query against the SearchEngine. diff --git a/plugins/search-backend/CHANGELOG.md b/plugins/search-backend/CHANGELOG.md index 47188017dc..678bcc8169 100644 --- a/plugins/search-backend/CHANGELOG.md +++ b/plugins/search-backend/CHANGELOG.md @@ -1,5 +1,13 @@ # @backstage/plugin-search-backend +## 0.2.2 + +### Patch Changes + +- Updated dependencies + - @backstage/backend-common@0.8.5 + - @backstage/plugin-search-backend-node@0.3.0 + ## 0.2.1 ### Patch Changes diff --git a/plugins/search-backend/api-report.md b/plugins/search-backend/api-report.md index 7eba677439..b4202a1916 100644 --- a/plugins/search-backend/api-report.md +++ b/plugins/search-backend/api-report.md @@ -3,15 +3,15 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts - import express from 'express'; import { Logger as Logger_2 } from 'winston'; import { SearchEngine } from '@backstage/plugin-search-backend-node'; // @public (undocumented) -export function createRouter({ engine, logger, }: RouterOptions): Promise; - +export function createRouter({ + engine, + logger, +}: RouterOptions): Promise; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/search-backend/package.json b/plugins/search-backend/package.json index 48729e88d9..d7e163198f 100644 --- a/plugins/search-backend/package.json +++ b/plugins/search-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search-backend", - "version": "0.2.1", + "version": "0.2.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -19,9 +19,9 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", + "@backstage/backend-common": "^0.8.5", "@backstage/search-common": "^0.1.2", - "@backstage/plugin-search-backend-node": "^0.2.1", + "@backstage/plugin-search-backend-node": "^0.3.0", "@types/express": "^4.17.6", "express": "^4.17.1", "express-promise-router": "^4.1.0", diff --git a/plugins/search/CHANGELOG.md b/plugins/search/CHANGELOG.md index 3292afa2d8..18fe34c720 100644 --- a/plugins/search/CHANGELOG.md +++ b/plugins/search/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-search +## 0.4.2 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.4.1 ### Patch Changes diff --git a/plugins/search/api-report.md b/plugins/search/api-report.md index 5451ed9adb..27ecf213bb 100644 --- a/plugins/search/api-report.md +++ b/plugins/search/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 { ApiRef } from '@backstage/core-plugin-api'; import { AsyncState } from 'react-use/lib/useAsync'; @@ -17,20 +18,31 @@ import { SearchResult as SearchResult_2 } from '@backstage/search-common'; import { SearchResultSet } from '@backstage/search-common'; // @public (undocumented) -export const DefaultResultListItem: ({ result }: { - result: IndexableDocument; +export const DefaultResultListItem: ({ + result, +}: { + result: IndexableDocument; }) => JSX.Element; // @public (undocumented) -export const Filters: ({ filters, filterOptions, resetFilters, updateSelected, updateChecked, }: FiltersProps) => JSX.Element; +export const Filters: ({ + filters, + filterOptions, + resetFilters, + updateSelected, + updateChecked, +}: FiltersProps) => JSX.Element; // @public (undocumented) -export const FiltersButton: ({ numberOfSelectedFilters, handleToggleFilters, }: FiltersButtonProps) => JSX.Element; +export const FiltersButton: ({ + numberOfSelectedFilters, + handleToggleFilters, +}: FiltersButtonProps) => JSX.Element; // @public (undocumented) export type FiltersState = { - selected: string; - checked: Array; + selected: string; + checked: Array; }; // @public (undocumented) @@ -43,28 +55,34 @@ export const searchApiRef: ApiRef; export const SearchBar: ({ className, debounceTime }: Props) => JSX.Element; // @public @deprecated (undocumented) -export const SearchBarNext: ({ className, debounceTime }: { - className?: string | undefined; - debounceTime?: number | undefined; +export const SearchBarNext: ({ + className, + debounceTime, +}: { + className?: string | undefined; + debounceTime?: number | undefined; }) => JSX.Element; // @public (undocumented) -export const SearchContextProvider: ({ initialState, children, }: React_2.PropsWithChildren<{ - initialState?: SettableSearchContext | undefined; +export const SearchContextProvider: ({ + initialState, + children, +}: React_2.PropsWithChildren<{ + initialState?: SettableSearchContext | undefined; }>) => JSX.Element; // @public (undocumented) export const SearchFilter: { - ({ component: Element, ...props }: Props_2): JSX.Element; - Checkbox(props: Omit & Component): JSX.Element; - Select(props: Omit & Component): JSX.Element; + ({ component: Element, ...props }: Props_2): JSX.Element; + Checkbox(props: Omit & Component): JSX.Element; + Select(props: Omit & Component): JSX.Element; }; // @public @deprecated (undocumented) export const SearchFilterNext: { - ({ component: Element, ...props }: Props_2): JSX.Element; - Checkbox(props: Omit & Component): JSX.Element; - Select(props: Omit & Component): JSX.Element; + ({ component: Element, ...props }: Props_2): JSX.Element; + Checkbox(props: Omit & Component): JSX.Element; + Select(props: Omit & Component): JSX.Element; }; // @public (undocumented) @@ -74,20 +92,21 @@ export const SearchPage: () => JSX.Element; export const SearchPageNext: () => JSX.Element; // @public (undocumented) -const searchPlugin: BackstagePlugin<{ +const searchPlugin: BackstagePlugin< + { root: RouteRef; nextRoot: RouteRef; -}, {}>; - -export { searchPlugin as plugin } - -export { searchPlugin } + }, + {} +>; +export { searchPlugin as plugin }; +export { searchPlugin }; // @public (undocumented) -export const SearchResult: ({ children }: { - children: (results: { - results: SearchResult_2[]; - }) => JSX.Element; +export const SearchResult: ({ + children, +}: { + children: (results: { results: SearchResult_2[] }) => JSX.Element; }) => JSX.Element; // @public (undocumented) @@ -96,7 +115,5 @@ export const SidebarSearch: () => JSX.Element; // @public (undocumented) export const useSearch: () => SearchContextValue; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/search/package.json b/plugins/search/package.json index fba0b2ab3b..c40885e357 100644 --- a/plugins/search/package.json +++ b/plugins/search/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-search", - "version": "0.4.1", + "version": "0.4.2", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -29,11 +29,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/search-common": "^0.1.2", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -50,7 +50,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/sentry/CHANGELOG.md b/plugins/sentry/CHANGELOG.md index b51632b812..5153be2b93 100644 --- a/plugins/sentry/CHANGELOG.md +++ b/plugins/sentry/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-sentry +## 0.3.14 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.3.13 ### Patch Changes diff --git a/plugins/sentry/api-report.md b/plugins/sentry/api-report.md index c3cce9ed7c..ebb4cfa357 100644 --- a/plugins/sentry/api-report.md +++ b/plugins/sentry/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -19,26 +20,24 @@ export const EntitySentryContent: () => JSX.Element; // @public (undocumented) export class MockSentryApi implements SentryApi { - // (undocumented) - fetchIssues(): Promise; + // (undocumented) + fetchIssues(): Promise; } // @public (undocumented) export class ProductionSentryApi implements SentryApi { - constructor(discoveryApi: DiscoveryApi, organization: string); - // (undocumented) - fetchIssues(project: string, statsFor: string): Promise; - } + constructor(discoveryApi: DiscoveryApi, organization: string); + // (undocumented) + fetchIssues(project: string, statsFor: string): Promise; +} // @public (undocumented) -export const Router: ({ entity }: { - entity: Entity; -}) => JSX.Element; +export const Router: ({ entity }: { entity: Entity }) => JSX.Element; // @public (undocumented) export interface SentryApi { - // (undocumented) - fetchIssues(project: string, statsFor: string): Promise; + // (undocumented) + fetchIssues(project: string, statsFor: string): Promise; } // @public (undocumented) @@ -46,55 +45,58 @@ export const sentryApiRef: ApiRef; // @public (undocumented) export type SentryIssue = { - platform: SentryPlatform; - lastSeen: string; - numComments: number; - userCount: number; - stats: { - '24h'?: EventPoint[]; - '12h'?: EventPoint[]; - }; - culprit: string; - title: string; - id: string; - assignedTo: any; - logger: any; - type: string; - annotations: any[]; - metadata: SentryIssueMetadata; - status: string; - subscriptionDetails: any; - isPublic: boolean; - hasSeen: boolean; - shortId: string; - shareId: string | null; - firstSeen: string; - count: string; - permalink: string; - level: string; - isSubscribed: boolean; - isBookmarked: boolean; - project: SentryProject; - statusDetails: any; + platform: SentryPlatform; + lastSeen: string; + numComments: number; + userCount: number; + stats: { + '24h'?: EventPoint[]; + '12h'?: EventPoint[]; + }; + culprit: string; + title: string; + id: string; + assignedTo: any; + logger: any; + type: string; + annotations: any[]; + metadata: SentryIssueMetadata; + status: string; + subscriptionDetails: any; + isPublic: boolean; + hasSeen: boolean; + shortId: string; + shareId: string | null; + firstSeen: string; + count: string; + permalink: string; + level: string; + isSubscribed: boolean; + isBookmarked: boolean; + project: SentryProject; + statusDetails: any; }; // @public (undocumented) -export const SentryIssuesWidget: ({ entity, statsFor, variant, }: { - entity: Entity; - statsFor?: "12h" | "24h" | undefined; - variant?: InfoCardVariants | undefined; +export const SentryIssuesWidget: ({ + entity, + statsFor, + variant, +}: { + entity: Entity; + statsFor?: '12h' | '24h' | undefined; + variant?: InfoCardVariants | undefined; }) => JSX.Element; // @public (undocumented) -const sentryPlugin: BackstagePlugin<{ +const sentryPlugin: BackstagePlugin< + { root: RouteRef; -}, {}>; - -export { sentryPlugin as plugin } - -export { sentryPlugin } - + }, + {} +>; +export { sentryPlugin as plugin }; +export { sentryPlugin }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/sentry/package.json b/plugins/sentry/package.json index e577cf385d..4d670a8a7f 100644 --- a/plugins/sentry/package.json +++ b/plugins/sentry/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sentry", - "version": "0.3.13", + "version": "0.3.14", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,10 +31,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -49,7 +49,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/shortcuts/api-report.md b/plugins/shortcuts/api-report.md index b29328d2e6..7aa54b8c3b 100644 --- a/plugins/shortcuts/api-report.md +++ b/plugins/shortcuts/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -12,33 +13,33 @@ import { StorageApi } from '@backstage/core-plugin-api'; // @public export class LocalStoredShortcuts implements ShortcutApi { - constructor(storageApi: StorageApi); - // (undocumented) - add(shortcut: Omit): Promise; - // (undocumented) - getColor(url: string): string; - // (undocumented) - remove(id: string): Promise; - // (undocumented) - shortcut$(): ObservableImpl; - // (undocumented) - update(shortcut: Shortcut): Promise; + constructor(storageApi: StorageApi); + // (undocumented) + add(shortcut: Omit): Promise; + // (undocumented) + getColor(url: string): string; + // (undocumented) + remove(id: string): Promise; + // (undocumented) + shortcut$(): ObservableImpl; + // (undocumented) + update(shortcut: Shortcut): Promise; } // @public (undocumented) export type Shortcut = { - id: string; - url: string; - title: string; + id: string; + url: string; + title: string; }; // @public (undocumented) export interface ShortcutApi { - add(shortcut: Omit): Promise; - getColor(url: string): string; - remove(id: string): Promise; - shortcut$(): Observable; - update(shortcut: Shortcut): Promise; + add(shortcut: Omit): Promise; + getColor(url: string): string; + remove(id: string): Promise; + shortcut$(): Observable; + update(shortcut: Shortcut): Promise; } // @public (undocumented) @@ -50,7 +51,5 @@ export const shortcutsApiRef: ApiRef; // @public (undocumented) export const shortcutsPlugin: BackstagePlugin<{}, {}>; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/shortcuts/package.json b/plugins/shortcuts/package.json index 4a0b8f3eea..1dbbbb1a18 100644 --- a/plugins/shortcuts/package.json +++ b/plugins/shortcuts/package.json @@ -20,7 +20,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -38,7 +38,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/sonarqube/CHANGELOG.md b/plugins/sonarqube/CHANGELOG.md index 8df5372fa9..390d8e4a68 100644 --- a/plugins/sonarqube/CHANGELOG.md +++ b/plugins/sonarqube/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-sonarqube +## 0.1.21 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.1.20 ### Patch Changes diff --git a/plugins/sonarqube/api-report.md b/plugins/sonarqube/api-report.md index 46bffb0a44..92d0cfa476 100644 --- a/plugins/sonarqube/api-report.md +++ b/plugins/sonarqube/api-report.md @@ -3,39 +3,44 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +/// import { BackstagePlugin } from '@backstage/core-plugin-api'; import { Entity } from '@backstage/catalog-model'; import { InfoCardVariants } from '@backstage/core-components'; // @public (undocumented) -export const EntitySonarQubeCard: ({ variant, duplicationRatings, }: { - entity?: Entity| undefined; - variant?: InfoCardVariants| undefined; - duplicationRatings?: { +export const EntitySonarQubeCard: ({ + variant, + duplicationRatings, +}: { + entity?: Entity | undefined; + variant?: InfoCardVariants | undefined; + duplicationRatings?: + | { greaterThan: number; - rating: "1.0" | "2.0" | "3.0" | "4.0" | "5.0"; - }[] | undefined; + rating: '1.0' | '2.0' | '3.0' | '4.0' | '5.0'; + }[] + | undefined; }) => JSX.Element; // @public (undocumented) export const isSonarQubeAvailable: (entity: Entity) => boolean; // @public (undocumented) -export const SonarQubeCard: ({ variant, duplicationRatings, }: { - entity?: Entity | undefined; - variant?: InfoCardVariants | undefined; - duplicationRatings?: DuplicationRating[] | undefined; +export const SonarQubeCard: ({ + variant, + duplicationRatings, +}: { + entity?: Entity | undefined; + variant?: InfoCardVariants | undefined; + duplicationRatings?: DuplicationRating[] | undefined; }) => JSX.Element; // @public (undocumented) const sonarQubePlugin: BackstagePlugin<{}, {}>; - -export { sonarQubePlugin as plugin } - -export { sonarQubePlugin } - +export { sonarQubePlugin as plugin }; +export { sonarQubePlugin }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/sonarqube/package.json b/plugins/sonarqube/package.json index c71b3e9177..3d120abf49 100644 --- a/plugins/sonarqube/package.json +++ b/plugins/sonarqube/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-sonarqube", - "version": "0.1.20", + "version": "0.1.21", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -32,10 +32,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -50,7 +50,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/splunk-on-call/CHANGELOG.md b/plugins/splunk-on-call/CHANGELOG.md index 037be680c9..a3a60c1b9d 100644 --- a/plugins/splunk-on-call/CHANGELOG.md +++ b/plugins/splunk-on-call/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-splunk-on-call +## 0.3.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.3.3 ### Patch Changes diff --git a/plugins/splunk-on-call/api-report.md b/plugins/splunk-on-call/api-report.md index 8ffe13660d..d17eb01564 100644 --- a/plugins/splunk-on-call/api-report.md +++ b/plugins/splunk-on-call/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -22,47 +23,55 @@ export const splunkOnCallApiRef: ApiRef; // @public (undocumented) export class SplunkOnCallClient implements SplunkOnCallApi { - constructor(config: ClientApiConfig); - // (undocumented) - static fromConfig(configApi: ConfigApi, discoveryApi: DiscoveryApi): SplunkOnCallClient; - // (undocumented) - getEscalationPolicies(): Promise; - // (undocumented) - getIncidents(): Promise; - // (undocumented) - getOnCallUsers(): Promise; - // (undocumented) - getTeams(): Promise; - // (undocumented) - getUsers(): Promise; - // (undocumented) - incidentAction({ routingKey, incidentType, incidentId, incidentDisplayName, incidentMessage, incidentStartTime, }: TriggerAlarmRequest): Promise; - } + constructor(config: ClientApiConfig); + // (undocumented) + static fromConfig( + configApi: ConfigApi, + discoveryApi: DiscoveryApi, + ): SplunkOnCallClient; + // (undocumented) + getEscalationPolicies(): Promise; + // (undocumented) + getIncidents(): Promise; + // (undocumented) + getOnCallUsers(): Promise; + // (undocumented) + getTeams(): Promise; + // (undocumented) + getUsers(): Promise; + // (undocumented) + incidentAction({ + routingKey, + incidentType, + incidentId, + incidentDisplayName, + incidentMessage, + incidentStartTime, + }: TriggerAlarmRequest): Promise; +} // @public (undocumented) export const SplunkOnCallPage: { - ({ title, subtitle, pageTitle, }: SplunkOnCallPageProps): JSX.Element; - defaultProps: { - title: string; - subtitle: string; - pageTitle: string; - }; + ({ title, subtitle, pageTitle }: SplunkOnCallPageProps): JSX.Element; + defaultProps: { + title: string; + subtitle: string; + pageTitle: string; + }; }; // @public (undocumented) -const splunkOnCallPlugin: BackstagePlugin<{ +const splunkOnCallPlugin: BackstagePlugin< + { root: RouteRef; -}, {}>; - -export { splunkOnCallPlugin as plugin } - -export { splunkOnCallPlugin } + }, + {} +>; +export { splunkOnCallPlugin as plugin }; +export { splunkOnCallPlugin }; // @public (undocumented) -export class UnauthorizedError extends Error { -} - +export class UnauthorizedError extends Error {} // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/splunk-on-call/package.json b/plugins/splunk-on-call/package.json index 6fbbba61f3..b553c4510d 100644 --- a/plugins/splunk-on-call/package.json +++ b/plugins/splunk-on-call/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-splunk-on-call", - "version": "0.3.3", + "version": "0.3.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,10 +30,10 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -48,7 +48,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/tech-radar/api-report.md b/plugins/tech-radar/api-report.md index 8b98f1e494..4087e0d196 100644 --- a/plugins/tech-radar/api-report.md +++ b/plugins/tech-radar/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -10,115 +11,116 @@ import { RouteRef } from '@backstage/core-plugin-api'; // @public (undocumented) export interface RadarEntry { - // (undocumented) - description?: string; - // (undocumented) - id: string; - // (undocumented) - key: string; - // (undocumented) - quadrant: string; - // (undocumented) - timeline: Array; - // (undocumented) - title: string; - // (undocumented) - url: string; + // (undocumented) + description?: string; + // (undocumented) + id: string; + // (undocumented) + key: string; + // (undocumented) + quadrant: string; + // (undocumented) + timeline: Array; + // (undocumented) + title: string; + // (undocumented) + url: string; } // @public (undocumented) export interface RadarEntrySnapshot { - // (undocumented) - date: Date; - // (undocumented) - description?: string; - // (undocumented) - moved?: MovedState; - // (undocumented) - ringId: string; + // (undocumented) + date: Date; + // (undocumented) + description?: string; + // (undocumented) + moved?: MovedState; + // (undocumented) + ringId: string; } // @public (undocumented) export interface RadarQuadrant { - // (undocumented) - id: string; - // (undocumented) - name: string; + // (undocumented) + id: string; + // (undocumented) + name: string; } // @public (undocumented) export interface RadarRing { - // (undocumented) - color: string; - // (undocumented) - id: string; - // (undocumented) - name: string; + // (undocumented) + color: string; + // (undocumented) + id: string; + // (undocumented) + name: string; } // @public (undocumented) export const Router: { - ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; - defaultProps: { - title: string; - subtitle: string; - pageTitle: string; - }; + ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; + defaultProps: { + title: string; + subtitle: string; + pageTitle: string; + }; }; // @public (undocumented) export interface TechRadarApi { - load: (id: string | undefined) => Promise; + load: (id: string | undefined) => Promise; } // @public (undocumented) export const techRadarApiRef: ApiRef; // @public (undocumented) -export const TechRadarComponent: (props: TechRadarComponentProps) => JSX.Element; +export const TechRadarComponent: ( + props: TechRadarComponentProps, +) => JSX.Element; // @public (undocumented) export interface TechRadarComponentProps { - // (undocumented) - height: number; - // (undocumented) - id?: string; - // (undocumented) - svgProps?: object; - // (undocumented) - width: number; + // (undocumented) + height: number; + // (undocumented) + id?: string; + // (undocumented) + svgProps?: object; + // (undocumented) + width: number; } // @public (undocumented) export interface TechRadarLoaderResponse { - // (undocumented) - entries: RadarEntry[]; - // (undocumented) - quadrants: RadarQuadrant[]; - // (undocumented) - rings: RadarRing[]; + // (undocumented) + entries: RadarEntry[]; + // (undocumented) + quadrants: RadarQuadrant[]; + // (undocumented) + rings: RadarRing[]; } // @public (undocumented) export const TechRadarPage: { - ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; - defaultProps: { - title: string; - subtitle: string; - pageTitle: string; - }; + ({ title, subtitle, pageTitle, ...props }: TechRadarPageProps): JSX.Element; + defaultProps: { + title: string; + subtitle: string; + pageTitle: string; + }; }; // @public (undocumented) -const techRadarPlugin: BackstagePlugin<{ +const techRadarPlugin: BackstagePlugin< + { root: RouteRef; -}, {}>; - -export { techRadarPlugin as plugin } - -export { techRadarPlugin } - + }, + {} +>; +export { techRadarPlugin as plugin }; +export { techRadarPlugin }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/tech-radar/package.json b/plugins/tech-radar/package.json index 3ad8d3020f..b6e9ccacc8 100644 --- a/plugins/tech-radar/package.json +++ b/plugins/tech-radar/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -46,7 +46,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/techdocs-backend/CHANGELOG.md b/plugins/techdocs-backend/CHANGELOG.md index 5f63c1b31d..c6dc26cb63 100644 --- a/plugins/techdocs-backend/CHANGELOG.md +++ b/plugins/techdocs-backend/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-techdocs-backend +## 0.8.6 + +### Patch Changes + +- Updated dependencies + - @backstage/catalog-model@0.9.0 + - @backstage/techdocs-common@0.6.7 + - @backstage/backend-common@0.8.5 + ## 0.8.5 ### Patch Changes diff --git a/plugins/techdocs-backend/api-report.md b/plugins/techdocs-backend/api-report.md index 947439533a..5777a3fd56 100644 --- a/plugins/techdocs-backend/api-report.md +++ b/plugins/techdocs-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 { Config } from '@backstage/config'; import express from 'express'; import { GeneratorBuilder } from '@backstage/techdocs-common'; @@ -14,11 +13,9 @@ import { PreparerBuilder } from '@backstage/techdocs-common'; import { PublisherBase } from '@backstage/techdocs-common'; // @public (undocumented) -export function createRouter({ preparers, generators, publisher, config, logger, discovery, }: RouterOptions): Promise; +export function createRouter(options: RouterOptions): Promise; - -export * from "@backstage/techdocs-common"; +export * from '@backstage/techdocs-common'; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 0f0752a0c2..714f3ad442 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs-backend", - "version": "0.8.5", + "version": "0.8.6", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -30,17 +30,17 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.4", - "@backstage/catalog-model": "^0.8.4", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/techdocs-common": "^0.6.6", + "@backstage/techdocs-common": "^0.6.7", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "dockerode": "^3.2.1", "express": "^4.17.1", "express-promise-router": "^4.1.0", - "fs-extra": "^10.0.0", + "fs-extra": "9.1.0", "knex": "^0.95.1", "winston": "^3.2.1" }, diff --git a/plugins/techdocs-backend/src/service/router.ts b/plugins/techdocs-backend/src/service/router.ts index eceae2e3d3..7563dfa2f9 100644 --- a/plugins/techdocs-backend/src/service/router.ts +++ b/plugins/techdocs-backend/src/service/router.ts @@ -31,7 +31,11 @@ import { Logger } from 'winston'; import { DocsBuilder } from '../DocsBuilder'; import { shouldCheckForUpdate } from '../DocsBuilder/BuildMetadataStorage'; -type RouterOptions = { +/** + * All of the required dependencies for running TechDocs in the "out-of-the-box" + * deployment configuration (prepare/generate/publish all in the Backend). + */ +type OutOfTheBoxDeploymentOptions = { preparers: PreparerBuilder; generators: GeneratorBuilder; publisher: PublisherBase; @@ -41,15 +45,39 @@ type RouterOptions = { config: Config; }; -export async function createRouter({ - preparers, - generators, - publisher, - config, - logger, - discovery, -}: RouterOptions): Promise { +/** + * Required dependencies for running TechDocs in the "recommended" deployment + * configuration (prepare/generate handled externally in CI/CD). + */ +type RecommendedDeploymentOptions = { + publisher: PublisherBase; + logger: Logger; + discovery: PluginEndpointDiscovery; + config: Config; +}; + +/** + * One of the two deployment configurations must be provided. + */ +type RouterOptions = + | RecommendedDeploymentOptions + | OutOfTheBoxDeploymentOptions; + +/** + * Typeguard to help createRouter() understand when we are in a "recommended" + * deployment vs. when we are in an out-of-the-box deployment configuration. + */ +function isOutOfTheBoxOption( + opt: RouterOptions, +): opt is OutOfTheBoxDeploymentOptions { + return (opt as OutOfTheBoxDeploymentOptions).preparers !== undefined; +} + +export async function createRouter( + options: RouterOptions, +): Promise { const router = Router(); + const { publisher, config, logger, discovery } = options; router.get('/metadata/techdocs/:namespace/:kind/:name', async (req, res) => { const { kind, namespace, name } = req.params; @@ -158,58 +186,63 @@ export async function createRouter({ }); return; } - const docsBuilder = new DocsBuilder({ - preparers, - generators, - publisher, - logger, - entity, - config, - }); - let foundDocs = false; - switch (publisherType) { - case 'local': - case 'awsS3': - case 'azureBlobStorage': - case 'openStackSwift': - case 'googleGcs': { - // This block should be valid for all storage implementations. So no need to duplicate in future, - // add the publisher type in the list here. - const updated = await docsBuilder.build(); - if (!updated) { - throw new NotModifiedError(); - } + // Set up a DocsBuilder if "out-of-the-box" configuration is provided. + if (isOutOfTheBoxOption(options)) { + const { preparers, generators } = options; + const docsBuilder = new DocsBuilder({ + preparers, + generators, + publisher, + logger, + entity, + config, + }); + let foundDocs = false; + switch (publisherType) { + case 'local': + case 'awsS3': + case 'azureBlobStorage': + case 'openStackSwift': + case 'googleGcs': { + // This block should be valid for all storage implementations. So no need to duplicate in future, + // add the publisher type in the list here. + const updated = await docsBuilder.build(); - // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched - // on the user's page. If not, respond with a message asking them to check back later. - // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second - for (let attempt = 0; attempt < 5; attempt++) { - if (await publisher.hasDocsBeenGenerated(entity)) { - foundDocs = true; - break; + if (!updated) { + throw new NotModifiedError(); } - await new Promise(r => setTimeout(r, 1000)); + + // With a maximum of ~5 seconds wait, check if the files got published and if docs will be fetched + // on the user's page. If not, respond with a message asking them to check back later. + // The delay here is to make sure GCS/AWS/etc. registers newly uploaded files which is usually <1 second + for (let attempt = 0; attempt < 5; attempt++) { + if (await publisher.hasDocsBeenGenerated(entity)) { + foundDocs = true; + break; + } + await new Promise(r => setTimeout(r, 1000)); + } + if (!foundDocs) { + logger.error( + 'Published files are taking longer to show up in storage. Something went wrong.', + ); + throw new NotFoundError( + 'Sorry! It took too long for the generated docs to show up in storage. Check back later.', + ); + } + + res + .status(201) + .json({ message: 'Docs updated or did not need updating' }); + break; } - if (!foundDocs) { - logger.error( - 'Published files are taking longer to show up in storage. Something went wrong.', - ); + + default: throw new NotFoundError( - 'Sorry! It took too long for the generated docs to show up in storage. Check back later.', + `Publisher type ${publisherType} is not supported by techdocs-backend docs builder.`, ); - } - - res - .status(201) - .json({ message: 'Docs updated or did not need updating' }); - break; } - - default: - throw new NotFoundError( - `Publisher type ${publisherType} is not supported by techdocs-backend docs builder.`, - ); } }); diff --git a/plugins/techdocs/CHANGELOG.md b/plugins/techdocs/CHANGELOG.md index 9e93c3a251..d0419ffd58 100644 --- a/plugins/techdocs/CHANGELOG.md +++ b/plugins/techdocs/CHANGELOG.md @@ -1,5 +1,16 @@ # @backstage/plugin-techdocs +## 0.9.9 + +### Patch Changes + +- 0172d3424: Fixed bug preventing scroll bar from showing up on code blocks in a TechDocs site. +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.9.8 ### Patch Changes diff --git a/plugins/techdocs/api-report.md b/plugins/techdocs/api-report.md index dbd5e3ab26..943831d91c 100644 --- a/plugins/techdocs/api-report.md +++ b/plugins/techdocs/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -16,14 +17,19 @@ import { Location as Location_2 } from '@backstage/catalog-model'; import { RouteRef } from '@backstage/core-plugin-api'; // @public (undocumented) -export const DocsCardGrid: ({ entities, }: { - entities: Entity[] | undefined; +export const DocsCardGrid: ({ + entities, +}: { + entities: Entity[] | undefined; }) => JSX.Element | null; // @public (undocumented) -export const DocsTable: ({ entities, title, }: { - entities: Entity[] | undefined; - title?: string | undefined; +export const DocsTable: ({ + entities, + title, +}: { + entities: Entity[] | undefined; + title?: string | undefined; }) => JSX.Element | null; // @public (undocumented) @@ -31,7 +37,7 @@ export const EmbeddedDocsRouter: (_props: Props) => JSX.Element; // @public (undocumented) export const EntityTechdocsContent: (_props: { - entity?: Entity| undefined; + entity?: Entity | undefined; }) => JSX.Element; // @public (undocumented) @@ -48,12 +54,12 @@ export type SyncResult = 'cached' | 'updated' | 'timeout'; // @public (undocumented) export interface TechDocsApi { - // (undocumented) - getApiOrigin(): Promise; - // (undocumented) - getEntityMetadata(entityId: EntityName): Promise; - // (undocumented) - getTechDocsMetadata(entityId: EntityName): Promise; + // (undocumented) + getApiOrigin(): Promise; + // (undocumented) + getEntityMetadata(entityId: EntityName): Promise; + // (undocumented) + getTechDocsMetadata(entityId: EntityName): Promise; } // @public (undocumented) @@ -61,58 +67,69 @@ export const techdocsApiRef: ApiRef; // @public export class TechDocsClient implements TechDocsApi { - constructor({ configApi, discoveryApi, identityApi, }: { - configApi: Config; - discoveryApi: DiscoveryApi; - identityApi: IdentityApi; - }); - // (undocumented) + constructor({ + configApi, + discoveryApi, + identityApi, + }: { configApi: Config; - // (undocumented) discoveryApi: DiscoveryApi; - // (undocumented) - getApiOrigin(): Promise; - getEntityMetadata(entityId: EntityName): Promise; - getTechDocsMetadata(entityId: EntityName): Promise; - // (undocumented) identityApi: IdentityApi; + }); + // (undocumented) + configApi: Config; + // (undocumented) + discoveryApi: DiscoveryApi; + // (undocumented) + getApiOrigin(): Promise; + getEntityMetadata(entityId: EntityName): Promise; + getTechDocsMetadata(entityId: EntityName): Promise; + // (undocumented) + identityApi: IdentityApi; } // @public (undocumented) -export const TechDocsCustomHome: ({ tabsConfig, }: { - tabsConfig: TabsConfig; +export const TechDocsCustomHome: ({ + tabsConfig, +}: { + tabsConfig: TabsConfig; }) => JSX.Element; // @public (undocumented) export const TechdocsPage: () => JSX.Element; // @public (undocumented) -const techdocsPlugin: BackstagePlugin<{ +const techdocsPlugin: BackstagePlugin< + { root: RouteRef; entityContent: RouteRef; -}, {}>; - -export { techdocsPlugin as plugin } - -export { techdocsPlugin } + }, + {} +>; +export { techdocsPlugin as plugin }; +export { techdocsPlugin }; // @public (undocumented) export const TechDocsReaderPage: () => JSX.Element; // @public (undocumented) export interface TechDocsStorageApi { - // (undocumented) - getApiOrigin(): Promise; - // (undocumented) - getBaseUrl(oldBaseUrl: string, entityId: EntityName, path: string): Promise; - // (undocumented) - getBuilder(): Promise; - // (undocumented) - getEntityDocs(entityId: EntityName, path: string): Promise; - // (undocumented) - getStorageUrl(): Promise; - // (undocumented) - syncEntityDocs(entityId: EntityName): Promise; + // (undocumented) + getApiOrigin(): Promise; + // (undocumented) + getBaseUrl( + oldBaseUrl: string, + entityId: EntityName, + path: string, + ): Promise; + // (undocumented) + getBuilder(): Promise; + // (undocumented) + getEntityDocs(entityId: EntityName, path: string): Promise; + // (undocumented) + getStorageUrl(): Promise; + // (undocumented) + syncEntityDocs(entityId: EntityName): Promise; } // @public (undocumented) @@ -120,30 +137,36 @@ export const techdocsStorageApiRef: ApiRef; // @public export class TechDocsStorageClient implements TechDocsStorageApi { - constructor({ configApi, discoveryApi, identityApi, }: { - configApi: Config; - discoveryApi: DiscoveryApi; - identityApi: IdentityApi; - }); - // (undocumented) + constructor({ + configApi, + discoveryApi, + identityApi, + }: { configApi: Config; - // (undocumented) discoveryApi: DiscoveryApi; - // (undocumented) - getApiOrigin(): Promise; - // (undocumented) - getBaseUrl(oldBaseUrl: string, entityId: EntityName, path: string): Promise; - // (undocumented) - getBuilder(): Promise; - getEntityDocs(entityId: EntityName, path: string): Promise; - // (undocumented) - getStorageUrl(): Promise; - // (undocumented) identityApi: IdentityApi; - syncEntityDocs(entityId: EntityName): Promise; + }); + // (undocumented) + configApi: Config; + // (undocumented) + discoveryApi: DiscoveryApi; + // (undocumented) + getApiOrigin(): Promise; + // (undocumented) + getBaseUrl( + oldBaseUrl: string, + entityId: EntityName, + path: string, + ): Promise; + // (undocumented) + getBuilder(): Promise; + getEntityDocs(entityId: EntityName, path: string): Promise; + // (undocumented) + getStorageUrl(): Promise; + // (undocumented) + identityApi: IdentityApi; + syncEntityDocs(entityId: EntityName): Promise; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 1a5abc6fbf..3b348873aa 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-techdocs", - "version": "0.9.8", + "version": "0.9.9", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -31,14 +31,14 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.7", + "@backstage/integration": "^0.5.8", "@backstage/integration-react": "^0.1.4", - "@backstage/plugin-catalog-react": "^0.2.5", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -54,7 +54,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index f80cf6cc4c..294f81378f 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -184,6 +184,17 @@ export const Reader = ({ entityId, onReady }: Props) => { } `, }), + injectCss({ + // Properly style code blocks. + css: ` + .md-typeset pre > code::-webkit-scrollbar-thumb { + background-color: hsla(0, 0%, 0%, 0.32); + } + .md-typeset pre > code::-webkit-scrollbar-thumb:hover { + background-color: hsla(0, 0%, 0%, 0.87); + } + `, + }), injectCss({ // Admonitions and others are using SVG masks to define icons. These // masks are defined as CSS variables. diff --git a/plugins/todo-backend/CHANGELOG.md b/plugins/todo-backend/CHANGELOG.md index a295ad777c..cc69ae6c3d 100644 --- a/plugins/todo-backend/CHANGELOG.md +++ b/plugins/todo-backend/CHANGELOG.md @@ -1,5 +1,15 @@ # @backstage/plugin-todo-backend +## 0.1.8 + +### Patch Changes + +- Updated dependencies + - @backstage/integration@0.5.8 + - @backstage/catalog-model@0.9.0 + - @backstage/backend-common@0.8.5 + - @backstage/catalog-client@0.3.16 + ## 0.1.7 ### Patch Changes diff --git a/plugins/todo-backend/api-report.md b/plugins/todo-backend/api-report.md index d203a52b36..92e133a481 100644 --- a/plugins/todo-backend/api-report.md +++ b/plugins/todo-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 { CatalogApi } from '@backstage/catalog-client'; import { Config } from '@backstage/config'; import { EntityName } from '@backstage/catalog-model'; @@ -20,79 +19,86 @@ export function createTodoParser(options?: TodoParserOptions): TodoParser; // @public (undocumented) export type ListTodosRequest = { - entity?: EntityName; - offset?: number; - limit?: number; - orderBy?: { - field: Fields; - direction: 'asc' | 'desc'; - }; - filters?: { - field: Fields; - value: string; - }[]; + entity?: EntityName; + offset?: number; + limit?: number; + orderBy?: { + field: Fields; + direction: 'asc' | 'desc'; + }; + filters?: { + field: Fields; + value: string; + }[]; }; // @public (undocumented) export type ListTodosResponse = { - items: TodoItem[]; - totalCount: number; - offset: number; - limit: number; + items: TodoItem[]; + totalCount: number; + offset: number; + limit: number; }; // @public (undocumented) export type ReadTodosOptions = { - url: string; + url: string; }; // @public (undocumented) export type ReadTodosResult = { - items: TodoItem[]; + items: TodoItem[]; }; // @public (undocumented) export type TodoItem = { - text: string; - tag: string; - author?: string; - viewUrl?: string; - lineNumber?: number; - repoFilePath?: string; + text: string; + tag: string; + author?: string; + viewUrl?: string; + lineNumber?: number; + repoFilePath?: string; }; // @public (undocumented) export interface TodoReader { - readTodos(options: ReadTodosOptions): Promise; + readTodos(options: ReadTodosOptions): Promise; } // @public (undocumented) export class TodoReaderService implements TodoService { - constructor(options: Options_2); - // (undocumented) - listTodos(req: ListTodosRequest, options?: { - token?: string; - }): Promise; - } + constructor(options: Options_2); + // (undocumented) + listTodos( + req: ListTodosRequest, + options?: { + token?: string; + }, + ): Promise; +} // @public (undocumented) export class TodoScmReader implements TodoReader { - constructor(options: Options); - // (undocumented) - static fromConfig(config: Config, options: Omit): TodoScmReader; - // (undocumented) - readTodos({ url }: ReadTodosOptions): Promise; + constructor(options: Options); + // (undocumented) + static fromConfig( + config: Config, + options: Omit, + ): TodoScmReader; + // (undocumented) + readTodos({ url }: ReadTodosOptions): Promise; } // @public (undocumented) export interface TodoService { - // (undocumented) - listTodos(req: ListTodosRequest, options?: { - token?: string; - }): Promise; + // (undocumented) + listTodos( + req: ListTodosRequest, + options?: { + token?: string; + }, + ): Promise; } - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/todo-backend/package.json b/plugins/todo-backend/package.json index 3e2c87a5eb..61e84f8377 100644 --- a/plugins/todo-backend/package.json +++ b/plugins/todo-backend/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-todo-backend", - "version": "0.1.7", + "version": "0.1.8", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -24,12 +24,12 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/backend-common": "^0.8.3", - "@backstage/catalog-client": "^0.3.13", - "@backstage/catalog-model": "^0.8.3", + "@backstage/backend-common": "^0.8.5", + "@backstage/catalog-client": "^0.3.16", + "@backstage/catalog-model": "^0.9.0", "@backstage/config": "^0.1.5", "@backstage/errors": "^0.1.1", - "@backstage/integration": "^0.5.6", + "@backstage/integration": "^0.5.8", "@types/express": "^4.17.6", "cross-fetch": "^3.0.6", "express": "^4.17.1", diff --git a/plugins/todo/CHANGELOG.md b/plugins/todo/CHANGELOG.md index d9b5ac52d1..e8a079880d 100644 --- a/plugins/todo/CHANGELOG.md +++ b/plugins/todo/CHANGELOG.md @@ -1,5 +1,14 @@ # @backstage/plugin-todo +## 0.1.4 + +### Patch Changes + +- Updated dependencies + - @backstage/core-components@0.1.5 + - @backstage/catalog-model@0.9.0 + - @backstage/plugin-catalog-react@0.2.6 + ## 0.1.3 ### Patch Changes diff --git a/plugins/todo/api-report.md b/plugins/todo/api-report.md index bdfea8b8f9..1fc8e8f99f 100644 --- a/plugins/todo/api-report.md +++ b/plugins/todo/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 { ApiRef } from '@backstage/core-plugin-api'; import { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -17,7 +18,5 @@ export const todoApiRef: ApiRef; // @public (undocumented) export const todoPlugin: BackstagePlugin<{}, {}>; - // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/todo/package.json b/plugins/todo/package.json index d4a18a07b9..df719ef10e 100644 --- a/plugins/todo/package.json +++ b/plugins/todo/package.json @@ -1,6 +1,6 @@ { "name": "@backstage/plugin-todo", - "version": "0.1.3", + "version": "0.1.4", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", @@ -26,11 +26,11 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/catalog-model": "^0.8.4", - "@backstage/core-components": "^0.1.4", + "@backstage/catalog-model": "^0.9.0", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/errors": "^0.1.1", - "@backstage/plugin-catalog-react": "^0.2.4", + "@backstage/plugin-catalog-react": "^0.2.6", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", "@material-ui/icons": "^4.9.1", @@ -42,7 +42,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/user-settings/api-report.md b/plugins/user-settings/api-report.md index 3c417f7a0b..2f3b9b2fed 100644 --- a/plugins/user-settings/api-report.md +++ b/plugins/user-settings/api-report.md @@ -3,21 +3,27 @@ > 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 { BackstagePlugin } from '@backstage/core-plugin-api'; import { IconComponent } from '@backstage/core-plugin-api'; +import { ProfileInfo } from '@backstage/core-plugin-api'; import { RouteRef } from '@backstage/core-plugin-api'; import { SessionApi } from '@backstage/core-plugin-api'; // @public (undocumented) -export const AuthProviders: ({ providerSettings }: Props_2) => JSX.Element; +export const DefaultProviderSettings: ({ + configuredProviders, +}: Props_3) => JSX.Element; // @public (undocumented) -export const DefaultProviderSettings: ({ configuredProviders }: Props_3) => JSX.Element; - -// @public (undocumented) -export const ProviderSettingsItem: ({ title, description, icon: Icon, apiRef, }: Props_4) => JSX.Element; +export const ProviderSettingsItem: ({ + title, + description, + icon: Icon, + apiRef, +}: Props_4) => JSX.Element; // @public (undocumented) export const Router: ({ providerSettings }: Props) => JSX.Element; @@ -26,20 +32,56 @@ export const Router: ({ providerSettings }: Props) => JSX.Element; export const Settings: () => JSX.Element; // @public (undocumented) -export const UserSettingsPage: ({ providerSettings }: { - providerSettings?: JSX.Element | undefined; +export const UserSettingsAppearanceCard: () => JSX.Element; + +// @public (undocumented) +export const UserSettingsAuthProviders: ({ + providerSettings, +}: Props_2) => JSX.Element; + +// @public (undocumented) +export const UserSettingsFeatureFlags: () => JSX.Element; + +// @public (undocumented) +export const UserSettingsGeneral: () => JSX.Element; + +// @public (undocumented) +export const UserSettingsMenu: () => JSX.Element; + +// @public (undocumented) +export const UserSettingsPage: ({ + providerSettings, +}: { + providerSettings?: JSX.Element | undefined; }) => JSX.Element; // @public (undocumented) -const userSettingsPlugin: BackstagePlugin<{ +export const UserSettingsPinToggle: () => JSX.Element; + +// @public (undocumented) +const userSettingsPlugin: BackstagePlugin< + { settingsPage: RouteRef; -}, {}>; + }, + {} +>; +export { userSettingsPlugin as plugin }; +export { userSettingsPlugin }; -export { userSettingsPlugin as plugin } +// @public (undocumented) +export const UserSettingsProfileCard: () => JSX.Element; -export { userSettingsPlugin } +// @public (undocumented) +export const UserSettingsSignInAvatar: ({ size }: Props_5) => JSX.Element; +// @public (undocumented) +export const UserSettingsThemeToggle: () => JSX.Element; + +// @public (undocumented) +export const useUserProfile: () => { + profile: ProfileInfo; + displayName: string; +}; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/user-settings/package.json b/plugins/user-settings/package.json index 7a691594a4..941116b0bb 100644 --- a/plugins/user-settings/package.json +++ b/plugins/user-settings/package.json @@ -30,7 +30,7 @@ "clean": "backstage-cli clean" }, "dependencies": { - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -45,7 +45,7 @@ "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", "@backstage/core-plugin-api": "^0.1.3", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/plugins/user-settings/src/components/AuthProviders/AuthProviders.test.tsx b/plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.test.tsx similarity index 92% rename from plugins/user-settings/src/components/AuthProviders/AuthProviders.test.tsx rename to plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.test.tsx index abb2f2f42c..085a884f2e 100644 --- a/plugins/user-settings/src/components/AuthProviders/AuthProviders.test.tsx +++ b/plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.test.tsx @@ -17,7 +17,7 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { AuthProviders } from './AuthProviders'; +import { UserSettingsAuthProviders } from './UserSettingsAuthProviders'; import { ApiProvider, @@ -52,12 +52,12 @@ const apiRegistry = ApiRegistry.from([ [googleAuthApiRef, mockGoogleAuth], ]); -describe('', () => { +describe('', () => { it('displays a provider and calls its sign-in handler on click', async () => { const rendered = await renderWithEffects( wrapInTestApp( - + , ), ); diff --git a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx b/plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.tsx similarity index 95% rename from plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx rename to plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.tsx index 1456380d55..8107944781 100644 --- a/plugins/user-settings/src/components/AuthProviders/AuthProviders.tsx +++ b/plugins/user-settings/src/components/AuthProviders/UserSettingsAuthProviders.tsx @@ -26,7 +26,7 @@ type Props = { providerSettings?: JSX.Element; }; -export const AuthProviders = ({ providerSettings }: Props) => { +export const UserSettingsAuthProviders = ({ providerSettings }: Props) => { const configApi = useApi(configApiRef); const providersConfig = configApi.getOptionalConfig('auth.providers'); const configuredProviders = providersConfig?.keys() || []; diff --git a/plugins/user-settings/src/components/AuthProviders/index.ts b/plugins/user-settings/src/components/AuthProviders/index.ts index 5221656caf..036467eb37 100644 --- a/plugins/user-settings/src/components/AuthProviders/index.ts +++ b/plugins/user-settings/src/components/AuthProviders/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { AuthProviders } from './AuthProviders'; +export { UserSettingsAuthProviders } from './UserSettingsAuthProviders'; export { DefaultProviderSettings } from './DefaultProviderSettings'; export { ProviderSettingsItem } from './ProviderSettingsItem'; diff --git a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx b/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx similarity index 97% rename from plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx rename to plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx index 2fee2157e2..5a3f077920 100644 --- a/plugins/user-settings/src/components/FeatureFlags/FeatureFlags.tsx +++ b/plugins/user-settings/src/components/FeatureFlags/UserSettingsFeatureFlags.tsx @@ -26,7 +26,7 @@ import { } from '@backstage/core-plugin-api'; import { InfoCard } from '@backstage/core-components'; -export const FeatureFlags = () => { +export const UserSettingsFeatureFlags = () => { const featureFlagsApi = useApi(featureFlagsApiRef); const featureFlags = featureFlagsApi.getRegisteredFlags(); diff --git a/plugins/user-settings/src/components/FeatureFlags/index.ts b/plugins/user-settings/src/components/FeatureFlags/index.ts index d23fc77066..fe1c091abf 100644 --- a/plugins/user-settings/src/components/FeatureFlags/index.ts +++ b/plugins/user-settings/src/components/FeatureFlags/index.ts @@ -14,4 +14,4 @@ * limitations under the License. */ -export { FeatureFlags } from './FeatureFlags'; +export { UserSettingsFeatureFlags } from './UserSettingsFeatureFlags'; diff --git a/plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.test.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx similarity index 52% rename from plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.test.tsx rename to plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index c6f2bce80c..982911821b 100644 --- a/plugins/scaffolder/src/components/SearchToolbar/SearchToolbar.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,21 +13,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import React from 'react'; -import { fireEvent, render } from '@testing-library/react'; -import SearchToolbar from './SearchToolbar'; +import { List } from '@material-ui/core'; +import { InfoCard } from '@backstage/core-components'; +import { UserSettingsPinToggle } from './UserSettingsPinToggle'; +import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; -describe('SearchToolbar', () => { - it('should display search value and execute set callback', async () => { - const setSearchSpy = jest.fn(); - const { getByDisplayValue } = render( - , - ); - - const searchInput = getByDisplayValue('hello'); - expect(searchInput).toBeInTheDocument(); - fireEvent.change(searchInput, { target: { value: 'world' } }); - expect(setSearchSpy).toHaveBeenCalled(); - }); -}); +export const UserSettingsAppearanceCard = () => ( + + + + + + +); diff --git a/plugins/user-settings/src/components/General/General.tsx b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx similarity index 65% rename from plugins/user-settings/src/components/General/General.tsx rename to plugins/user-settings/src/components/General/UserSettingsGeneral.tsx index a9a09a98f5..bd08fef565 100644 --- a/plugins/user-settings/src/components/General/General.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx @@ -13,26 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { Grid, List } from '@material-ui/core'; +import { Grid } from '@material-ui/core'; import React from 'react'; -import { PinButton } from './PinButton'; -import { Profile } from './Profile'; -import { ThemeToggle } from './ThemeToggle'; -import { InfoCard } from '@backstage/core-components'; +import { UserSettingsProfileCard } from './UserSettingsProfileCard'; +import { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; -export const General = () => { +export const UserSettingsGeneral = () => { return ( - + - - - - - - + ); diff --git a/plugins/user-settings/src/components/General/PinButton.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx similarity index 90% rename from plugins/user-settings/src/components/General/PinButton.test.tsx rename to plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index 67f2326c14..39387df907 100644 --- a/plugins/user-settings/src/components/General/PinButton.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -17,10 +17,10 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { PinButton } from './PinButton'; +import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { SidebarPinStateContext } from '@backstage/core-components'; -describe('', () => { +describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( @@ -28,7 +28,7 @@ describe('', () => { - + , ), ); diff --git a/plugins/user-settings/src/components/General/PinButton.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx similarity index 97% rename from plugins/user-settings/src/components/General/PinButton.tsx rename to plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index acf9891dec..4d71df8113 100644 --- a/plugins/user-settings/src/components/General/PinButton.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -24,7 +24,7 @@ import { } from '@material-ui/core'; import { SidebarPinStateContext } from '@backstage/core-components'; -export const PinButton = () => { +export const UserSettingsPinToggle = () => { const { isPinned, toggleSidebarPinState } = useContext( SidebarPinStateContext, ); diff --git a/plugins/user-settings/src/components/General/Profile.tsx b/plugins/user-settings/src/components/General/UserSettingsProfileCard.tsx similarity index 90% rename from plugins/user-settings/src/components/General/Profile.tsx rename to plugins/user-settings/src/components/General/UserSettingsProfileCard.tsx index 97391e8372..f79b1084c5 100644 --- a/plugins/user-settings/src/components/General/Profile.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsProfileCard.tsx @@ -15,19 +15,19 @@ */ import { Grid, Typography } from '@material-ui/core'; import React from 'react'; -import { SignInAvatar } from './SignInAvatar'; +import { UserSettingsSignInAvatar } from './UserSettingsSignInAvatar'; import { UserSettingsMenu } from './UserSettingsMenu'; import { useUserProfile } from '../useUserProfileInfo'; import { InfoCard } from '@backstage/core-components'; -export const Profile = () => { +export const UserSettingsProfileCard = () => { const { profile, displayName } = useUserProfile(); return ( - + diff --git a/plugins/user-settings/src/components/General/SignInAvatar.tsx b/plugins/user-settings/src/components/General/UserSettingsSignInAvatar.tsx similarity index 95% rename from plugins/user-settings/src/components/General/SignInAvatar.tsx rename to plugins/user-settings/src/components/General/UserSettingsSignInAvatar.tsx index 68e65fdffe..d36655a75f 100644 --- a/plugins/user-settings/src/components/General/SignInAvatar.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsSignInAvatar.tsx @@ -31,7 +31,7 @@ const useStyles = makeStyles(theme => ({ type Props = { size?: number }; -export const SignInAvatar = ({ size }: Props) => { +export const UserSettingsSignInAvatar = ({ size }: Props) => { const { iconSize } = sidebarConfig; const classes = useStyles(size ? { size } : { size: iconSize }); const { profile } = useUserProfile(); diff --git a/plugins/user-settings/src/components/General/ThemeToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.test.tsx similarity index 91% rename from plugins/user-settings/src/components/General/ThemeToggle.test.tsx rename to plugins/user-settings/src/components/General/UserSettingsThemeToggle.test.tsx index b69cd8d53e..caae4d69bc 100644 --- a/plugins/user-settings/src/components/General/ThemeToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.test.tsx @@ -19,7 +19,7 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { lightTheme } from '@backstage/theme'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { ThemeToggle } from './ThemeToggle'; +import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; import { ApiProvider, ApiRegistry, @@ -37,13 +37,13 @@ const apiRegistry = ApiRegistry.from([ [appThemeApiRef, AppThemeSelector.createWithStorage([mockTheme])], ]); -describe('', () => { +describe('', () => { it('toggles the theme select button', async () => { const themeApi = apiRegistry.get(appThemeApiRef); const rendered = await renderWithEffects( wrapInTestApp( - + , ), ); diff --git a/plugins/user-settings/src/components/General/ThemeToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx similarity index 98% rename from plugins/user-settings/src/components/General/ThemeToggle.tsx rename to plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx index fd255969a5..4261218c44 100644 --- a/plugins/user-settings/src/components/General/ThemeToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsThemeToggle.tsx @@ -87,7 +87,7 @@ const TooltipToggleButton = ({ ); -export const ThemeToggle = () => { +export const UserSettingsThemeToggle = () => { const classes = useStyles(); const appThemeApi = useApi(appThemeApiRef); const themeId = useObservable( diff --git a/plugins/user-settings/src/components/General/index.ts b/plugins/user-settings/src/components/General/index.ts index 89afe9d540..80a9c75d89 100644 --- a/plugins/user-settings/src/components/General/index.ts +++ b/plugins/user-settings/src/components/General/index.ts @@ -14,5 +14,10 @@ * limitations under the License. */ -export { General } from './General'; -export { SignInAvatar } from './SignInAvatar'; +export { UserSettingsGeneral } from './UserSettingsGeneral'; +export { UserSettingsProfileCard } from './UserSettingsProfileCard'; +export { UserSettingsMenu } from './UserSettingsMenu'; +export { UserSettingsSignInAvatar } from './UserSettingsSignInAvatar'; +export { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; +export { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; +export { UserSettingsPinToggle } from './UserSettingsPinToggle'; diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index 433f24c2c8..eebf6ff865 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -15,9 +15,9 @@ */ import React from 'react'; -import { AuthProviders } from './AuthProviders'; -import { FeatureFlags } from './FeatureFlags'; -import { General } from './General'; +import { UserSettingsAuthProviders } from './AuthProviders'; +import { UserSettingsFeatureFlags } from './FeatureFlags'; +import { UserSettingsGeneral } from './General'; import { Header, Page, TabbedLayout } from '@backstage/core-components'; type Props = { @@ -31,16 +31,16 @@ export const SettingsPage = ({ providerSettings }: Props) => { - + - + - + diff --git a/plugins/user-settings/src/components/index.ts b/plugins/user-settings/src/components/index.ts index 89e6d0efad..cb058f9c5e 100644 --- a/plugins/user-settings/src/components/index.ts +++ b/plugins/user-settings/src/components/index.ts @@ -16,3 +16,6 @@ export { Settings } from './Settings'; export { SettingsPage as Router } from './SettingsPage'; export * from './AuthProviders'; +export * from './General'; +export * from './FeatureFlags'; +export { useUserProfile } from './useUserProfileInfo'; diff --git a/plugins/welcome/api-report.md b/plugins/welcome/api-report.md index 727be3ad75..49ccd46994 100644 --- a/plugins/welcome/api-report.md +++ b/plugins/welcome/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 { BackstagePlugin } from '@backstage/core-plugin-api'; @@ -11,12 +12,8 @@ export const WelcomePage: () => JSX.Element; // @public (undocumented) const welcomePlugin: BackstagePlugin<{}, {}>; - -export { welcomePlugin as plugin } - -export { welcomePlugin } - +export { welcomePlugin as plugin }; +export { welcomePlugin }; // (No @packageDocumentation comment for this package) - ``` diff --git a/plugins/welcome/package.json b/plugins/welcome/package.json index 311498ba49..a04d3cf9dd 100644 --- a/plugins/welcome/package.json +++ b/plugins/welcome/package.json @@ -30,7 +30,7 @@ "start": "backstage-cli plugin:serve" }, "dependencies": { - "@backstage/core-components": "^0.1.4", + "@backstage/core-components": "^0.1.5", "@backstage/core-plugin-api": "^0.1.3", "@backstage/theme": "^0.2.8", "@material-ui/core": "^4.11.0", @@ -44,7 +44,7 @@ "devDependencies": { "@backstage/cli": "^0.7.3", "@backstage/core-app-api": "^0.1.4", - "@backstage/dev-utils": "^0.2.0", + "@backstage/dev-utils": "^0.2.1", "@backstage/test-utils": "^0.1.14", "@testing-library/jest-dom": "^5.10.1", "@testing-library/react": "^11.2.5", diff --git a/scripts/api-extractor.ts b/scripts/api-extractor.ts index 2631f7de93..f88b30d1fd 100644 --- a/scripts/api-extractor.ts +++ b/scripts/api-extractor.ts @@ -23,6 +23,7 @@ import { dirname, join, } from 'path'; +import prettier from 'prettier'; import fs from 'fs-extra'; import { Extractor, @@ -61,6 +62,27 @@ PackageJsonLookup.prototype.tryGetPackageJsonFilePathFor = function tryGetPackag return old.call(this, path); }; +/** + * Another monkey patch where we apply prettier to the API reports. This has to be patched into + * the middle of the process as API Extractor does a comparison of the contents of the old + * and new files during generation. This inserts the formatting just before that comparison. + */ +const { + ApiReportGenerator, +} = require('@microsoft/api-extractor/lib/generators/ApiReportGenerator'); + +const originalGenerateReviewFileContent = + ApiReportGenerator.generateReviewFileContent; +ApiReportGenerator.generateReviewFileContent = function decoratedGenerateReviewFileContent( + ...args +) { + const content = originalGenerateReviewFileContent.apply(this, args); + return prettier.format(content, { + ...require('@spotify/prettier-config'), + parser: 'markdown', + }); +}; + const PACKAGE_ROOTS = ['packages', 'plugins']; const SKIPPED_PACKAGES = [ @@ -73,14 +95,6 @@ const SKIPPED_PACKAGES = [ 'packages/e2e-test', 'packages/storybook', 'packages/techdocs-cli', - - // TODO(Rugvip): Enable these once `import * as ...` and `import()` PRs have landed, #1796 & #1916. - 'packages/core-components', - 'plugins/catalog', - 'plugins/catalog-backend', - 'plugins/catalog-react', - 'plugins/github-deployments', - 'plugins/sentry-backend', ]; async function findPackageDirs() { diff --git a/test.yaml b/test.yaml deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/yarn.lock b/yarn.lock index 4fc278e1a2..0400d0bc59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -55,11 +55,6 @@ http-errors "^1.7.3" object-path "^0.11.4" -"@ardatan/aggregate-error@0.0.1": - version "0.0.1" - resolved "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.1.tgz#1403ac5de10d8ca689fc1f65844c27179ae1d44f" - integrity sha512-UQ9BequOTIavs0pTHLMwQwKQF8tTV1oezY/H2O9chA+JNPFZSua55xpU5dPSjAU9/jLJ1VwU+HJuTVN8u7S6Fg== - "@ardatan/aggregate-error@0.0.6": version "0.0.6" resolved "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.6.tgz#fe6924771ea40fc98dc7a7045c2e872dc8527609" @@ -1291,14 +1286,7 @@ core-js-pure "^3.0.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": - version "7.14.0" - resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6" - integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.14.0": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.10.5", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.3.4", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.14.6" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d" integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg== @@ -1360,6 +1348,19 @@ "@babel/helper-validator-identifier" "^7.14.0" to-fast-properties "^2.0.0" +"@backstage/catalog-model@^0.8.4": + version "0.9.0" + dependencies: + "@backstage/config" "^0.1.5" + "@backstage/errors" "^0.1.1" + "@types/json-schema" "^7.0.5" + "@types/yup" "^0.29.8" + ajv "^7.0.3" + json-schema "^0.3.0" + lodash "^4.17.15" + uuid "^8.0.0" + yup "^0.29.3" + "@backstage/core-api@^0.2.23": version "0.2.23" resolved "https://registry.npmjs.org/@backstage/core-api/-/core-api-0.2.23.tgz#c0ec13407ff7c78d376eb18e9d8e1490d54d995b" @@ -2270,16 +2271,7 @@ unixify "1.0.0" valid-url "1.0.9" -"@graphql-tools/merge@^6.0.0": - version "6.0.15" - resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-6.0.15.tgz#09c84bd08971edfb3e1035d71432b47687820bdc" - integrity sha512-qusTLzkf6GtxS6LRQnEAWIwA1BeJj5SkZ2pnE4/wVe9gs0grpEsOKYxvGpBi8IZR7r8UeNpkdgk2HP0jlq/WWA== - dependencies: - "@graphql-tools/schema" "6.0.15" - "@graphql-tools/utils" "6.0.15" - tslib "~2.0.0" - -"@graphql-tools/merge@^6.2.12", "@graphql-tools/merge@^6.2.14": +"@graphql-tools/merge@^6.0.0", "@graphql-tools/merge@^6.2.12", "@graphql-tools/merge@^6.2.14": version "6.2.14" resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-6.2.14.tgz#694e2a2785ba47558e5665687feddd2935e9d94e" integrity sha512-RWT4Td0ROJai2eR66NHejgf8UwnXJqZxXgDWDI+7hua5vNA2OW8Mf9K1Wav1ZkjWnuRp4ztNtkZGie5ISw55ow== @@ -2331,23 +2323,7 @@ relay-compiler "10.1.0" tslib "~2.0.1" -"@graphql-tools/schema@6.0.15": - version "6.0.15" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-6.0.15.tgz#b016f9f36820342982887a291baa7e7d11b039ae" - integrity sha512-Wo+d6/OPjeXjwB1pcqsWmqLdweGH+BVhvKe/YPQA/uiWr8ikgShvNLNiuF03gc/1AMR487A09XcPEyabRKJLew== - dependencies: - "@graphql-tools/utils" "6.0.15" - tslib "~2.0.0" - -"@graphql-tools/schema@^7.0.0": - version "7.1.3" - resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.3.tgz#d816400da51fbac1f0086e35540ab63b5e30e858" - integrity sha512-ZY76hmcJlF1iyg3Im0sQ3ASRkiShjgv102vLTVcH22lEGJeCaCyyS/GF1eUHom418S60bS8Th6+autRUxfBiBg== - dependencies: - "@graphql-tools/utils" "^7.1.2" - tslib "~2.1.0" - -"@graphql-tools/schema@^7.1.5": +"@graphql-tools/schema@^7.0.0", "@graphql-tools/schema@^7.1.5": version "7.1.5" resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-7.1.5.tgz#07b24e52b182e736a6b77c829fc48b84d89aa711" integrity sha512-uyn3HSNSckf4mvQSq0Q07CPaVZMNFCYEVxroApOaw802m9DcZPgf9XVPy/gda5GWj9AhbijfRYVTZQgHnJ4CXA== @@ -2381,14 +2357,6 @@ valid-url "1.0.9" ws "7.4.5" -"@graphql-tools/utils@6.0.15": - version "6.0.15" - resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-6.0.15.tgz#6d54d383285bea3c22797531933b62a408e78e49" - integrity sha512-VG5cMLPgh9RDLGHamGpXVnBrNw7bZGT46LrxK7IIqDZI9H0GPsRCo8+p+CfDkw0IlDiEECb624WVCpm9IYNecA== - dependencies: - "@ardatan/aggregate-error" "0.0.1" - camel-case "4.1.1" - "@graphql-tools/utils@^7.0.0", "@graphql-tools/utils@^7.1.0", "@graphql-tools/utils@^7.1.2", "@graphql-tools/utils@^7.5.0", "@graphql-tools/utils@^7.7.0", "@graphql-tools/utils@^7.7.1", "@graphql-tools/utils@^7.8.1", "@graphql-tools/utils@^7.9.0", "@graphql-tools/utils@^7.9.1": version "7.10.0" resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-7.10.0.tgz#07a4cb5d1bec1ff1dc1d47a935919ee6abd38699" @@ -3428,25 +3396,7 @@ resolved "https://registry.npmjs.org/@material-icons/font/-/font-1.0.3.tgz#f722e5a69a03f20ef47d015cb69420bebeeaabe5" integrity sha512-aIRd0Z9b/HJ/O24KOaP7dNsXypMnXhpWrpLVYvQB/JesVqXYSbioYND200sR+C14a0LSCp+qWnWCnSXmN1hWGw== -"@material-ui/core@^4.11.0": - version "4.11.3" - resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.11.3.tgz#f22e41775b0bd075e36a7a093d43951bf7f63850" - integrity sha512-Adt40rGW6Uds+cAyk3pVgcErpzU/qxc7KBR94jFHBYretU4AtWZltYcNsbeMn9tXL86jjVL1kuGcIHsgLgFGRw== - dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/styles" "^4.11.3" - "@material-ui/system" "^4.11.3" - "@material-ui/types" "^5.1.0" - "@material-ui/utils" "^4.11.2" - "@types/react-transition-group" "^4.2.0" - clsx "^1.0.4" - hoist-non-react-statics "^3.3.2" - popper.js "1.16.1-lts" - prop-types "^15.7.2" - react-is "^16.8.0 || ^17.0.0" - react-transition-group "^4.4.0" - -"@material-ui/core@^4.11.3": +"@material-ui/core@^4.11.0", "@material-ui/core@^4.11.3": version "4.11.4" resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.11.4.tgz#4fb9fe5dec5dcf780b687e3a40cff78b2b9640a4" integrity sha512-oqb+lJ2Dl9HXI9orc6/aN8ZIAMkeThufA5iZELf2LQeBn2NtjVilF5D2w7e9RpntAzDb4jK5DsVhkfOvFY/8fg== @@ -3493,19 +3443,7 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" -"@material-ui/pickers@^3.2.2": - version "3.2.10" - resolved "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.2.10.tgz#19df024895876eb0ec7cd239bbaea595f703f0ae" - integrity sha512-B8G6Obn5S3RCl7hwahkQj9sKUapwXWFjiaz/Bsw1fhYFdNMnDUolRiWQSoKPb1/oKe37Dtfszoywi1Ynbo3y8w== - dependencies: - "@babel/runtime" "^7.6.0" - "@date-io/core" "1.x" - "@types/styled-jsx" "^2.2.8" - clsx "^1.0.2" - react-transition-group "^4.0.0" - rifm "^0.7.0" - -"@material-ui/pickers@^3.3.10": +"@material-ui/pickers@^3.2.2", "@material-ui/pickers@^3.3.10": version "3.3.10" resolved "https://registry.npmjs.org/@material-ui/pickers/-/pickers-3.3.10.tgz#f1b0f963348cc191645ef0bdeff7a67c6aa25485" integrity sha512-hS4pxwn1ZGXVkmgD4tpFpaumUaAg2ZzbTrxltfC5yPw4BJV+mGkfnQOB4VpWEYZw2jv65Z0wLwDE/piQiPPZ3w== @@ -3517,7 +3455,7 @@ react-transition-group "^4.0.0" rifm "^0.7.0" -"@material-ui/styles@^4.10.0", "@material-ui/styles@^4.11.0", "@material-ui/styles@^4.11.3", "@material-ui/styles@^4.11.4", "@material-ui/styles@^4.9.6": +"@material-ui/styles@^4.10.0", "@material-ui/styles@^4.11.0", "@material-ui/styles@^4.11.4", "@material-ui/styles@^4.9.6": version "4.11.4" resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.4.tgz#eb9dfccfcc2d208243d986457dff025497afa00d" integrity sha512-KNTIZcnj/zprG5LW0Sao7zw+yG3O35pviHzejMdcSGCdWbiO8qzRgOYL8JAxAsWBKOKYwVZxXtHWaB5T2Kvxew== @@ -3549,7 +3487,7 @@ csstype "^2.5.2" prop-types "^15.7.2" -"@material-ui/types@5.1.0", "@material-ui/types@^5.1.0": +"@material-ui/types@5.1.0": version "5.1.0" resolved "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz#efa1c7a0b0eaa4c7c87ac0390445f0f88b0d88f2" integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== @@ -3570,20 +3508,20 @@ dependencies: "@types/whatwg-streams" "^0.0.7" -"@microsoft/api-documenter@^7.12.16": - version "7.13.24" - resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.24.tgz#3bae26f7520a993c4009569d8bd229c76d266dc3" - integrity sha512-HH8aE7Yo0D+eQuV86lwF4YGW669wNxUevybGMndsTyQEeRqUpnr4wz9ptFi7FGjaUjTKjGWQKH2bm4dt8BZsUg== +"@microsoft/api-documenter@^7.13.30": + version "7.13.30" + resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.13.30.tgz#f4832b8747ad9f61b3a0d87eb61b6b1aca2aeb60" + integrity sha512-n91XihJptwcHp1g5FUIcrjDXhg/g2q6+Rj+nuPBkvsCAKQP/OwCLNVO3tYNpz+qa+lWrHPWL3Urc8G3th5cn7w== dependencies: "@microsoft/api-extractor-model" "7.13.3" "@microsoft/tsdoc" "0.13.2" "@rushstack/node-core-library" "3.39.0" - "@rushstack/ts-command-line" "4.7.10" + "@rushstack/ts-command-line" "4.8.0" colors "~1.2.1" js-yaml "~3.13.1" resolve "~1.17.0" -"@microsoft/api-extractor-model@7.13.3", "@microsoft/api-extractor-model@^7.12.5": +"@microsoft/api-extractor-model@7.13.3", "@microsoft/api-extractor-model@^7.13.3": version "7.13.3" resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.13.3.tgz#ac01c064c5af520d3661c85d7e5ef95e1ca8ab92" integrity sha512-uXilAhu2GcvyY/0NwVRk3AN7TFYjkPnjHLV2UywTTz9uglS+Af0YjNrCy+aaK8qXtfbFWdBzkH9N2XU8/YBeRQ== @@ -3592,30 +3530,23 @@ "@microsoft/tsdoc-config" "~0.15.2" "@rushstack/node-core-library" "3.39.0" -"@microsoft/api-extractor-model@workspace:*": - version "7.12.5" - resolved "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.12.5.tgz#28d2804865ceba9cd89ab4f05cff99d16fa6c9b8" - integrity sha512-oeHZW83JWjIVoCDvdwI5nsZGPxThbq4gZTLAYNeJGZE/mKEO0iayMPGmI3EllJBjwQsFvNVU+O/HGULhB2to/g== +"@microsoft/api-extractor@^7.18.1": + version "7.18.1" + resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.18.1.tgz#61b39f972b646261dd49f2de9f5d448aa6497e7a" + integrity sha512-qljUF2Q0zAx1vJrjKkJVGN7OVbsXki+Pji99jywyl6L/FK3YZ7PpstUJYE6uBcLPy6rhNPWPAsHNTMpG/kHIsg== dependencies: - "@microsoft/tsdoc" "0.12.24" - "@rushstack/node-core-library" "3.36.2" - -"@microsoft/api-extractor@7.13.2-pr1916.0": - version "7.13.2-pr1916.0" - resolved "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.13.2-pr1916.0.tgz#2e10cb928ea81b56cd5f63264da11f1f9b9310a5" - integrity sha512-0/HajL+NUixuNGMfFZbHmKJn5VEqiF45q2FXhu8UrggutdJ+9M6wZ++fejUHfxlC/WhQVrVVRtf4xvVM3oIW+A== - dependencies: - "@microsoft/api-extractor-model" "workspace:*" - "@microsoft/tsdoc" "0.12.24" - "@rushstack/node-core-library" "workspace:*" - "@rushstack/rig-package" "workspace:*" - "@rushstack/ts-command-line" "workspace:*" + "@microsoft/api-extractor-model" "7.13.3" + "@microsoft/tsdoc" "0.13.2" + "@microsoft/tsdoc-config" "~0.15.2" + "@rushstack/node-core-library" "3.39.0" + "@rushstack/rig-package" "0.2.12" + "@rushstack/ts-command-line" "4.8.0" colors "~1.2.1" lodash "~4.17.15" resolve "~1.17.0" semver "~7.3.0" source-map "~0.6.1" - typescript "~4.1.3" + typescript "~4.3.2" "@microsoft/fetch-event-source@2.0.1": version "2.0.1" @@ -3637,11 +3568,6 @@ jju "~1.4.0" resolve "~1.19.0" -"@microsoft/tsdoc@0.12.24": - version "0.12.24" - resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.12.24.tgz#30728e34ebc90351dd3aff4e18d038eed2c3e098" - integrity sha512-Mfmij13RUTmHEMi9vRUhMXD7rnGR2VvxeNYtaGtaJ4redwwjT4UXYJ+nzmVJF7hhd4pn/Fx5sncDKxMVFJSWPg== - "@microsoft/tsdoc@0.13.2": version "0.13.2" resolved "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.13.2.tgz#3b0efb6d3903bd49edb073696f60e90df08efb26" @@ -3903,16 +3829,7 @@ "@octokit/types" "^6.16.2" deprecation "^2.3.1" -"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5": - version "2.0.5" - resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.5.tgz#72cc91edc870281ad583a42619256b380c600143" - integrity sha512-T/2wcCFyM7SkXzNoyVNWjyVlUwBvW3igM3Btr/eKYiPmucXTtkxt2RBsf6gn3LTzaLSLTQtNmvg+dGsOxQrjZg== - dependencies: - "@octokit/types" "^6.0.3" - deprecation "^2.0.0" - once "^1.4.0" - -"@octokit/request-error@^2.1.0": +"@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.5", "@octokit/request-error@^2.1.0": version "2.1.0" resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz#9e150357831bfc788d13a4fd4b1913d60c74d677" integrity sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg== @@ -4004,12 +3921,7 @@ schema-utils "^2.6.5" source-map "^0.7.3" -"@popperjs/core@^2.5.4": - version "2.5.4" - resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.5.4.tgz#de25b5da9f727985a3757fd59b5d028aba75841a" - integrity sha512-ZpKr+WTb8zsajqgDkvCEWgp6d5eJT6Q63Ng2neTbzBO76Lbe91vX/iVIW9dikq+Fs3yEo+ls4cxeXABD2LtcbQ== - -"@popperjs/core@^2.6.0": +"@popperjs/core@^2.5.4", "@popperjs/core@^2.6.0": version "2.9.2" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.9.2.tgz#adea7b6953cbb34651766b0548468e743c6a2353" integrity sha512-VZMYa7+fXHdwIq1TDhSXoVmSPEGM/aa+6Aiq3nVVJ9bXr24zScr+NlKFKC3iPljA7ho/GAZr+d2jOf5GIRC30Q== @@ -4248,21 +4160,6 @@ estree-walker "^2.0.1" picomatch "^2.2.2" -"@rushstack/node-core-library@3.36.2", "@rushstack/node-core-library@workspace:*": - version "3.36.2" - resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.36.2.tgz#ba00d313577f9b06d5aafaa29da0d94e594874c0" - integrity sha512-5J8xSY/PuCKR+yfxS497l0PP43kBUeD86S4eS3RzrmMle04J4522MWal8mk1T1EIDpYpgi8qScannU9oVxoStA== - dependencies: - "@types/node" "10.17.13" - colors "~1.2.1" - fs-extra "~7.0.1" - import-lazy "~4.0.0" - jju "~1.4.0" - resolve "~1.17.0" - semver "~7.3.0" - timsort "~0.3.0" - z-schema "~3.18.3" - "@rushstack/node-core-library@3.39.0": version "3.39.0" resolved "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-3.39.0.tgz#38928946d15ae89b773386cf97433d0d1ec83b93" @@ -4278,7 +4175,7 @@ timsort "~0.3.0" z-schema "~3.18.3" -"@rushstack/rig-package@workspace:*": +"@rushstack/rig-package@0.2.12": version "0.2.12" resolved "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.2.12.tgz#c434d62b28e0418a040938226f8913971d0424c7" integrity sha512-nbePcvF8hQwv0ql9aeQxcaMPK/h1OLAC00W7fWCRWIvD2MchZOE8jumIIr66HGrfG2X1sw++m/ZYI4D+BM5ovQ== @@ -4286,10 +4183,10 @@ resolve "~1.17.0" strip-json-comments "~3.1.1" -"@rushstack/ts-command-line@4.7.10", "@rushstack/ts-command-line@workspace:*": - version "4.7.10" - resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.7.10.tgz#a2ec6efb1945b79b496671ce90eb1be4f1397d31" - integrity sha512-8t042g8eerypNOEcdpxwRA3uCmz0duMo21rG4Z2mdz7JxJeylDmzjlU3wDdef2t3P1Z61JCdZB6fbm1Mh0zi7w== +"@rushstack/ts-command-line@4.8.0": + version "4.8.0" + resolved "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-4.8.0.tgz#611accb931b9ac62ff4d078f68f95c47f6606724" + integrity sha512-nZ8cbzVF1VmFPfSJfy8vEohdiFAH/59Y/Y+B4nsJbn4SkifLJ8LqNZ5+LxCC2UR242EXFumxlsY1d6fPBxck5Q== dependencies: "@types/argparse" "1.0.38" argparse "~1.0.9" @@ -5373,15 +5270,7 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react-hooks@^3.3.0": - version "3.4.1" - resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.4.1.tgz#1f8ccd21208086ec228d9743fe40b69d0efcd7e5" - integrity sha512-LbzvE7oKsVzuW1cxA/aOeNgeVvmHWG2p/WSzalIGyWuqZT3jVcNDT5KPEwy36sUYWde0Qsh32xqIUFXukeywXg== - dependencies: - "@babel/runtime" "^7.5.4" - "@types/testing-library__react-hooks" "^3.3.0" - -"@testing-library/react-hooks@^3.4.2": +"@testing-library/react-hooks@^3.3.0", "@testing-library/react-hooks@^3.4.2": version "3.4.2" resolved "https://registry.npmjs.org/@testing-library/react-hooks/-/react-hooks-3.4.2.tgz#8deb94f7684e0d896edd84a4c90e5b79a0810bc2" integrity sha512-RfPG0ckOzUIVeIqlOc1YztKgFW+ON8Y5xaSPbiBkfj9nMkkiLhLeBXT5icfPX65oJV/zCZu4z8EVnUc6GY9C5A== @@ -5489,14 +5378,7 @@ "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0" -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.0.9" - resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.9.tgz#be82fab304b141c3eee81a4ce3b034d0eba1590a" - integrity sha512-jEFQ8L1tuvPjOI8lnpaf73oCJe+aoxL6ygqSy6c8LcW98zaC+4mzWuQIRCEvKeCOu+lbqdXcg4Uqmm1S8AP1tw== - dependencies: - "@babel/types" "^7.3.0" - -"@types/babel__traverse@^7.0.4": +"@types/babel__traverse@*", "@types/babel__traverse@^7.0.4", "@types/babel__traverse@^7.0.6": version "7.0.15" resolved "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.15.tgz#db9e4238931eb69ef8aab0ad6523d4d4caa39d03" integrity sha512-Pzh9O3sTK8V6I1olsXpCfj2k/ygO2q1X0vhhnDrEQyYLHZesWz+zMZMVcwXLCYf0U36EtmyYaFGPfXlTtDHe3A== @@ -5557,14 +5439,7 @@ dependencies: "@types/tern" "*" -"@types/color-convert@*": - version "1.9.0" - resolved "https://registry.npmjs.org/@types/color-convert/-/color-convert-1.9.0.tgz#bfa8203e41e7c65471e9841d7e306a7cd8b5172d" - integrity sha512-OKGEfULrvSL2VRbkl/gnjjgbbF7ycIlpSsX7Nkab4MOWi5XxmgBYvuiQ7lcCFY5cPDz7MUNaKgxte2VRmtr4Fg== - dependencies: - "@types/color-name" "*" - -"@types/color-convert@^2.0.0": +"@types/color-convert@*", "@types/color-convert@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" integrity sha512-m7GG7IKKGuJUXvkZ1qqG3ChccdIM/qBBo913z+Xft0nKCX4hAU/IxKwZBU4cpRZ7GS5kV4vOblUkILtSShCPXQ== @@ -5693,13 +5568,6 @@ resolved "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-2.0.0.tgz#59df94a8e47ed1050a337d4ffb4d4d213aa590a8" integrity sha512-EF0lWZ4tg7oDFg4YQFlbOU3936e3a9UmoQ2IXlBy1+cv2c2Pv7knhKUzGlH5Hq2sF/KeDTH1amiRPey2rrLMQA== -"@types/d3-shape@*", "@types/d3-shape@^2.0.0": - version "2.1.0" - resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.0.tgz#cc7bbc9fc2c25f092bd457887a3224a21a55ca55" - integrity sha512-xTMEs8eITRksXclcVxMHIONRdyjj2TjDIwO4XFOPTVBNK9/oC4ZOhUbvTz1IpcsEsS/mClwuulP+OoawSAbSGA== - dependencies: - "@types/d3-path" "^1" - "@types/d3-shape@^1": version "1.3.5" resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-1.3.5.tgz#c0164c1be1429473016f855871d487f806c4e968" @@ -5707,6 +5575,13 @@ dependencies: "@types/d3-path" "^1" +"@types/d3-shape@^2.0.0": + version "2.1.0" + resolved "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-2.1.0.tgz#cc7bbc9fc2c25f092bd457887a3224a21a55ca55" + integrity sha512-xTMEs8eITRksXclcVxMHIONRdyjj2TjDIwO4XFOPTVBNK9/oC4ZOhUbvTz1IpcsEsS/mClwuulP+OoawSAbSGA== + dependencies: + "@types/d3-path" "^1" + "@types/d3-zoom@^2.0.0": version "2.0.1" resolved "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-2.0.1.tgz#6f0d993042124947314053c937784e24d8b003cf" @@ -5847,11 +5722,6 @@ dependencies: highlight.js "^10.1.0" -"@types/history@*": - version "4.7.5" - resolved "https://registry.npmjs.org/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860" - integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw== - "@types/html-minifier-terser@^5.0.0": version "5.1.0" resolved "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-5.1.0.tgz#551a4589b6ee2cc9c1dff08056128aec29b94880" @@ -5886,9 +5756,9 @@ integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A== "@types/http-errors@^1.6.3": - version "1.8.0" - resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69" - integrity sha512-2aoSC4UUbHDj2uCsCxcG/vRMXey/m17bC7UwitVm5hn22nI8O8Y9iDpA76Orc+DWkQ4zZrOKEshCqR/jSuXAHA== + version "1.8.1" + resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.1.tgz#e81ad28a60bee0328c6d2384e029aec626f1ae67" + integrity sha512-e+2rjEwK6KDaNOm5Aa9wNGgyS9oSZU/4pfSMMPYNOfjvFI0WVXm29+ITRFr6aKDvvKo7uU1jV68MW4ScsfDi7Q== "@types/http-proxy-agent@^2.0.2": version "2.0.2" @@ -6103,12 +5973,7 @@ resolved "https://registry.npmjs.org/@types/lunr/-/lunr-2.3.3.tgz#ec985618fd2712c010f8edab4f1ae7784ad7c583" integrity sha512-09sXZZVsB3Ib41U0fC+O1O+4UOZT1bl/e+/QubPxpqDWHNEchvx/DEb1KJMOwq6K3MTNzZFoNSzVdR++o1DVnw== -"@types/luxon@^1.25.0": - version "1.26.5" - resolved "https://registry.npmjs.org/@types/luxon/-/luxon-1.26.5.tgz#843fb705e16e4d2a90847a351b799ea9d879859e" - integrity sha512-XeQxxRMyJi1znfzHw4CGDLyup/raj84SnjjkI2fDootZPGlB0yqtvlvEIAmzHDa5wiEI5JJevZOWxpcofsaV+A== - -"@types/luxon@^1.27.0": +"@types/luxon@^1.25.0", "@types/luxon@^1.27.0": version "1.27.0" resolved "https://registry.npmjs.org/@types/luxon/-/luxon-1.27.0.tgz#1e3b5a7f8ca6944349c43498b4442b742c71ab0b" integrity sha512-rr2lNXsErnA/ARtgFn46NtQjUa66cuwZYeo/2K7oqqxhJErhXgHBPyNKCo+pfOC3L7HFwtao8ebViiU9h4iAxA== @@ -6168,14 +6033,7 @@ dependencies: "@types/node" "*" -"@types/mock-fs@^4.10.0": - version "4.10.0" - resolved "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.10.0.tgz#460061b186993d76856f669d5317cda8a007c24b" - integrity sha512-FQ5alSzmHMmliqcL36JqIA4Yyn9jyJKvRSGV3mvPh108VFatX7naJDzSG4fnFQNZFq9dIx0Dzoe6ddflMB2Xkg== - dependencies: - "@types/node" "*" - -"@types/mock-fs@^4.13.0": +"@types/mock-fs@^4.10.0", "@types/mock-fs@^4.13.0": version "4.13.0" resolved "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.0.tgz#b8b01cd2db588668b2532ecd21b1babd3fffb2c0" integrity sha512-FUqxhURwqFtFBCuUj3uQMp7rPSQs//b3O9XecAVxhqS9y4/W8SIJEZFq2mmpnFVZBXwR/2OyPLE97CpyYiB8Mw== @@ -6197,7 +6055,7 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@^14.14.31", "@types/node@^14.14.32": +"@types/node@*", "@types/node@>= 8", "@types/node@>=12.12.47", "@types/node@>=13.7.0": version "14.17.0" resolved "https://registry.npmjs.org/@types/node/-/node-14.17.0.tgz#3ba770047723b3eeb8dc9fca02cce8a7fb6378da" integrity sha512-w8VZUN/f7SSbvVReb9SWp6cJFevxb4/nkG65yLAya//98WgocKm5PLDAtSs5CtJJJM+kHmJjO/6mmYW4MHShZA== @@ -6207,11 +6065,6 @@ resolved "https://registry.npmjs.org/@types/node/-/node-10.17.13.tgz#ccebcdb990bd6139cd16e84c39dc2fb1023ca90c" integrity sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== -"@types/node@>=13.7.0": - version "15.12.1" - resolved "https://registry.npmjs.org/@types/node/-/node-15.12.1.tgz#9b60797dee1895383a725f828a869c86c6caa5c2" - integrity sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw== - "@types/node@^10.1.0", "@types/node@^10.12.0": version "10.17.35" resolved "https://registry.npmjs.org/@types/node/-/node-10.17.35.tgz#58058f29b870e6ae57b20e4f6e928f02b7129f56" @@ -6222,12 +6075,7 @@ resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c" integrity sha512-Be46CNIHWAagEfINOjmriSxuv7IVcqbGe+sDSg2SYCEz/0CRBy7LRASGfRbD8KZkqoePU73Wsx3UvOSFcq/9hA== -"@types/node@^13.7.0": - version "13.13.45" - resolved "https://registry.npmjs.org/@types/node/-/node-13.13.45.tgz#e6676bcca092bae5751d015f074a234d5a82eb63" - integrity sha512-703YTEp8AwQeapI0PTXDOj+Bs/mtdV/k9VcTP7z/de+lx6XjFMKdB+JhKnK+6PZ5za7omgZ3V6qm/dNkMj/Zow== - -"@types/node@^14.14.33": +"@types/node@^14.14.31", "@types/node@^14.14.32", "@types/node@^14.14.33": version "14.17.3" resolved "https://registry.npmjs.org/@types/node/-/node-14.17.3.tgz#6d327abaa4be34a74e421ed6409a0ae2f47f4c3d" integrity sha512-e6ZowgGJmTuXa3GyaPbTGxX17tnThl2aSSizrFthQ7m9uLGZBXiGhgE55cjRZTF5kjZvYn9EOPOMljdjwbflxw== @@ -6242,6 +6090,11 @@ resolved "https://registry.npmjs.org/@types/npmlog/-/npmlog-4.1.2.tgz#d070fe6a6b78755d1092a3dc492d34c3d8f871c4" integrity sha512-4QQmOF5KlwfxJ5IGXFIudkeLCdMABz03RcUXu+LCb24zmln8QW6aDjuGl4d4XPVLf2j+FnjelHTP7dvceAFbhA== +"@types/nunjucks@^3.1.4": + version "3.1.4" + resolved "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.4.tgz#2f80e2fa5e7982b2131201d0b4474ab4d03d9de1" + integrity sha512-cR65PLlHKW/qxxj840dbNb3ICO+iAVQzaNKJ8TcKOVKFi+QcAkhw9SCY8VFAyU41SmJMs+2nrIN2JGhX+jYb7A== + "@types/oauth@*": version "0.9.1" resolved "https://registry.npmjs.org/@types/oauth/-/oauth-0.9.1.tgz#e17221e7f7936b0459ae7d006255dff61adca305" @@ -6334,21 +6187,16 @@ integrity sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q== "@types/prop-types@*", "@types/prop-types@^15.7.3": - version "15.7.3" - resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" - integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + version "15.7.4" + resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" + integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== "@types/q@^1.5.1": version "1.5.2" resolved "https://registry.npmjs.org/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8" integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw== -"@types/qs@*", "@types/qs@^6.9.0": - version "6.9.4" - resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.4.tgz#a59e851c1ba16c0513ea123830dd639a0a15cb6a" - integrity sha512-+wYo+L6ZF6BMoEjtf8zB2esQsqdV6WsjRK/GP9WOgLPrq87PbNWgIxS76dS5uvl/QXtHGakZmwTznIfcPXcKlQ== - -"@types/qs@^6.9.5": +"@types/qs@*", "@types/qs@^6.9.0", "@types/qs@^6.9.5": version "6.9.6" resolved "https://registry.npmjs.org/@types/qs/-/qs-6.9.6.tgz#df9c3c8b31a247ec315e6996566be3171df4b3b1" integrity sha512-0/HnwIfW4ki2D8L8c9GVcG5I72s9jP5GSLVF0VIXDW00kmIpA6O33G7a8n59Tmh7Nz0WUC3rSb7PTY/sdW2JzA== @@ -6363,15 +6211,7 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz#7ee330ba7caafb98090bece86a5ee44115904c2c" integrity sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== -"@types/reach__router@^1.3.5": - version "1.3.5" - resolved "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.5.tgz#14e1e981cccd3a5e50dc9e969a72de0b9d472f6d" - integrity sha512-h0NbqXN/tJuBY/xggZSej1SKQEstbHO7J/omt1tYoFGmj3YXOodZKbbqD4mNDh7zvEGYd7YFrac1LTtAr3xsYQ== - dependencies: - "@types/history" "*" - "@types/react" "*" - -"@types/reach__router@^1.3.7": +"@types/reach__router@^1.3.5", "@types/reach__router@^1.3.7": version "1.3.7" resolved "https://registry.npmjs.org/@types/reach__router/-/reach__router-1.3.7.tgz#de8ab374259ae7f7499fc1373b9697a5f3cd6428" integrity sha512-cyBEb8Ef3SJNH5NYEIDGPoMMmYUxROatuxbICusVRQIqZUB85UCt6R2Ok60tKS/TABJsJYaHyNTW3kqbpxlMjg== @@ -6491,15 +6331,7 @@ dependencies: "@types/react" "*" -"@types/recharts@^1.8.14": - version "1.8.15" - resolved "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.15.tgz#02bc06085c9a31a58c00194d15377b45cf506bbf" - integrity sha512-ApCfDT/R8RCbZTcl0iqLiKsxVdzE3GzoawTgJUHuQOz6ZXhsPVfp7CNSKI2s3zFwrRRsgmpv2AEcbcZceNHg4w== - dependencies: - "@types/d3-shape" "*" - "@types/react" "*" - -"@types/recharts@^1.8.15": +"@types/recharts@^1.8.14", "@types/recharts@^1.8.15": version "1.8.19" resolved "https://registry.npmjs.org/@types/recharts/-/recharts-1.8.19.tgz#047f72cf4c25df545aa1085fe3a085e58a2483c1" integrity sha512-Fd2cYnBlWz/ga8rLmjwsZNBAc4CzXZiuTYPPqMIgrtQ02yI/OTm8WPM6ZyUuYlSdyangtsvFmHWzZ7W4tuknDA== @@ -6707,13 +6539,6 @@ dependencies: "@types/jest" "*" -"@types/testing-library__react-hooks@^3.3.0": - version "3.4.0" - resolved "https://registry.npmjs.org/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.0.tgz#be148b7fa7d19cd3349c4ef9d9534486bc582fcc" - integrity sha512-QYLZipqt1hpwYsBU63Ssa557v5wWbncqL36No59LI7W3nCMYKrLWTnYGn2griZ6v/3n5nKXNYkTeYpqPHY7Ukg== - dependencies: - "@types/react-test-renderer" "*" - "@types/testing-library__react-hooks@^3.4.0": version "3.4.1" resolved "https://registry.npmjs.org/@types/testing-library__react-hooks/-/testing-library__react-hooks-3.4.1.tgz#b8d7311c6c1f7db3103e94095fe901f8fef6e433" @@ -7130,6 +6955,11 @@ JSONStream@^1.0.4: jsonparse "^1.2.0" through ">=2.2.7 <3" +a-sync-waterfall@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz#75b6b6aa72598b497a125e7a2770f14f4c8a1fa7" + integrity sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA== + abab@^2.0.0, abab@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz#623e2075e02eb2d3f2475e49f99c91846467907a" @@ -7208,12 +7038,7 @@ acorn@^6.0.1, acorn@^6.4.1: resolved "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474" integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA== -acorn@^7.1.1: - version "7.3.1" - resolved "https://registry.npmjs.org/acorn/-/acorn-7.3.1.tgz#85010754db53c3fbaf3b9ea3e083aa5c5d147ffd" - integrity sha512-tLc0wSnatxAQHVHUapaHdz72pi9KUyHjq5KyHjGg9Y8Ifdc79pTh2XvI6I1/chZbnM7QtNKzh66ooDogPZSleA== - -acorn@^7.4.0: +acorn@^7.1.1, acorn@^7.4.0: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== @@ -7448,15 +7273,7 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -anymatch@^3.0.3: - version "3.1.1" - resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -anymatch@~3.1.2: +anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== @@ -7850,7 +7667,7 @@ arrify@^2.0.0, arrify@^2.0.1: resolved "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== -asap@^2.0.0, asap@~2.0.3: +asap@^2.0.0, asap@^2.0.3, asap@~2.0.3: version "2.0.6" resolved "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= @@ -8010,16 +7827,11 @@ aws-sign2@~0.7.0: resolved "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= -aws4@^1.11.0: +aws4@^1.11.0, aws4@^1.8.0: version "1.11.0" resolved "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== -aws4@^1.8.0: - version "1.9.1" - resolved "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e" - integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug== - axe-core@^4.0.2: version "4.1.3" resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.1.3.tgz#64a4c85509e0991f5168340edc4bedd1ceea6966" @@ -9092,15 +8904,7 @@ cachedir@^2.3.0: resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8" integrity sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw== -call-bind@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" - integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.0" - -call-bind@^1.0.2: +call-bind@^1.0.0, call-bind@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== @@ -9137,7 +8941,7 @@ callsites@^3.0.0: resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== -camel-case@4.1.1, camel-case@^4.1.1: +camel-case@4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== @@ -9145,7 +8949,7 @@ camel-case@4.1.1, camel-case@^4.1.1: pascal-case "^3.1.1" tslib "^1.10.0" -camel-case@4.1.2, camel-case@^4.1.2: +camel-case@4.1.2, camel-case@^4.1.1, camel-case@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== @@ -9966,13 +9770,13 @@ concat-with-sourcemaps@^1.1.0: source-map "^0.6.1" concurrently@^6.0.0: - version "6.0.0" - resolved "https://registry.npmjs.org/concurrently/-/concurrently-6.0.0.tgz#c1a876dd99390979c71f8c6fe6796882f3a13199" - integrity sha512-Ik9Igqnef2ONLjN2o/OVx1Ow5tymVvvEwQeYCQdD/oV+CN9oWhxLk7ibcBdOtv0UzBqHCEKRwbKceYoTK8t3fQ== + version "6.2.0" + resolved "https://registry.npmjs.org/concurrently/-/concurrently-6.2.0.tgz#587e2cb8afca7234172d8ea55176088632c4c56d" + integrity sha512-v9I4Y3wFoXCSY2L73yYgwA9ESrQMpRn80jMcqMgHx720Hecz2GZAvTI6bREVST6lkddNypDKRN22qhK0X8Y00g== dependencies: chalk "^4.1.0" date-fns "^2.16.1" - lodash "^4.17.20" + lodash "^4.17.21" read-pkg "^5.2.0" rxjs "^6.6.3" spawn-command "^0.0.2-1" @@ -10215,12 +10019,7 @@ core-js-compat@^3.9.0, core-js-compat@^3.9.1: browserslist "^4.16.6" semver "7.0.0" -core-js-pure@^3.0.0, core-js-pure@^3.0.1: - version "3.6.4" - resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz#4bf1ba866e25814f149d4e9aaa08c36173506e3a" - integrity sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw== - -core-js-pure@^3.10.2, core-js-pure@^3.6.5: +core-js-pure@^3.0.0, core-js-pure@^3.0.1, core-js-pure@^3.10.2, core-js-pure@^3.6.5: version "3.15.1" resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.15.1.tgz#8356f6576fa2aa8e54ca6fe02620968ff010eed7" integrity sha512-OZuWHDlYcIda8sJLY4Ec6nWq2hRjlyCqCZ+jCflyleMkVt3tPedDVErvHslyS2nbO+SlBFMSBJYvtLMwxnrzjA== @@ -10732,16 +10531,11 @@ cssstyle@^2.2.0: dependencies: cssom "~0.3.6" -csstype@^2.2.0: +csstype@^2.2.0, csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: version "2.6.17" resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e" integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A== -csstype@^2.5.2, csstype@^2.5.7, csstype@^2.6.7: - version "2.6.9" - resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz#05141d0cd557a56b8891394c1911c40c8a98d098" - integrity sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q== - csstype@^3.0.2, csstype@^3.0.6: version "3.0.7" resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b" @@ -11056,12 +10850,7 @@ date-fns@^1.27.2: resolved "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== -date-fns@^2.0.0-alpha.27, date-fns@^2.16.1: - version "2.18.0" - resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.18.0.tgz#08e50aee300ad0d2c5e054e3f0d10d8f9cdfe09e" - integrity sha512-NYyAg4wRmGVU4miKq5ivRACOODdZRY3q5WLmOJSq8djyzftYphU7dTHLcEtLqEvfqMKQ0jVv91P4BAwIjsXIcw== - -date-fns@^2.18.0: +date-fns@^2.0.0-alpha.27, date-fns@^2.16.1, date-fns@^2.18.0: version "2.19.0" resolved "https://registry.npmjs.org/date-fns/-/date-fns-2.19.0.tgz#65193348635a28d5d916c43ec7ce6fbd145059e1" integrity sha512-X3bf2iTPgCAQp9wvjOQytnf5vO5rESYRXlPIVcgSbtT5OTScPcsf9eZU+B/YIkKAtYr5WeCii58BgATrNitlWg== @@ -11095,14 +10884,14 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1: +debug@4, debug@4.3.2, debug@^4.0.0, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.3.0, debug@^4.3.1: version "4.3.2" resolved "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b" integrity sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw== dependencies: ms "2.1.2" -debug@4.3.1, debug@^4.3.0: +debug@4.3.1: version "4.3.1" resolved "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== @@ -11562,7 +11351,7 @@ dom-helpers@^5.0.0, dom-helpers@^5.0.1: "@babel/runtime" "^7.8.7" csstype "^2.6.7" -dom-serializer@0, dom-serializer@^0.2.1: +dom-serializer@0: version "0.2.2" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== @@ -11594,12 +11383,7 @@ domelementtype@1, domelementtype@^1.3.1: resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== -domelementtype@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz#1f8bdfe91f5a78063274e803b4bdcedf6e94f94d" - integrity sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ== - -domelementtype@^2.1.0: +domelementtype@^2.0.1, domelementtype@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== @@ -11660,16 +11444,7 @@ domutils@^1.5.1, domutils@^1.7.0: dom-serializer "0" domelementtype "1" -domutils@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/domutils/-/domutils-2.0.0.tgz#15b8278e37bfa8468d157478c58c367718133c08" - integrity sha512-n5SelJ1axbO636c2yUtOGia/IcJtVtlhQbFiVDBZHKV5ReJO1ViX7sFEemtuyoAnBxk5meNSYgA8V4s0271efg== - dependencies: - dom-serializer "^0.2.1" - domelementtype "^2.0.1" - domhandler "^3.0.0" - -domutils@^2.4.4: +domutils@^2.0.0, domutils@^2.4.4: version "2.4.4" resolved "https://registry.npmjs.org/domutils/-/domutils-2.4.4.tgz#282739c4b150d022d34699797369aad8d19bbbd3" integrity sha512-jBC0vOsECI4OMdD0GC9mGn7NXPLb+Qt6KW1YDQzeQYRUFKmNG8lh7mO5HiELfr+lLQE7loDVI4QcAxV80HS+RA== @@ -11678,14 +11453,6 @@ domutils@^2.4.4: domelementtype "^2.0.1" domhandler "^4.0.0" -dot-case@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" - integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== - dependencies: - no-case "^3.0.3" - tslib "^1.10.0" - dot-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" @@ -11701,20 +11468,13 @@ dot-prop@^4.1.0: dependencies: is-obj "^1.0.0" -dot-prop@^5.1.0: +dot-prop@^5.1.0, dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== dependencies: is-obj "^2.0.0" -dot-prop@^5.2.0: - version "5.2.0" - resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz#c34ecc29556dc45f1f4c22697b6f4904e0cc4fcb" - integrity sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A== - dependencies: - is-obj "^2.0.0" - dot-prop@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083" @@ -11837,17 +11597,7 @@ ejs@^3.1.2: dependencies: jake "^10.6.1" -electron-to-chromium@^1.3.378: - version "1.3.509" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.509.tgz#830fcb89cd66dc2984d18d794973b99e3f00584c" - integrity sha512-cN4lkjNRuTG8rtAqTOVgwpecEC2kbKA04PG6YijcKGHK/kD0xLjiqExcAOmLUwtXZRF8cBeam2I0VZcih919Ug== - -electron-to-chromium@^1.3.564: - version "1.3.685" - resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.685.tgz#f636d17c9c232c925f8bbfbff4f86303da091ff9" - integrity sha512-C3oFZNkJ8lz85ADqr3hzpjBc2ciejMRN2SCd/D0hwcqpr6MGxfdN/j89VN6l+ERTuCUvhg0VYsf40Q4qTz4bhQ== - -electron-to-chromium@^1.3.723: +electron-to-chromium@^1.3.378, electron-to-chromium@^1.3.564, electron-to-chromium@^1.3.723: version "1.3.739" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.739.tgz#f07756aa92cabd5a6eec6f491525a64fe62f98b9" integrity sha512-+LPJVRsN7hGZ9EIUUiWCpO7l4E3qBYHNadazlucBfsXBbccDFNKUBAgzE68FnkWGJPwD/AfKhSzL+G+Iqb8A4A== @@ -12036,59 +11786,7 @@ errs@^0.3.2: resolved "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz#798099b2dbd37ca2bc749e538a7c1307d0b50499" integrity sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk= -es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4: - version "1.17.4" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" - integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.1.5" - is-regex "^1.0.5" - object-inspect "^1.7.0" - object-keys "^1.1.1" - object.assign "^4.1.0" - string.prototype.trimleft "^2.1.1" - string.prototype.trimright "^2.1.1" - -es-abstract@^1.17.5: - version "1.17.7" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" - integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.18.0-next.1: - version "1.18.0-next.1" - resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-negative-zero "^2.0.0" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.18.0-next.2: +es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2: version "1.18.0" resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== @@ -12494,21 +12192,14 @@ esquery@^1.4.0: dependencies: estraverse "^5.1.0" -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - integrity sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ== - dependencies: - estraverse "^4.1.0" - -esrecurse@^4.3.0: +esrecurse@^4.1.0, esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== dependencies: estraverse "^5.2.0" -estraverse@^4.1.0, estraverse@^4.1.1, estraverse@^4.2.0: +estraverse@^4.1.1, estraverse@^4.2.0: version "4.3.0" resolved "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== @@ -12584,12 +12275,7 @@ eventemitter3@^3.1.0: resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== -eventemitter3@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb" - integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg== - -eventemitter3@^4.0.4: +eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== @@ -12599,12 +12285,12 @@ events@1.1.1: resolved "https://registry.npmjs.org/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= -events@3.1.0, events@^3.0.0: +events@3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/events/-/events-3.1.0.tgz#84279af1b34cb75aa88bf5ff291f6d0bd9b31a59" integrity sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg== -events@^3.3.0: +events@^3.0.0, events@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== @@ -13101,15 +12787,7 @@ file-entry-cache@^6.0.1: dependencies: flat-cache "^3.0.4" -file-loader@^6.0.0: - version "6.1.0" - resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.1.0.tgz#65b9fcfb0ea7f65a234a1f10cdd7f1ab9a33f253" - integrity sha512-26qPdHyTsArQ6gU4P1HJbAbnFTyT2r0pG7czh1GFAd9TZbj0n94wWbupgixZH/ET/meqi2/5+F7DhW4OAXD+Lg== - dependencies: - loader-utils "^2.0.0" - schema-utils "^2.7.1" - -file-loader@^6.2.0: +file-loader@^6.0.0, file-loader@^6.2.0: version "6.2.0" resolved "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz#baef7cf8e1840df325e4390b4484879480eebe4d" integrity sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw== @@ -13464,6 +13142,25 @@ fs-constants@^1.0.0: resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@10.0.0: + version "10.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" + integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs-extra@9.1.0, fs-extra@^9.0.0, fs-extra@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" + integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^0.30.0: version "0.30.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz#f233ffcc08d4da7d432daa449776989db1df93f0" @@ -13475,15 +13172,6 @@ fs-extra@^0.30.0: path-is-absolute "^1.0.0" rimraf "^2.2.8" -fs-extra@^10.0.0: - version "10.0.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" - integrity sha512-C5owb14u9eJwizKGdchcDUQeFtlSHHthBk8pbX9Vc1PFZrLombudjDnNns88aYslCyF6IY5SUw3Roz6xShcEIQ== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^7.0.1, fs-extra@~7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9" @@ -13502,16 +13190,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.0, fs-extra@^9.1.0: - version "9.1.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-minipass@^1.2.5: version "1.2.7" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" @@ -13554,12 +13232,7 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@^2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" - integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== - -fsevents@~2.3.1, fsevents@~2.3.2: +fsevents@^2.1.2, fsevents@~2.3.1, fsevents@~2.3.2: version "2.3.2" resolved "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== @@ -13713,15 +13386,6 @@ get-caller-file@^2.0.1, get-caller-file@^2.0.5: resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.0: - version "1.0.1" - resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.1.tgz#94a9768fcbdd0595a1c9273aacf4c89d075631be" - integrity sha512-ZnWP+AmS1VUaLgTRy47+zKtjTxz+0xMpx3I52i+aalBK1QP19ggLF3Db89KJX7kjfOfP2eoa01qc++GwPgufPg== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" @@ -13853,7 +13517,7 @@ git-up@^4.0.0: is-ssh "^1.3.0" parse-url "^5.0.0" -git-url-parse@^11.4.4: +git-url-parse@^11.4.4, git-url-parse@~11.4.4: version "11.4.4" resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.4.4.tgz#5d747debc2469c17bc385719f7d0427802d83d77" integrity sha512-Y4o9o7vQngQDIU9IjyCmRJBin5iYjI5u9ZITnddRZpD7dcCFQj2sL2XuMNbLRE4b4B/4ENPsp2Q8P44fjAZ0Pw== @@ -14359,12 +14023,7 @@ graphql-type-json@^0.3.2: resolved "https://registry.npmjs.org/graphql-type-json/-/graphql-type-json-0.3.2.tgz#f53a851dbfe07bd1c8157d24150064baab41e115" integrity sha512-J+vjof74oMlCWXSvt0DOf2APEdZOCdubEvGDUAlqH//VBYcOYsGgRW7Xzorr44LvkjiuvecWc8fChxuZZbChtg== -graphql-ws@^4.1.0: - version "4.2.0" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.2.0.tgz#dfb8e355d3a226672ec682ea517b64e9e7e006a3" - integrity sha512-tPdRAFfl9Ypx+evsSYFa3nTGVcSKKaOMhTlOD7eEbiMpsb2hi6XJeSyenrH/hgf5+Q3v3GV6aZ9R/UEjakl7eQ== - -graphql-ws@^4.4.1: +graphql-ws@^4.1.0, graphql-ws@^4.4.1: version "4.7.0" resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.7.0.tgz#b323fbf35a3736eed85dac24c0054d6d10c93e62" integrity sha512-Md8SsmC9ZlsogFPd3Ot8HbIAAqsHh8Xoq7j4AmcIat1Bh6k91tjVyQvA0Au1/BolXSYq+RDvib6rATU2Hcf1Xw== @@ -14492,12 +14151,7 @@ has-glob@^1.0.0: dependencies: is-glob "^3.0.0" -has-symbols@^1.0.0, has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-symbols@^1.0.2: +has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== @@ -14615,12 +14269,12 @@ hex-color-regex@^1.1.0: resolved "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -highlight.js@^10.1.0, highlight.js@^10.6.0: +highlight.js@^10.1.0, highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@^10.6.0: version "10.7.2" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.2.tgz#89319b861edc66c48854ed1e6da21ea89f847360" integrity sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg== -highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.4.0: +highlight.js@~10.4.0: version "10.4.1" resolved "https://registry.npmjs.org/highlight.js/-/highlight.js-10.4.1.tgz#d48fbcf4a9971c4361b3f95f302747afe19dbad0" integrity sha512-yR5lWvNz7c85OhVAEAeFhVCc/GV4C30Fjzc/rCP0aCWzc1UUOPUk55dK/qdwTZHBvMZo+eZ2jpk62ndX/xMFlg== @@ -14704,12 +14358,7 @@ html-encoding-sniffer@^2.0.1: dependencies: whatwg-encoding "^1.0.5" -html-entities@^1.2.0, html-entities@^1.3.1: - version "1.3.1" - resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.3.1.tgz#fb9a1a4b5b14c5daba82d3e34c6ae4fe701a0e44" - integrity sha512-rhE/4Z3hIhzHAUKbW8jVcCyuT5oJCXXqhN/6mXXVCpzTmvJnoH2HL/bt3EZ6p55jbFJBeAe1ZNpL5BugLujxNA== - -html-entities@^1.2.1: +html-entities@^1.2.0, html-entities@^1.2.1, html-entities@^1.3.1: version "1.3.3" resolved "https://registry.npmjs.org/html-entities/-/html-entities-1.3.3.tgz#3dca638a43ee7de316fc23067398491152ad4736" integrity sha512-/VulV3SYni1taM7a4RMdceqzJWR39gpZHjBwUnsCFKWV/GJkD14CJ5F7eWcZozmHJK0/f/H5U3b3SiPkuvxMgg== @@ -15455,17 +15104,7 @@ is-buffer@^2.0.0: resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz#3e572f23c8411a5cfd9557c849e3665e0b290623" integrity sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A== -is-callable@^1.1.4, is-callable@^1.1.5: - version "1.1.5" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" - integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== - -is-callable@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== - -is-callable@^1.2.3: +is-callable@^1.1.4, is-callable@^1.2.3: version "1.2.3" resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== @@ -15691,11 +15330,6 @@ is-module@^1.0.0: resolved "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" integrity sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE= -is-negative-zero@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" - integrity sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE= - is-negative-zero@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" @@ -15830,14 +15464,7 @@ is-reference@^1.2.1: dependencies: "@types/estree" "*" -is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== - dependencies: - has-symbols "^1.0.1" - -is-regex@^1.1.2: +is-regex@^1.0.4, is-regex@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== @@ -15993,6 +15620,11 @@ isarray@^2.0.5: resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== +isbinaryfile@^4.0.8: + version "4.0.8" + resolved "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.8.tgz#5d34b94865bd4946633ecc78a026fc76c5b11fcf" + integrity sha512-53h6XFniq77YdW+spoRrebh0mnmTxRPTlcuIArO57lmMdq4uBKFKaeTjnb92oYWrSn/LVL+LT+Hap2tFQj8V+w== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -16555,16 +16187,7 @@ jest-when@^3.1.0: bunyan "^1.8.12" expect "^24.8.0" -jest-worker@^26.2.1: - version "26.3.0" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz#7c8a97e4f4364b4f05ed8bca8ca0c24de091871f" - integrity sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.6.2: +jest-worker@^26.2.1, jest-worker@^26.6.2: version "26.6.2" resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== @@ -17183,14 +16806,7 @@ keyv@^3.0.0: dependencies: json-buffer "3.0.0" -keyv@^4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6" - integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw== - dependencies: - json-buffer "3.0.1" - -keyv@^4.0.3: +keyv@^4.0.0, keyv@^4.0.3: version "4.0.3" resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== @@ -17923,13 +17539,6 @@ lower-case-first@^2.0.2: dependencies: tslib "^2.0.3" -lower-case@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" - integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== - dependencies: - tslib "^1.10.0" - lower-case@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" @@ -17994,17 +17603,12 @@ lunr@^2.3.9: resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1" integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow== -luxon@1.25.0, luxon@^1.25.0: +luxon@1.25.0: version "1.25.0" resolved "https://registry.npmjs.org/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72" integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ== -luxon@^1.26.0: - version "1.26.0" - resolved "https://registry.npmjs.org/luxon/-/luxon-1.26.0.tgz#d3692361fda51473948252061d0f8561df02b578" - integrity sha512-+V5QIQ5f6CDXQpWNICELwjwuHdqeJM1UenlZWx5ujcRMc9venvluCjFb4t5NYLhb6IhkbMVOxzVuOqkgMxee2A== - -luxon@^1.27.0: +luxon@^1.25.0, luxon@^1.26.0, luxon@^1.27.0: version "1.27.0" resolved "https://registry.npmjs.org/luxon/-/luxon-1.27.0.tgz#ae10c69113d85dab8f15f5e8390d0cbeddf4f00f" integrity sha512-VKsFsPggTA0DvnxtJdiExAucKdAnwbCCNlMM5ENvHlxubqWd0xhZcdb4XgZ7QFNhaRhilXCFxHuoObP5BNA4PA== @@ -18675,14 +18279,7 @@ minipass-json-stream@^1.0.1: jsonparse "^1.3.1" minipass "^3.0.0" -minipass-pipeline@^1.2.2: - version "1.2.2" - resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.2.tgz#3dcb6bb4a546e32969c7ad710f2c79a86abba93a" - integrity sha512-3JS5A2DKhD2g0Gg8x3yamO0pj7YeKGwVlDS90pF++kxptwx/F+B//roxf9SqYil5tQo65bijy+dAuAFZmYOouA== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.4: +minipass-pipeline@^1.2.2, minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== @@ -19043,16 +18640,7 @@ ncp@~2.0.0: resolved "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz#195a21d6c46e361d2fb1281ba38b91e9df7bdbb3" integrity sha1-GVoh1sRuNh0vsSgbo4uR6d9727M= -needle@^2.2.1: - version "2.4.1" - resolved "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz#14af48732463d7475696f937626b1b993247a56a" - integrity sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g== - dependencies: - debug "^3.2.6" - iconv-lite "^0.4.4" - sax "^1.2.4" - -needle@^2.5.0: +needle@^2.2.1, needle@^2.5.0: version "2.6.0" resolved "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz#24dbb55f2509e2324b4a99d61f413982013ccdbe" integrity sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg== @@ -19091,14 +18679,6 @@ nice-try@^1.0.4: resolved "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== -no-case@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" - integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== - dependencies: - lower-case "^2.0.1" - tslib "^1.10.0" - no-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" @@ -19287,17 +18867,7 @@ node-pre-gyp@^0.15.0: semver "^5.3.0" tar "^4.4.2" -node-releases@^1.1.52: - version "1.1.60" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.60.tgz#6948bdfce8286f0b5d0e5a88e8384e954dfe7084" - integrity sha512-gsO4vjEdQaTusZAEebUWp2a5d7dF5DYoIpDG7WySnk7BuZDW+GPpHXoXXuYawRBr/9t5q54tirPz79kFIWg4dA== - -node-releases@^1.1.61: - version "1.1.71" - resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== - -node-releases@^1.1.71: +node-releases@^1.1.52, node-releases@^1.1.61, node-releases@^1.1.71: version "1.1.72" resolved "https://registry.npmjs.org/node-releases/-/node-releases-1.1.72.tgz#14802ab6b1039a79a0c7d662b610a5bbd76eacbe" integrity sha512-LLUo+PpH3dU6XizX3iVoubUNheF/owjXCZZ5yACDxNnPtgFuludV1ZL3ayK1kVep42Rmm0+R9/Y60NQbZ2bifw== @@ -19539,6 +19109,15 @@ number-is-nan@^1.0.0: resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= +nunjucks@^3.2.3: + version "3.2.3" + resolved "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz#1b33615247290e94e28263b5d855ece765648a31" + integrity sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ== + dependencies: + a-sync-waterfall "^1.0.0" + asap "^2.0.3" + commander "^5.1.0" + nwsapi@^2.0.7, nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -19573,26 +19152,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^2.0.1: - version "2.0.3" - resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.0.3.tgz#d12db044e03cd2ca3d77c0570d87225b02e1e6ea" - integrity sha512-JPKn0GMu+Fa3zt3Bmr66JhokJU5BaNBIh4ZeTlaCBzrBsOeXzwcKKAK1tbLiPKgvwmPXsDvvLHoWh5Bm7ofIYg== - -object-hash@^2.1.1: +object-hash@^2.0.1, object-hash@^2.1.1: version "2.2.0" resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz#5ad518581eefc443bd763472b8ff2e9c2c0d54a5" integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw== -object-inspect@^1.7.0: - version "1.7.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" - integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== - -object-inspect@^1.8.0: - version "1.8.0" - resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz#df807e5ecf53a609cc6bfe93eac3cc7be5b3a9d0" - integrity sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA== - object-inspect@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" @@ -19603,7 +19167,7 @@ object-is@^1.0.1: resolved "https://registry.npmjs.org/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== -object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: +object-keys@^1.0.12, object-keys@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== @@ -19620,17 +19184,7 @@ object-visit@^1.0.0: dependencies: isobject "^3.0.0" -object.assign@^4.1.0: - version "4.1.0" - resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" - integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== - dependencies: - define-properties "^1.1.2" - function-bind "^1.1.1" - has-symbols "^1.0.0" - object-keys "^1.0.11" - -object.assign@^4.1.1, object.assign@^4.1.2: +object.assign@^4.1.0, object.assign@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== @@ -19880,12 +19434,7 @@ outdent@^0.5.0: resolved "https://registry.npmjs.org/outdent/-/outdent-0.5.0.tgz#9e10982fdc41492bb473ad13840d22f9655be2ff" integrity sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q== -overlayscrollbars@^1.10.2: - version "1.13.0" - resolved "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.0.tgz#1edb436328133b94877b558f77966d5497ca36a7" - integrity sha512-p8oHrMeRAKxXDMPI/EBNITj/zTVHKNnAnM59Im+xnoZUlV07FyTg46wom2286jJlXGGfcPFG/ba5NUiCwWNd4w== - -overlayscrollbars@^1.13.1: +overlayscrollbars@^1.10.2, overlayscrollbars@^1.13.1: version "1.13.1" resolved "https://registry.npmjs.org/overlayscrollbars/-/overlayscrollbars-1.13.1.tgz#0b840a88737f43a946b9d87875a2f9e421d0338a" integrity sha512-gIQfzgGgu1wy80EB4/6DaJGHMEGmizq27xHIESrzXq0Y/J0Ay1P3DWk6tuVmEPIZH15zaBlxeEJOqdJKmowHCQ== @@ -20122,15 +19671,7 @@ parallel-transform@^1.1.0: inherits "^2.0.3" readable-stream "^2.1.5" -param-case@^3.0.3: - version "3.0.3" - resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" - integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== - dependencies: - dot-case "^3.0.3" - tslib "^1.10.0" - -param-case@^3.0.4: +param-case@^3.0.3, param-case@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== @@ -20251,15 +19792,7 @@ parseurl@^1.3.2, parseurl@~1.3.2, parseurl@~1.3.3: resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== -pascal-case@^3.1.1: - version "3.1.1" - resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" - integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== - dependencies: - no-case "^3.0.3" - tslib "^1.10.0" - -pascal-case@^3.1.2: +pascal-case@^3.1.1, pascal-case@^3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== @@ -21130,16 +20663,7 @@ postcss-selector-parser@^3.0.0: indexes-of "^1.0.1" uniq "^1.0.1" -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.2" - resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.2.tgz#934cf799d016c83411859e09dcecade01286ec5c" - integrity sha512-36P2QR59jDTOAiIkqEprfJDsoNrvwFei3eCqKd1Y0tUsBimsq39BLp7RD+JWny3WgB1zGhJX8XVePwm9k4wdBg== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.4: +postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2, postcss-selector-parser@^6.0.4: version "6.0.4" resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== @@ -21445,7 +20969,7 @@ proto-list@~1.2.1: resolved "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -protobufjs@^6.10.0: +protobufjs@^6.10.0, protobufjs@^6.10.2: version "6.11.2" resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" integrity sha512-4BQJoPooKJl2G9j3XftkIXjoC9C0Av2NOrWmbLWT1vH32GcSUHjM0Arra6UfTsVyfMAuFzaLucXn1sadxJydAw== @@ -21464,25 +20988,6 @@ protobufjs@^6.10.0: "@types/node" ">=13.7.0" long "^4.0.0" -protobufjs@^6.10.2: - version "6.10.2" - resolved "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.2.tgz#b9cb6bd8ec8f87514592ba3fdfd28e93f33a469b" - integrity sha512-27yj+04uF6ya9l+qfpH187aqEzfCF4+Uit0I9ZBQVqK09hk/SQzKa2MUqUpXaVa7LOFRg1TSSr3lVxGOk6c0SQ== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" "^13.7.0" - long "^4.0.0" - protocols@^1.1.0, protocols@^1.4.0: version "1.4.7" resolved "https://registry.npmjs.org/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32" @@ -21606,18 +21111,13 @@ qs@6.7.0: resolved "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== -qs@^6.10.0, qs@^6.10.1, qs@^6.5.1: +qs@^6.10.0, qs@^6.10.1, qs@^6.5.1, qs@^6.5.2, qs@^6.6.0, qs@^6.7.0, qs@^6.9.1, qs@^6.9.4, qs@^6.9.6: version "6.10.1" resolved "https://registry.npmjs.org/qs/-/qs-6.10.1.tgz#4931482fa8d647a5aab799c5271d2133b981fb6a" integrity sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg== dependencies: side-channel "^1.0.4" -qs@^6.5.2, qs@^6.6.0, qs@^6.7.0, qs@^6.9.1, qs@^6.9.4, qs@^6.9.6: - version "6.9.6" - resolved "https://registry.npmjs.org/qs/-/qs-6.9.6.tgz#26ed3c8243a431b2924aca84cc90471f35d5a0ee" - integrity sha512-TIRk4aqYLNoJUbd+g2lEdz5kLWIuTMRagAXxl78Q0RiVjAOugHmeKNGdd3cwo/ktpf9aL9epCfFqWDEKysUlLQ== - qs@~6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -22230,16 +21730,7 @@ react-text-truncate@^0.16.0: dependencies: prop-types "^15.5.7" -react-textarea-autosize@^8.1.1: - version "8.2.0" - resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.2.0.tgz#fae38653f5ec172a855fd5fffb39e466d56aebdb" - integrity sha512-grajUlVbkx6VdtSxCgzloUIphIZF5bKr21OYMceWPKkniy7H0mRAT/AXPrRtObAe+zUePnNlBwUc4ivVjUGIjw== - dependencies: - "@babel/runtime" "^7.10.2" - use-composed-ref "^1.0.0" - use-latest "^1.0.0" - -react-textarea-autosize@^8.3.0: +react-textarea-autosize@^8.1.1, react-textarea-autosize@^8.3.0: version "8.3.3" resolved "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.3.3.tgz#f70913945369da453fd554c168f6baacd1fa04d8" integrity sha512-2XlHXK2TDxS6vbQaoPbMOfQ8GK7+irc2fVK6QFIcC8GOnH3zI/v481n+j1L0WaPVvKxwesnY93fEfH++sus2rQ== @@ -23139,9 +22630,9 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: inherits "^2.0.1" rollup-plugin-dts@^3.0.1: - version "3.0.1" - resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-3.0.1.tgz#4419efb51d935cc0cff6f577f8aad97a980ee524" - integrity sha512-sdTsd0tEIV1b5Bio1k4Ei3N4/7jbwcVRdlYotGYdJOKR59JH7DzqKTSCbfaKPzuAcKTp7k317z2BzYJ3bkhDTw== + version "3.0.2" + resolved "https://registry.npmjs.org/rollup-plugin-dts/-/rollup-plugin-dts-3.0.2.tgz#2b628d88f864d271d6eaec2e4c2a60ae4e944c5c" + integrity sha512-hswlsdWu/x7k5pXzaLP6OvKRKcx8Bzprksz9i9mUe72zvt8LvqAb/AZpzs6FkLgmyRaN8B6rUQOVtzA3yEt9Yw== dependencies: magic-string "^0.25.7" optionalDependencies: @@ -23233,21 +22724,7 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.5, rxjs@^6.6.0: - version "6.6.2" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz#8096a7ac03f2cc4fe5860ef6e572810d9e01c0d2" - integrity sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg== - dependencies: - tslib "^1.9.0" - -rxjs@^6.6.3: - version "6.6.6" - resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== - dependencies: - tslib "^1.9.0" - -rxjs@^6.6.6: +rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.5.3, rxjs@^6.5.5, rxjs@^6.6.0, rxjs@^6.6.3, rxjs@^6.6.6: version "6.6.7" resolved "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== @@ -23307,9 +22784,9 @@ sane@^4.0.3: walker "~1.0.5" sanitize-html@^2.3.2: - version "2.3.3" - resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.3.3.tgz#3db382c9a621cce4c46d90f10c64f1e9da9e8353" - integrity sha512-DCFXPt7Di0c6JUnlT90eIgrjs6TsJl/8HYU3KLdmrVclFN4O0heTcVbJiMa23OKVr6aR051XYtsgd8EWwEBwUA== + version "2.4.0" + resolved "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.4.0.tgz#8da7524332eb210d968971621b068b53f17ab5a3" + integrity sha512-Y1OgkUiTPMqwZNRLPERSEi39iOebn2XJLbeiGOBhaJD/yLqtLGu6GE5w7evx177LeGgSE+4p4e107LMiydOf6A== dependencies: deepmerge "^4.2.2" escape-string-regexp "^4.0.0" @@ -23362,7 +22839,7 @@ schema-utils@^1.0.0: ajv-errors "^1.0.0" ajv-keywords "^3.1.0" -schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0, schema-utils@^2.7.1: +schema-utils@^2.6.5, schema-utils@^2.6.6, schema-utils@^2.7.0: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== @@ -23663,15 +23140,7 @@ shx@^0.3.2: minimist "^1.2.3" shelljs "^0.8.4" -side-channel@^1.0.2: - version "1.0.2" - resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" - integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== - dependencies: - es-abstract "^1.17.0-next.1" - object-inspect "^1.7.0" - -side-channel@^1.0.4: +side-channel@^1.0.2, side-channel@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== @@ -24245,16 +23714,11 @@ stoppable@^1.1.0: resolved "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz#32da568e83ea488b08e4d7ea2c3bcc9d75015d5b" integrity sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw== -store2@^2.12.0: +store2@^2.12.0, store2@^2.7.1: version "2.12.0" resolved "https://registry.npmjs.org/store2/-/store2-2.12.0.tgz#e1f1b7e1a59b6083b2596a8d067f6ee88fd4d3cf" integrity sha512-7t+/wpKLanLzSnQPX8WAcuLCCeuSHoWdQuh9SB3xD0kNOM38DNf+0Oa+wmvxmYueRzkmh6IcdKFtvTa+ecgPDw== -store2@^2.7.1: - version "2.10.0" - resolved "https://registry.npmjs.org/store2/-/store2-2.10.0.tgz#46b82bb91878daf1b0d56dec2f1d41e54d5103cf" - integrity sha512-tWEpK0snS2RPUq1i3R6OahfJNjWCQYNxq0+by1amCSuw0mXtymJpzmZIeYpA1UAa+7B0grCpNYIbDcd7AgTbFg== - storybook-dark-mode@^1.0.3: version "1.0.8" resolved "https://registry.npmjs.org/storybook-dark-mode/-/storybook-dark-mode-1.0.8.tgz#bbd64b382fd62d38685fdd769e2cac4e32ec293d" @@ -24443,14 +23907,6 @@ string.prototype.padstart@^3.0.0: define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -string.prototype.trimend@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" - integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - string.prototype.trimend@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" @@ -24459,30 +23915,6 @@ string.prototype.trimend@^1.0.4: call-bind "^1.0.2" define-properties "^1.1.3" -string.prototype.trimleft@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" - integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimright@^2.1.1: - version "2.1.1" - resolved "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" - integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== - dependencies: - define-properties "^1.1.3" - function-bind "^1.1.1" - -string.prototype.trimstart@^1.0.1: - version "1.0.3" - resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" - integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - string.prototype.trimstart@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" @@ -24912,18 +24344,7 @@ tar-fs@~2.0.1: pump "^3.0.0" tar-stream "^2.0.0" -tar-stream@^2.0.0, tar-stream@^2.1.4: - version "2.1.4" - resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.4.tgz#c4fb1a11eb0da29b893a5b25476397ba2d053bfa" - integrity sha512-o3pS2zlG4gxr67GmFYBLlq+dM8gyRGUOvsrHclSkvtVtQbjV0s/+ZE8OpICbaj8clrX3tjeHngYGP7rweaBnuw== - dependencies: - bl "^4.0.3" - end-of-stream "^1.4.1" - fs-constants "^1.0.0" - inherits "^2.0.3" - readable-stream "^3.1.1" - -tar-stream@^2.2.0: +tar-stream@^2.0.0, tar-stream@^2.1.4, tar-stream@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287" integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ== @@ -24993,21 +24414,7 @@ teeny-request@^7.0.0: stream-events "^1.0.5" uuid "^8.0.0" -telejson@^5.0.2: - version "5.0.2" - resolved "https://registry.npmjs.org/telejson/-/telejson-5.0.2.tgz#ed1e64be250cc1c757a53c19e1740b49832b3d51" - integrity sha512-XCrDHGbinczsscs8LXFr9jDhvy37yBk9piB7FJrCfxE8oP66WDkolNMpaBkWYgQqB9dQGBGtTDzGQPedc9KJmw== - dependencies: - "@types/is-function" "^1.0.0" - global "^4.4.0" - is-function "^1.0.2" - is-regex "^1.1.1" - is-symbol "^1.0.3" - isobject "^4.0.0" - lodash "^4.17.19" - memoizerific "^1.11.3" - -telejson@^5.1.0, telejson@^5.3.2: +telejson@^5.0.2, telejson@^5.1.0, telejson@^5.3.2: version "5.3.3" resolved "https://registry.npmjs.org/telejson/-/telejson-5.3.3.tgz#fa8ca84543e336576d8734123876a9f02bf41d2e" integrity sha512-PjqkJZpzEggA9TBpVtJi1LVptP7tYtXB6rEubwlHap76AMjzvOdKX41CxyaW7ahhzDU1aftXnMCx5kAPDZTQBA== @@ -25571,7 +24978,7 @@ tslib@^2, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3, tslib@^2.1.0, tslib@~2.3.0: resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz#803b8cdab3e12ba581a4ca41c8839bbb0dacb09e" integrity sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg== -tslib@~2.0.0, tslib@~2.0.1: +tslib@~2.0.1: version "2.0.3" resolved "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== @@ -25738,16 +25145,16 @@ typescript-json-schema@^0.50.1: typescript "~4.2.3" yargs "^16.2.0" -typescript@^4.0.3, typescript@~4.1.3: - version "4.2.3" - resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.3.tgz#39062d8019912d43726298f09493d598048c1ce3" - integrity sha512-qOcYwxaByStAWrBf4x0fibwZvMRG+r4cQoTjbPtUlrWjBHbmCAww1i448U0GJ+3cNNEtebDteo/cHOR3xJ4wEw== - -typescript@~4.2.3: +typescript@^4.0.3, typescript@~4.2.3: version "4.2.4" resolved "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== +typescript@~4.3.2: + version "4.3.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz#4d1c37cc16e893973c45a06886b7113234f119f4" + integrity sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA== + ua-parser-js@^0.7.18: version "0.7.28" resolved "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" @@ -25810,16 +25217,11 @@ undefsafe@^2.0.3: dependencies: debug "^2.2.0" -underscore@^1.12.1: +underscore@^1.12.1, underscore@^1.9.1: version "1.13.1" resolved "https://registry.npmjs.org/underscore/-/underscore-1.13.1.tgz#0c1c6bd2df54b6b69f2314066d65b6cde6fcf9d1" integrity sha512-hzSoAVtJF+3ZtiFX0VgfFPHEDRm7Y/QPjGyNo4TVdnDTdft3tr8hEkD25a1jC+TjTuE7tkHGKkhwCgs9dgBB2g== -underscore@^1.9.1: - version "1.11.0" - resolved "https://registry.npmjs.org/underscore/-/underscore-1.11.0.tgz#dd7c23a195db34267186044649870ff1bab5929e" - integrity sha512-xY96SsN3NA461qIRKZ/+qox37YXPtSBswMGfiNptr+wrt6ds4HaMw23TP612fEyGekRE6LNRiLYr/aqbHXNedw== - unfetch@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" @@ -26639,16 +26041,7 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -whatwg-url@^8.0.0: - version "8.1.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz#c628acdcf45b82274ce7281ee31dd3c839791771" - integrity sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^2.0.2" - webidl-conversions "^5.0.0" - -whatwg-url@^8.4.0: +whatwg-url@^8.0.0, whatwg-url@^8.4.0: version "8.4.0" resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.4.0.tgz#50fb9615b05469591d2b2bd6dfaed2942ed72837" integrity sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw== @@ -26897,7 +26290,7 @@ ws@^5.2.0: dependencies: async-limiter "~1.0.0" -"ws@^5.2.0 || ^6.0.0 || ^7.0.0": +"ws@^5.2.0 || ^6.0.0 || ^7.0.0", ws@^7.2.3, ws@^7.3.1: version "7.5.0" resolved "https://registry.npmjs.org/ws/-/ws-7.5.0.tgz#0033bafea031fb9df041b2026fc72a571ca44691" integrity sha512-6ezXvzOZupqKj4jUqbQ9tXuJNo+BR2gU8fFRk3XCP3e0G6WT414u5ELe6Y0vtp7kmSJ3F7YWObSNr1ESsgi4vw== @@ -26909,11 +26302,6 @@ ws@^6.1.2, ws@^6.2.1: dependencies: async-limiter "~1.0.0" -ws@^7.2.3, ws@^7.3.1: - version "7.3.1" - resolved "https://registry.npmjs.org/ws/-/ws-7.3.1.tgz#d0547bf67f7ce4f12a72dfe31262c68d7dc551c8" - integrity sha512-D3RuNkynyHmEJIpD2qrgVkc9DQ23OrN/moAwZX4L8DfvszsJxpjQuUq3LMx6HoYji9fbIOBY18XWBsAux1ZZUA== - xcase@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9"