diff --git a/.changeset/late-walls-cry.md b/.changeset/late-walls-cry.md new file mode 100644 index 0000000000..c9fcb3112a --- /dev/null +++ b/.changeset/late-walls-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-search-backend-module-elasticsearch': patch +--- + +Pinning version of elastic search client to 7.13.0 to prevent breaking change towards third party ElasticSearch clusters on 7.14.0. diff --git a/.changeset/neat-pugs-wait.md b/.changeset/neat-pugs-wait.md new file mode 100644 index 0000000000..8902f44c9d --- /dev/null +++ b/.changeset/neat-pugs-wait.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-code-coverage': patch +--- + +Change represented test date from epoch to something more human friendly. Round test coverage to 2 decimal places. diff --git a/.changeset/odd-rats-walk.md b/.changeset/odd-rats-walk.md new file mode 100644 index 0000000000..3a8710cc32 --- /dev/null +++ b/.changeset/odd-rats-walk.md @@ -0,0 +1,5 @@ +--- +'@backstage/config-loader': patch +--- + +Bump msw to the same version as the rest diff --git a/.changeset/real-mails-add.md b/.changeset/real-mails-add.md new file mode 100644 index 0000000000..30a9100699 --- /dev/null +++ b/.changeset/real-mails-add.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +fixed route resolving (issue #7741) when user cannot select a tab in any of the tabbed pages (like the Catalog page) if it shares the same initial letters as a preceding tab. (i.e. where tab with a path of /ci is followed by a path of /ci-2, user cannot select /ci-2 as /ci will always be selected first). diff --git a/.changeset/slimy-days-leave.md b/.changeset/slimy-days-leave.md new file mode 100644 index 0000000000..df772d80d4 --- /dev/null +++ b/.changeset/slimy-days-leave.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Allow singleton and flexibly nested EntityFilters diff --git a/.changeset/smart-penguins-compete.md b/.changeset/smart-penguins-compete.md new file mode 100644 index 0000000000..d3518600a6 --- /dev/null +++ b/.changeset/smart-penguins-compete.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +export `loadIdentityOwnerRefs` and `loadCatalogOwnerRefs` all the way diff --git a/.changeset/tall-boxes-sit.md b/.changeset/tall-boxes-sit.md new file mode 100644 index 0000000000..a74e0400bd --- /dev/null +++ b/.changeset/tall-boxes-sit.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Add Theme Overrides for Sidebar diff --git a/.changeset/tough-buckets-explain.md b/.changeset/tough-buckets-explain.md new file mode 100644 index 0000000000..a2ca37d516 --- /dev/null +++ b/.changeset/tough-buckets-explain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': patch +--- + +Simplified queue time calculation in `BuildTable`. diff --git a/.github/workflows/snyk-github-issue-sync.yml b/.github/workflows/snyk-github-issue-sync.yml new file mode 100644 index 0000000000..34eb5c1ca4 --- /dev/null +++ b/.github/workflows/snyk-github-issue-sync.yml @@ -0,0 +1,23 @@ +name: 'Snyk Github Issue Sync' + +on: + schedule: + - cron: '0 */4 * * *' + +jobs: + sync: + steps: + - uses: actions/checkout@v2 + - name: Create Snyk report + uses: snyk/actions/node@master + with: + args: > + --yarn-workspaces + --org=backstage-dgh + --strict-out-of-sync=false + --json-file-output=snyk.json + json: true + env: + SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} + - name: Update Github issues + run: yarn ts-node scripts/snyk-github-issue-sync.ts diff --git a/ADOPTERS.md b/ADOPTERS.md index 06501a2a10..f0b2fa16f6 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -63,3 +63,4 @@ | [Splunk](https://www.splunk.com) | [@tonytamsf](https://github.com/tonytamsf) | Developer portal as a centralized place to find people, services, documentation, escalation policies and give bravos. This portal is also being used as a centralized search engine for engineering specific documentation. | | [SoundCloud](https://www.soundcloud.com) | [Julio Zynger](https://github.com/julioz) | Developer portal as a [humane registry](https://martinfowler.com/bliki/HumaneRegistry.html) for the organization: catalog of people, services, documentation, feature toggles, escalation policies, etc. | | [Volvofinans Bank](https://www.volvofinans.se) | [Johan Hammar](https://github.com/johanhammar) | Developer portal enabling engineers to manage and explore software and documentation. | +| [Palo Alto Networks](https://www.paloaltonetworks.com) | [Jeremy Guarini](https://github.com/jeremyguarini), [Brian Lomeland](https://github.com/bbbmmmlll), [Palo Alto Networks](https://github.com/PaloAltoNetworks) | Developer portal, service catalog, documentation and tooling | diff --git a/microsite/sidebars.json b/microsite/sidebars.json index bee9cd7180..23cc6135c0 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -275,7 +275,8 @@ "architecture-decisions/adrs-adr008", "architecture-decisions/adrs-adr009", "architecture-decisions/adrs-adr010", - "architecture-decisions/adrs-adr011" + "architecture-decisions/adrs-adr011", + "architecture-decisions/adrs-adr012" ], "FAQ": ["FAQ"] } diff --git a/package.json b/package.json index f5d20fd39d..62a8a0924e 100644 --- a/package.json +++ b/package.json @@ -58,9 +58,10 @@ "@microsoft/tsdoc": "^0.13.2" }, "devDependencies": { - "@types/webpack": "^5.28.0", "@changesets/cli": "^2.14.0", + "@octokit/rest": "^18.12.0", "@spotify/prettier-config": "^11.0.0", + "@types/webpack": "^5.28.0", "command-exists": "^1.2.9", "concurrently": "^6.0.0", "eslint-plugin-notice": "^0.9.10", @@ -68,6 +69,7 @@ "husky": "^6.0.0", "lerna": "^4.0.0", "lint-staged": "^11.1.2", + "minimist": "^1.2.5", "prettier": "^2.2.1", "shx": "^0.3.2", "yarn-lock-check": "^1.0.5" diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 910dd87108..22fcedf486 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -53,7 +53,7 @@ "@types/node": "^14.14.32", "@types/yup": "^0.29.13", "mock-fs": "^5.1.0", - "msw": "^0.29.0" + "msw": "^0.35.0" }, "files": [ "dist" diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx index 9b33304a95..bbd48c127c 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.test.tsx @@ -31,6 +31,12 @@ const testRoute2 = { children:
tabbed-test-content-2
, }; +const testRoute3 = { + title: 'tabbed-test-title-3', + path: '/some-other-path-similar', + children:
tabbed-test-content-3
, +}; + describe('RoutedTabs', () => { it('renders simplest case', async () => { const rendered = await renderInTestApp( @@ -46,7 +52,7 @@ describe('RoutedTabs', () => { } + element={} /> , ); @@ -61,6 +67,13 @@ describe('RoutedTabs', () => { expect(rendered.getByText('tabbed-test-title-2')).toBeInTheDocument(); expect(rendered.queryByText('tabbed-test-content-2')).toBeInTheDocument(); + + const thirdTab = rendered.queryAllByRole('tab')[2]; + act(() => { + fireEvent.click(thirdTab); + }); + expect(rendered.getByText('tabbed-test-title-3')).toBeInTheDocument(); + expect(rendered.queryByText('tabbed-test-content-3')).toBeInTheDocument(); }); describe('correctly delegates nested links', () => { diff --git a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx index 2a65c8ffc8..5debafd3f8 100644 --- a/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx +++ b/packages/core-components/src/components/TabbedLayout/RoutedTabs.tsx @@ -33,9 +33,15 @@ export function useSelectedSubRoute(subRoutes: SubRoute[]): { element: children, })); - const element = useRoutes(routes) ?? subRoutes[0].children; + // TODO: remove once react-router updated + const sortedRoutes = routes.sort((a, b) => + // remove "/*" symbols from path end before comparing + b.path.replace(/\/\*$/, '').localeCompare(a.path.replace(/\/\*$/, '')), + ); - const [matchedRoute] = matchRoutes(routes, `/${params['*']}`) ?? []; + const element = useRoutes(sortedRoutes) ?? subRoutes[0].children; + + const [matchedRoute] = matchRoutes(sortedRoutes, `/${params['*']}`) ?? []; const foundIndex = matchedRoute ? subRoutes.findIndex(t => `${t.path}/*` === matchedRoute.route.path) : 0; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 883e0395cd..ed089ad6a1 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -350,21 +350,30 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { ); } -export const SidebarSpace = styled('div')({ - flex: 1, -}); +export const SidebarSpace = styled('div')( + { + flex: 1, + }, + { name: 'BackstageSidebarSpace' }, +); -export const SidebarSpacer = styled('div')({ - height: 8, -}); +export const SidebarSpacer = styled('div')( + { + height: 8, + }, + { name: 'BackstageSidebarSpacer' }, +); -export const SidebarDivider = styled('hr')({ - height: 1, - width: '100%', - background: '#383838', - border: 'none', - margin: '12px 0px', -}); +export const SidebarDivider = styled('hr')( + { + height: 1, + width: '100%', + background: '#383838', + border: 'none', + margin: '12px 0px', + }, + { name: 'BackstageSidebarDivider' }, +); const styledScrollbar = (theme: Theme): CreateCSSProperties => ({ overflowY: 'auto', diff --git a/plugins/azure-devops-common/.eslintrc.js b/plugins/azure-devops-common/.eslintrc.js new file mode 100644 index 0000000000..13573efa9c --- /dev/null +++ b/plugins/azure-devops-common/.eslintrc.js @@ -0,0 +1,3 @@ +module.exports = { + extends: [require.resolve('@backstage/cli/config/eslint')], +}; diff --git a/plugins/azure-devops-common/api-report.md b/plugins/azure-devops-common/api-report.md new file mode 100644 index 0000000000..0bc6568189 --- /dev/null +++ b/plugins/azure-devops-common/api-report.md @@ -0,0 +1,47 @@ +## API Report File for "@backstage/plugin-azure-devops-common" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +// Warning: (ae-missing-release-tag) "BuildResult" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum BuildResult { + Canceled = 32, + Failed = 8, + None = 0, + PartiallySucceeded = 4, + Succeeded = 2, +} + +// Warning: (ae-missing-release-tag) "BuildStatus" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export enum BuildStatus { + All = 47, + Cancelling = 4, + Completed = 2, + InProgress = 1, + None = 0, + NotStarted = 32, + Postponed = 8, +} + +// Warning: (ae-missing-release-tag) "RepoBuild" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type RepoBuild = { + id?: number; + title: string; + link?: string; + status?: BuildStatus; + result?: BuildResult; + queueTime?: Date; + startTime?: Date; + finishTime?: Date; + source: string; + uniqueName?: string; +}; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/azure-devops-common/package.json b/plugins/azure-devops-common/package.json new file mode 100644 index 0000000000..05f70f6596 --- /dev/null +++ b/plugins/azure-devops-common/package.json @@ -0,0 +1,37 @@ +{ + "name": "@backstage/plugin-azure-devops-common", + "version": "0.0.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": false, + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "module": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/azure-devops-common" + }, + "keywords": [ + "backstage" + ], + "scripts": { + "build": "backstage-cli build", + "lint": "backstage-cli lint", + "test": "backstage-cli test --passWithNoTests", + "prepack": "backstage-cli prepack", + "postpack": "backstage-cli postpack", + "clean": "backstage-cli clean" + }, + "devDependencies": { + "@backstage/cli": "^0.8.1" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/azure-devops-common/src/index.ts b/plugins/azure-devops-common/src/index.ts new file mode 100644 index 0000000000..26a854de89 --- /dev/null +++ b/plugins/azure-devops-common/src/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 * from './types'; diff --git a/plugins/azure-devops-common/src/types.ts b/plugins/azure-devops-common/src/types.ts new file mode 100644 index 0000000000..d7736d03e1 --- /dev/null +++ b/plugins/azure-devops-common/src/types.ts @@ -0,0 +1,82 @@ +/* + * 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 enum BuildResult { + /** + * No result + */ + None = 0, + /** + * The build completed successfully. + */ + Succeeded = 2, + /** + * The build completed compilation successfully but had other errors. + */ + PartiallySucceeded = 4, + /** + * The build completed unsuccessfully. + */ + Failed = 8, + /** + * The build was canceled before starting. + */ + Canceled = 32, +} + +export enum BuildStatus { + /** + * No status. + */ + None = 0, + /** + * The build is currently in progress. + */ + InProgress = 1, + /** + * The build has completed. + */ + Completed = 2, + /** + * The build is cancelling + */ + Cancelling = 4, + /** + * The build is inactive in the queue. + */ + Postponed = 8, + /** + * The build has not yet started. + */ + NotStarted = 32, + /** + * All status. + */ + All = 47, +} + +export type RepoBuild = { + id?: number; + title: string; + link?: string; + status?: BuildStatus; + result?: BuildResult; + queueTime?: Date; + startTime?: Date; + finishTime?: Date; + source: string; + uniqueName?: string; +}; diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx new file mode 100644 index 0000000000..76a19155ba --- /dev/null +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.stories.tsx @@ -0,0 +1,94 @@ +/* + * 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 { + BuildResult, + BuildStatus, + RepoBuild, +} from '@backstage/plugin-azure-devops-common'; + +import { BuildTable } from './BuildTable'; +import { MemoryRouter } from 'react-router'; +import React from 'react'; + +export default { + title: 'Plugins/Azure Devops/Build Table', + component: BuildTable, +}; + +const buildStatuses: Array<[BuildStatus, BuildResult]> = [ + [BuildStatus.InProgress, BuildResult.None], // In Progress + [BuildStatus.Completed, BuildResult.Succeeded], // Succeeded + [BuildStatus.Completed, BuildResult.Failed], // Failed + [BuildStatus.Completed, BuildResult.PartiallySucceeded], // Partially Succeeded + [BuildStatus.Completed, BuildResult.Canceled], // Cancelled + [BuildStatus.Completed, BuildResult.None], // Unknown + [BuildStatus.Cancelling, BuildResult.None], // Cancelling + [BuildStatus.Postponed, BuildResult.None], // Postponed + [BuildStatus.NotStarted, BuildResult.None], // Not Started + [BuildStatus.None, BuildResult.None], // Unknown +]; + +const generateTestData = (rows = 10): RepoBuild[] => { + const repoBuilds: RepoBuild[] = []; + + for (let i = 0; i < rows; i++) { + const [status, result] = buildStatuses[i] ?? [ + BuildStatus.Completed, + BuildResult.Succeeded, + ]; + + repoBuilds.push({ + id: rows - i + 12534, + title: `backstage ci - 1.0.0-preview-${rows - i}`, + status, + result, + queueTime: new Date(Date.now() - i * 60000), + source: 'refs/heads/main', + link: '', + }); + } + + return repoBuilds; +}; + +export const Default = () => ( + + + +); + +export const Empty = () => ( + + + +); + +export const Loading = () => ( + + + +); + +export const ErrorMessage = () => ( + + + +); diff --git a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx index 7e260be6e9..5667c65711 100644 --- a/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx +++ b/plugins/azure-devops/src/components/BuildTable/BuildTable.tsx @@ -149,9 +149,7 @@ const columns: TableColumn[] = [ field: 'queueTime', width: 'auto', render: (row: Partial) => - DateTime.fromISO( - row.queueTime ? row.queueTime.toString() : new Date().toString(), - ).toRelative(), + DateTime.fromJSDate(row.queueTime ?? new Date()).toRelative(), }, ]; diff --git a/plugins/catalog-backend/api-report.md b/plugins/catalog-backend/api-report.md index 6d21884c12..143e31bc21 100644 --- a/plugins/catalog-backend/api-report.md +++ b/plugins/catalog-backend/api-report.md @@ -870,11 +870,14 @@ export type EntityAncestryResponse = { // Warning: (ae-missing-release-tag) "EntityFilter" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export type EntityFilter = { - anyOf: { - allOf: EntitiesSearchFilter[]; - }[]; -}; +export type EntityFilter = + | { + allOf: EntityFilter[]; + } + | { + anyOf: EntityFilter[]; + } + | EntitiesSearchFilter; // Warning: (ae-missing-release-tag) "EntityPagination" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/plugins/catalog-backend/src/catalog/types.ts b/plugins/catalog-backend/src/catalog/types.ts index 0bc0ec4311..df693bfd42 100644 --- a/plugins/catalog-backend/src/catalog/types.ts +++ b/plugins/catalog-backend/src/catalog/types.ts @@ -22,9 +22,10 @@ import { Entity, EntityRelationSpec } from '@backstage/catalog-model'; * Any (at least one) of the outer sets must match, within which all of the * individual filters must match. */ -export type EntityFilter = { - anyOf: { allOf: EntitiesSearchFilter[] }[]; -}; +export type EntityFilter = + | { allOf: EntityFilter[] } + | { anyOf: EntityFilter[] } + | EntitiesSearchFilter; /** * A pagination rule for entities. diff --git a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts index 5cc31b5be5..6a150f7583 100644 --- a/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts +++ b/plugins/catalog-backend/src/legacy/database/CommonDatabase.ts @@ -46,7 +46,11 @@ import { DbPageInfo, Transaction, } from './types'; -import { EntityPagination } from '../../catalog/types'; +import { EntityPagination, EntitiesSearchFilter } from '../../catalog/types'; + +type LegacyEntityFilter = { + anyOf: { allOf: EntitiesSearchFilter[] }[]; +}; // The number of items that are sent per batch to the database layer, when // doing .batchInsert calls to knex. This needs to be low enough to not cause @@ -217,13 +221,28 @@ export class CommonDatabase implements Database { let entitiesQuery = tx('entities'); - for (const singleFilter of request?.filter?.anyOf ?? []) { + if ( + request?.filter && + (request.filter.hasOwnProperty('key') || + request.filter.hasOwnProperty('allOf')) + ) { + throw new Error( + 'Filters for the legacy CommonDatabase must obey the { anyOf: [{ allOf: [] }] } format.', + ); + } + for (const singleFilter of (request?.filter as LegacyEntityFilter)?.anyOf ?? + []) { entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { - for (const { - key, - matchValueIn, - matchValueExists, - } of singleFilter.allOf) { + for (const filter of singleFilter.allOf) { + if ( + filter.hasOwnProperty('anyOf') || + filter.hasOwnProperty('allOf') + ) { + throw new Error( + 'Nested filters are not supported in the legacy CommonDatabase', + ); + } + const { key, matchValueIn, matchValueExists } = filter; // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to // make a lot of sense. However, it had abysmal performance on sqlite // when datasets grew large, so we're using IN instead. diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts index 082aa47df5..32d5fa843e 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts @@ -23,6 +23,7 @@ import { DbFinalEntitiesRow, DbRefreshStateReferencesRow, DbRefreshStateRow, + DbSearchRow, } from '../database/tables'; import { NextEntitiesCatalog } from './NextEntitiesCatalog'; @@ -73,6 +74,52 @@ describe('NextEntitiesCatalog', () => { } } + async function addEntityToSearch(knex: Knex, entity: Entity) { + const id = uuid(); + const entityRef = stringifyEntityRef(entity); + const entityJson = JSON.stringify(entity); + + await knex('refresh_state').insert({ + entity_id: id, + entity_ref: entityRef, + unprocessed_entity: entityJson, + errors: '[]', + next_update_at: '2031-01-01 23:00:00', + last_discovery_at: '2021-04-01 13:37:00', + }); + + await knex('final_entities').insert({ + entity_id: id, + final_entity: entityJson, + hash: 'h', + stitch_ticket: '', + }); + + await insertSearchRow(knex, id, null, entity); + } + + async function insertSearchRow( + knex: Knex, + id: string, + previousKey: string | null, + previousValue: Object, + ) { + return Promise.all( + Object.entries(previousValue).map(async ([key, value]) => { + const currentKey = `${previousKey ? `${previousKey}.` : ``}${key}`; + if (typeof value === 'object') { + await insertSearchRow(knex, id, currentKey, value); + } else { + await knex('search').insert({ + entity_id: id, + key: currentKey, + value: value, + }); + } + }), + ); + } + describe('entityAncestry', () => { it.each(databases.eachSupportedId())( 'should return the ancestry with one parent, %p', @@ -209,4 +256,106 @@ describe('NextEntitiesCatalog', () => { 60_000, ); }); + + describe('entities', () => { + it.each(databases.eachSupportedId())( + 'should return correct entity for simple filter', + async databaseId => { + const { knex } = await createDatabase(databaseId); + const entity1: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one' }, + spec: {}, + }; + const entity2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two' }, + spec: { + test: 'test value', + }, + }; + await addEntityToSearch(knex, entity1); + await addEntityToSearch(knex, entity2); + const catalog = new NextEntitiesCatalog(knex); + + const testFilter = { + key: 'spec.test', + matchValueExists: true, + }; + const request = { filter: testFilter }; + const { entities } = await catalog.entities(request); + + expect(entities.length).toBe(1); + expect(entities[0]).toEqual(entity2); + }, + ); + + it.each(databases.eachSupportedId())( + 'should return correct entity for nested filter', + async databaseId => { + const { knex } = await createDatabase(databaseId); + const entity1: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'one', org: 'a', desc: 'description' }, + spec: {}, + }; + const entity2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'two', org: 'b', desc: 'description' }, + spec: {}, + }; + const entity3: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'three', org: 'b', color: 'red' }, + spec: {}, + }; + const entity4: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'four', org: 'b', color: 'blue' }, + spec: {}, + }; + await addEntityToSearch(knex, entity1); + await addEntityToSearch(knex, entity2); + await addEntityToSearch(knex, entity3); + await addEntityToSearch(knex, entity4); + const catalog = new NextEntitiesCatalog(knex); + + const testFilter1 = { + key: 'metadata.org', + matchValueExists: true, + matchValueIn: ['b'], + }; + const testFilter2 = { + key: 'metadata.desc', + matchValueExists: true, + }; + const testFilter3 = { + key: 'metadata.color', + matchValueExists: true, + matchValueIn: ['blue'], + }; + const request = { + filter: { + allOf: [ + testFilter1, + { + anyOf: [testFilter2, testFilter3], + }, + ], + }, + }; + const { entities } = await catalog.entities(request); + + expect(entities.length).toBe(2); + expect(entities).toContainEqual(entity2); + expect(entities).toContainEqual(entity4); + }, + ); + }); }); diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index 0891be045d..841e57c921 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -23,6 +23,8 @@ import { EntitiesResponse, EntityAncestryResponse, EntityPagination, + EntityFilter, + EntitiesSearchFilter, } from '../catalog/types'; import { DbFinalEntitiesRow, @@ -73,6 +75,90 @@ function stringifyPagination(input: { limit: number; offset: number }) { return base64; } +function addCondition( + queryBuilder: Knex.QueryBuilder, + db: Knex, + { key, matchValueIn, matchValueExists }: EntitiesSearchFilter, +) { + // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to + // make a lot of sense. However, it had abysmal performance on sqlite + // when datasets grew large, so we're using IN instead. + const matchQuery = db('search') + .select('entity_id') + .where(function keyFilter() { + this.andWhere({ key: key.toLowerCase() }); + if (matchValueExists !== false && matchValueIn) { + if (matchValueIn.length === 1) { + this.andWhere({ value: matchValueIn[0].toLowerCase() }); + } else if (matchValueIn.length > 1) { + this.andWhere( + 'value', + 'in', + matchValueIn.map(v => v.toLowerCase()), + ); + } + } + }); + // Explicitly evaluate matchValueExists as a boolean since it may be undefined + queryBuilder.andWhere( + 'entity_id', + matchValueExists === false ? 'not in' : 'in', + matchQuery, + ); +} + +function isEntitiesSearchFilter( + filter: EntitiesSearchFilter | EntityFilter, +): filter is EntitiesSearchFilter { + return filter.hasOwnProperty('key'); +} + +function isAndEntityFilter( + filter: { allOf: EntityFilter[] } | EntityFilter, +): filter is { allOf: EntityFilter[] } { + return filter.hasOwnProperty('allOf'); +} + +function isOrEntityFilter( + filter: { anyOf: EntityFilter[] } | EntityFilter, +): filter is { anyOf: EntityFilter[] } { + return filter.hasOwnProperty('anyOf'); +} + +function parseFilter( + filter: EntityFilter, + query: Knex.QueryBuilder, + db: Knex, +): Knex.QueryBuilder { + if (isEntitiesSearchFilter(filter)) { + return query.where(function filterFunction() { + addCondition(this, db, filter); + }); + } + + if (isOrEntityFilter(filter)) { + let cumulativeQuery = query; + for (const subFilter of filter.anyOf ?? []) { + cumulativeQuery = cumulativeQuery.orWhere(subQuery => + parseFilter(subFilter, subQuery, db), + ); + } + return cumulativeQuery; + } + + if (isAndEntityFilter(filter)) { + let cumulativeQuery = query; + for (const subFilter of filter.allOf ?? []) { + cumulativeQuery = cumulativeQuery.andWhere(subQuery => + parseFilter(subFilter, subQuery, db), + ); + } + return cumulativeQuery; + } + + return query; +} + export class NextEntitiesCatalog implements EntitiesCatalog { constructor(private readonly database: Knex) {} @@ -80,41 +166,8 @@ export class NextEntitiesCatalog implements EntitiesCatalog { const db = this.database; let entitiesQuery = db('final_entities'); - - for (const singleFilter of request?.filter?.anyOf ?? []) { - entitiesQuery = entitiesQuery.orWhere(function singleFilterFn() { - for (const { - key, - matchValueIn, - matchValueExists, - } of singleFilter.allOf) { - // NOTE(freben): This used to be a set of OUTER JOIN, which may seem to - // make a lot of sense. However, it had abysmal performance on sqlite - // when datasets grew large, so we're using IN instead. - const matchQuery = db('search') - .select('entity_id') - .where(function keyFilter() { - this.andWhere({ key: key.toLowerCase() }); - if (matchValueExists !== false && matchValueIn) { - if (matchValueIn.length === 1) { - this.andWhere({ value: matchValueIn[0].toLowerCase() }); - } else if (matchValueIn.length > 1) { - this.andWhere( - 'value', - 'in', - matchValueIn.map(v => v.toLowerCase()), - ); - } - } - }); - // Explicitly evaluate matchValueExists as a boolean since it may be undefined - this.andWhere( - 'entity_id', - matchValueExists === false ? 'not in' : 'in', - matchQuery, - ); - } - }); + if (request?.filter) { + entitiesQuery = parseFilter(request.filter, entitiesQuery, db); } // TODO: move final_entities to use entity_ref diff --git a/plugins/catalog-react/api-report.md b/plugins/catalog-react/api-report.md index bf8295a8b5..c4aa254226 100644 --- a/plugins/catalog-react/api-report.md +++ b/plugins/catalog-react/api-report.md @@ -15,6 +15,7 @@ import { Context } from 'react'; import { Entity } from '@backstage/catalog-model'; import { EntityName } from '@backstage/catalog-model'; import { IconButton } from '@material-ui/core'; +import { IdentityApi } from '@backstage/core-plugin-api'; import { LinkProps } from '@backstage/core-components'; import { Observable } from '@backstage/types'; import { PropsWithChildren } from 'react'; @@ -736,6 +737,17 @@ export function getEntitySourceLocation( // @public export function isOwnerOf(owner: Entity, owned: Entity): boolean; +// @public +export function loadCatalogOwnerRefs( + catalogApi: CatalogApi, + identityOwnerRefs: string[], +): Promise; + +// @public +export function loadIdentityOwnerRefs( + identityApi: IdentityApi, +): Promise; + // Warning: (ae-missing-release-tag) "MockEntityListContextProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -823,8 +835,6 @@ export function useEntityListProvider< EntityFilters extends DefaultEntityFilters = DefaultEntityFilters, >(): EntityListContextProps; -// Warning: (ae-missing-release-tag) "useEntityOwnership" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public export function useEntityOwnership(): { loading: boolean; diff --git a/plugins/catalog-react/src/hooks/index.ts b/plugins/catalog-react/src/hooks/index.ts index 38fc58222d..bbbca3a501 100644 --- a/plugins/catalog-react/src/hooks/index.ts +++ b/plugins/catalog-react/src/hooks/index.ts @@ -37,4 +37,8 @@ export { useOwnUser } from './useOwnUser'; export { useRelatedEntities } from './useRelatedEntities'; export { useStarredEntities } from './useStarredEntities'; export { useStarredEntity } from './useStarredEntity'; -export { useEntityOwnership } from './useEntityOwnership'; +export { + loadCatalogOwnerRefs, + useEntityOwnership, + loadIdentityOwnerRefs, +} from './useEntityOwnership'; diff --git a/plugins/catalog-react/src/hooks/useEntityOwnership.ts b/plugins/catalog-react/src/hooks/useEntityOwnership.ts index aee903603a..54e9e0ff99 100644 --- a/plugins/catalog-react/src/hooks/useEntityOwnership.ts +++ b/plugins/catalog-react/src/hooks/useEntityOwnership.ts @@ -49,9 +49,16 @@ function extendUserId(id: string): string { } } -// Takes the relevant parts of the Backstage identity, and translates them into -// a list of entity refs on string form that represent the user's ownership -// connections. +/** + * Takes the relevant parts of the Backstage identity, and translates them into + * a list of entity refs on string form that represent the user's ownership + * connections. + * + * @public + * + * @param identityApi - The IdentityApi implementation + * @returns IdentityOwner refs as a string array + */ export async function loadIdentityOwnerRefs( identityApi: IdentityApi, ): Promise { @@ -81,9 +88,17 @@ export async function loadIdentityOwnerRefs( return result; } -// Takes the relevant parts of the User entity corresponding to the Backstage -// identity, and translates them into a list of entity refs on string form that -// represent the user's ownership connections. +/** + * Takes the relevant parts of the User entity corresponding to the Backstage + * identity, and translates them into a list of entity refs on string form that + * represent the user's ownership connections. + * + * @public + * + * @param catalogApi - The Catalog API implementation + * @param identityOwnerRefs - List of identity owner refs as strings + * @returns OwnerRefs as a string array + */ export async function loadCatalogOwnerRefs( catalogApi: CatalogApi, identityOwnerRefs: string[], @@ -113,6 +128,10 @@ export async function loadCatalogOwnerRefs( * owner of a given entity. When the hook is initially mounted, the loading * flag will be true and the results returned from the function will always be * false. + * + * @public + * + * @returns a function that checks if the signed in user owns an entity */ export function useEntityOwnership(): { loading: boolean; diff --git a/plugins/code-coverage/package.json b/plugins/code-coverage/package.json index 2b09b4d99f..d01e12dff5 100644 --- a/plugins/code-coverage/package.json +++ b/plugins/code-coverage/package.json @@ -33,6 +33,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "@material-ui/styles": "^4.11.0", "highlight.js": "^10.6.0", + "luxon": "^2.0.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-router": "6.0.0-beta.0", diff --git a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx index 61dc7d7158..4291599098 100644 --- a/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx +++ b/plugins/code-coverage/src/components/CoverageHistoryChart/CoverageHistoryChart.tsx @@ -46,6 +46,8 @@ import { codeCoverageApiRef } from '../../api'; import { Progress, ResponseErrorPanel } from '@backstage/core-components'; import { useApi } from '@backstage/core-plugin-api'; +import { DateTime } from 'luxon'; + type Coverage = 'line' | 'branch'; const useStyles = makeStyles(theme => ({ @@ -69,6 +71,13 @@ const getTrendIcon = (trend: number, classes: ClassNameMap) => { } }; +// convert timestamp to human friendly form +function formatDateToHuman(timeStamp: string | number) { + return DateTime.fromMillis(Number(timeStamp)).toLocaleString( + DateTime.DATETIME_MED, + ); +} + export const CoverageHistoryChart = () => { const { entity } = useEntity(); const codeCoverageApi = useApi(codeCoverageApiRef); @@ -149,10 +158,10 @@ export const CoverageHistoryChart = () => { margin={{ right: 48, top: 32 }} > - + - + { title: 'Coverage', type: 'numeric', field: 'coverage', - render: (row: CoverageTableRow) => `${row.coverage}%`, + render: (row: CoverageTableRow) => `${row.coverage.toFixed(2)}%`, }, { title: 'Missing lines', diff --git a/plugins/search-backend-module-elasticsearch/package.json b/plugins/search-backend-module-elasticsearch/package.json index 87688e455f..2d5cabf725 100644 --- a/plugins/search-backend-module-elasticsearch/package.json +++ b/plugins/search-backend-module-elasticsearch/package.json @@ -22,7 +22,7 @@ "dependencies": { "@backstage/config": "^0.1.8", "@backstage/search-common": "^0.2.0", - "@elastic/elasticsearch": "^7.13.0", + "@elastic/elasticsearch": "7.13.0", "@acuris/aws-es-connection": "^2.2.0", "aws-sdk": "^2.948.0", "elastic-builder": "^2.16.0", diff --git a/scripts/snyk-github-issue-sync.ts b/scripts/snyk-github-issue-sync.ts new file mode 100644 index 0000000000..d7f64cf1ff --- /dev/null +++ b/scripts/snyk-github-issue-sync.ts @@ -0,0 +1,200 @@ +/* + * 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. + */ +/* eslint-disable import/no-extraneous-dependencies */ +import { Octokit } from '@octokit/rest'; +import minimist from 'minimist'; +// Generated by GitHub workflow .github/workflows/snyk-github-issue-creator +import synkJsonOutput from '../snyk.json'; + +type Vulnerability = { + description: string; + packages: { + name: string; + target: string; + }[]; + snykId: string; +}; + +const argv = minimist(process.argv.slice(2)); + +const GH_OWNER = 'backstage'; +const GH_REPO = 'backstage'; +const SNYK_GH_LABEL = 'snyk-vulnerability'; +const SNYK_ID_REGEX = /\[([A-Z0-9-:]+)]/i; + +const isDryRun = 'dryrun' in argv; + +if (!process.env.GITHUB_TOKEN) { + console.error('GITHUB_TOKEN is not set. Please provide a Github token'); + process.exit(1); +} + +const octokit = new Octokit({ + auth: process.env.GITHUB_TOKEN, +}); + +if (isDryRun) { + console.log( + '⚠️ Running in dryrun mode, no issues will be updated on Github ⚠️', + ); +} + +const fetchSnykGithubIssueMap = async (): Promise> => { + const snykGithubIssueMap: Record = {}; + + const iterator = octokit.paginate.iterator(octokit.rest.issues.listForRepo, { + owner: GH_OWNER, + repo: GH_REPO, + per_page: 100, + state: 'open', + labels: SNYK_GH_LABEL, + }); + + for await (const { data: issues } of iterator) { + for (const issue of issues) { + // Gets the Vulnerability ID from square braces + const match = SNYK_ID_REGEX.exec(issue.title); + + if (match && match[1]) { + snykGithubIssueMap[match[1]] = issue.number; + } else { + console.log(`Unmatched Snyk ID for ${issue.title}`); + } + } + } + + return snykGithubIssueMap; +}; + +const generateIssueBody = (vulnerability: Vulnerability) => ` +## Affecting Packages/Plugins + +${Array.from(vulnerability.packages).map( + ({ name, target }) => `* [${name}](${target})\n`, +)} + +${vulnerability.description} +`; + +const createGithubIssue = async (vulnerability: Vulnerability) => { + console.log( + `Create Github Issue for Snyk Vulnerability ${vulnerability.snykId}`, + ); + + vulnerability.packages.forEach(({ name, target }) => { + console.log(`- ${name} [${target}]`); + }); + + if (!isDryRun) { + await octokit.issues.create({ + owner: GH_OWNER, + repo: GH_REPO, + title: `Snyk vulnerability [${vulnerability.snykId}]`, + labels: [SNYK_GH_LABEL, 'help wanted'], + body: generateIssueBody(vulnerability), + }); + } +}; + +const updateGithubIssue = async ( + githubIssueId: number, + vulnerability: Vulnerability, +) => { + console.log( + `Update Github Issue #${githubIssueId} for Snky Vulnerability ${vulnerability.snykId}`, + ); + + if (!isDryRun) { + await octokit.issues.update({ + owner: GH_OWNER, + repo: GH_REPO, + issue_number: githubIssueId, + body: generateIssueBody(vulnerability), + }); + } +}; + +const closeGithubIssue = async (githubIssueId: number) => { + console.log(`Closing Github Issue #${githubIssueId}`); + + if (!isDryRun) { + await octokit.issues.update({ + owner: GH_OWNER, + repo: GH_REPO, + issue_number: githubIssueId, + state: 'closed', + }); + } +}; + +async function main() { + const snykGithubIssueMap = await fetchSnykGithubIssueMap(); + const vulnerabilityStore: Record = {}; + + // Group the Snyk vulnerabilities, and link back to the affecting packages. + synkJsonOutput.forEach( + ({ projectName, displayTargetFile, vulnerabilities }) => { + vulnerabilities.forEach( + ({ id, description }: { id: string; description: string }) => { + if (id !== undefined && description !== undefined) { + if (vulnerabilityStore[id]) { + if ( + !vulnerabilityStore[id].packages.some( + ({ name }) => name === projectName, + ) + ) { + vulnerabilityStore[id].packages.push({ + name: projectName, + target: displayTargetFile, + }); + } + } else { + vulnerabilityStore[id] = { + description, + snykId: id, + packages: [ + { + name: projectName, + target: displayTargetFile, + }, + ], + }; + } + } + }, + ); + }, + ); + + for (const [id, vulnerability] of Object.entries(vulnerabilityStore)) { + if (snykGithubIssueMap[id]) { + await updateGithubIssue(snykGithubIssueMap[id], vulnerability); + } else { + await createGithubIssue(vulnerability); + } + } + + for (const [snykId, githubIssueId] of Object.entries(snykGithubIssueMap)) { + if (!vulnerabilityStore[snykId]) { + await closeGithubIssue(githubIssueId); + } + } +} + +main().catch(error => { + console.error(error.stack); + process.exit(1); +}); diff --git a/yarn.lock b/yarn.lock index 28e400827b..b5786626dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2712,7 +2712,7 @@ find-my-way "^2.2.2" into-stream "^5.1.1" -"@elastic/elasticsearch@^7.13.0": +"@elastic/elasticsearch@7.13.0": version "7.13.0" resolved "https://registry.npmjs.org/@elastic/elasticsearch/-/elasticsearch-7.13.0.tgz#6dcf511dfa91187e22c81e54f41f4bd0fd96b4d6" integrity sha512-WgwLWo2p9P2tdqzBGX9fHeG8p5IOTXprXNTECQG2mJ7z9n93N5AFBJpEw4d35tWWeCWi9jI13A2wzQZH7XZ/xw== @@ -2968,16 +2968,14 @@ stream-events "^1.0.1" xdg-basedir "^4.0.0" -"@graphiql/toolkit@^0.2.0": - version "0.2.2" - resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.2.2.tgz#193d570afcf686c9ee61c92054c1782b9f3c1255" - integrity sha512-kDgYhqnS4p4LqSo1KvLd3tbX8Hhdj0ZrgQuGsosjjEnahiPYmmylxUL1p9lj6348OsypcTlCncGpEjeb9S3TiQ== +"@graphiql/toolkit@^0.3.2": + version "0.3.2" + resolved "https://registry.npmjs.org/@graphiql/toolkit/-/toolkit-0.3.2.tgz#551753436ada2bc27ea870b7668e5199a958ccfb" + integrity sha512-IweIT9VC8uDovg7kuCO9YqZcnIuWU8IGzrpUisXv6CUNK2Ed1ke8yERDTMmF/rjvLd2DeVZwM8iEOjEs4sUJQw== dependencies: - "@n1ru4l/push-pull-async-iterable-iterator" "^2.1.4" - graphql-ws "^4.3.2" + "@n1ru4l/push-pull-async-iterable-iterator" "^3.0.0" + graphql-ws "^4.9.0" meros "^1.1.4" - optionalDependencies: - subscriptions-transport-ws "^0.9.18" "@graphql-codegen/cli@^1.21.3": version "1.21.6" @@ -4635,10 +4633,10 @@ outvariant "^1.2.0" strict-event-emitter "^0.2.0" -"@n1ru4l/push-pull-async-iterable-iterator@^2.1.4": - version "2.1.4" - resolved "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-2.1.4.tgz#a90225474352f9f159bff979905f707b9c6bcf04" - integrity sha512-qLIvoOUJ+zritv+BlzcBMePKNjKQzH9Rb2i9W98YXxf/M62Lye8qH0peyiU8yJ1tL0kfulWi31BoK10E6BKJeA== +"@n1ru4l/push-pull-async-iterable-iterator@^3.0.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@n1ru4l/push-pull-async-iterable-iterator/-/push-pull-async-iterable-iterator-3.1.0.tgz#be450c97d1c7cd6af1a992d53232704454345df9" + integrity sha512-K4scWxGhdQM0masHHy4gIQs2iGiLEXCrXttumknyPJqtdl4J179BjpibWSSQ1fxKdCcHgIlCTKXJU6cMM6D6Wg== "@nodelib/fs.scandir@2.1.3": version "2.1.3" @@ -4893,6 +4891,19 @@ before-after-hook "^2.1.0" universal-user-agent "^6.0.0" +"@octokit/core@^3.5.1": + version "3.5.1" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.5.1.tgz#8601ceeb1ec0e1b1b8217b960a413ed8e947809b" + integrity sha512-omncwpLVxMP+GLpLPgeGJBF6IWJFjXDS5flY5VbppePYX9XehevbDykRH9PdCdvqt9TS5AOTiDide7h0qrkHjw== + dependencies: + "@octokit/auth-token" "^2.4.4" + "@octokit/graphql" "^4.5.8" + "@octokit/request" "^5.6.0" + "@octokit/request-error" "^2.0.5" + "@octokit/types" "^6.0.3" + before-after-hook "^2.2.0" + universal-user-agent "^6.0.0" + "@octokit/endpoint@^6.0.1": version "6.0.3" resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz#dd09b599662d7e1b66374a177ab620d8cdf73487" @@ -4927,6 +4938,11 @@ "@octokit/types" "^6.12.2" btoa-lite "^1.0.0" +"@octokit/openapi-types@^11.2.0": + version "11.2.0" + resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz#b38d7fc3736d52a1e96b230c1ccd4a58a2f400a6" + integrity sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA== + "@octokit/openapi-types@^7.3.2": version "7.3.2" resolved "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-7.3.2.tgz#065ce49b338043ec7f741316ce06afd4d459d944" @@ -4937,6 +4953,13 @@ resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" integrity sha512-93uGjlhUD+iNg1iWhUENAtJata6w5nE+V4urXOAlIXdco6xNZtUSfYY8dzp3Udy74aqO/B5UZL80x/YMa5PKRw== +"@octokit/plugin-paginate-rest@^2.16.8": + version "2.17.0" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz#32e9c7cab2a374421d3d0de239102287d791bce7" + integrity sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw== + dependencies: + "@octokit/types" "^6.34.0" + "@octokit/plugin-paginate-rest@^2.6.2": version "2.7.0" resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.7.0.tgz#6bb7b043c246e0654119a6ec4e72a172c9e2c7f3" @@ -4949,6 +4972,11 @@ resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.2.tgz#394d59ec734cd2f122431fbaf05099861ece3c44" integrity sha512-oTJSNAmBqyDR41uSMunLQKMX0jmEXbwD1fpz8FG27lScV3RhtGfBa1/BBLym+PxcC16IBlF7KH9vP1BUYxA+Eg== +"@octokit/plugin-request-log@^1.0.4": + version "1.0.4" + resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz#5e50ed7083a613816b1e4a28aeec5fb7f1462e85" + integrity sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA== + "@octokit/plugin-rest-endpoint-methods@5.3.1": version "5.3.1" resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.3.1.tgz#deddce769b4ec3179170709ab42e4e9e6195aaa9" @@ -4957,6 +4985,14 @@ "@octokit/types" "^6.16.2" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@^5.12.0": + version "5.13.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz#8c46109021a3412233f6f50d28786f8e552427ba" + integrity sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA== + dependencies: + "@octokit/types" "^6.34.0" + deprecation "^2.3.1" + "@octokit/request-error@^2.0.0", "@octokit/request-error@^2.0.2", "@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" @@ -4988,6 +5024,16 @@ "@octokit/plugin-request-log" "^1.0.2" "@octokit/plugin-rest-endpoint-methods" "5.3.1" +"@octokit/rest@^18.12.0": + version "18.12.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz#f06bc4952fc87130308d810ca9d00e79f6988881" + integrity sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q== + dependencies: + "@octokit/core" "^3.5.1" + "@octokit/plugin-paginate-rest" "^2.16.8" + "@octokit/plugin-request-log" "^1.0.4" + "@octokit/plugin-rest-endpoint-methods" "^5.12.0" + "@octokit/types@^5.0.0", "@octokit/types@^5.0.1": version "5.5.0" resolved "https://registry.npmjs.org/@octokit/types/-/types-5.5.0.tgz#e5f06e8db21246ca102aa28444cdb13ae17a139b" @@ -5002,6 +5048,13 @@ dependencies: "@octokit/openapi-types" "^7.3.2" +"@octokit/types@^6.34.0": + version "6.34.0" + resolved "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz#c6021333334d1ecfb5d370a8798162ddf1ae8218" + integrity sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw== + dependencies: + "@octokit/openapi-types" "^11.2.0" + "@octokit/webhooks-methods@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-2.0.0.tgz#1108b9ea661ca6c81e4a8bfa63a09eb27d5bc2db" @@ -9898,6 +9951,11 @@ before-after-hook@^2.1.0: resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A== +before-after-hook@^2.2.0: + version "2.2.2" + resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz#a6e8ca41028d90ee2c24222f201c90956091613e" + integrity sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ== + better-opn@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/better-opn/-/better-opn-2.1.1.tgz#94a55b4695dc79288f31d7d0e5f658320759f7c6" @@ -11076,18 +11134,18 @@ code-point-at@^1.0.0: resolved "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codemirror-graphql@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.0.0.tgz#ba8db60dc42b87768d643b3d19bf088f43dc5380" - integrity sha512-6LnSeRldL7psIBfjDr4xXKxCqPVYfQE4Yj04p2VpIyAIpc4MVE4VOjzvILgnmAW8X93ou5/s5gQXvB4huDwTUQ== +codemirror-graphql@^1.0.3: + version "1.1.0" + resolved "https://registry.npmjs.org/codemirror-graphql/-/codemirror-graphql-1.1.0.tgz#dd22ddf7761efa9131fa99a70a4a85fe653484e5" + integrity sha512-bp2XUg7epL07kJcylM8VCISK6X+rFsHL2lUkPQAw2v721MVhn+80FgjMP8tiZCOfJgHn1+JgsA71L5nOHWgUdA== dependencies: - graphql-language-service-interface "^2.8.2" - graphql-language-service-parser "^1.9.0" + graphql-language-service-interface "^2.9.0" + graphql-language-service-parser "^1.10.0" -codemirror@^5.54.0: - version "5.59.4" - resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.59.4.tgz#bfc11c8ce32b04818e8d661bbd790a94f4b3a6f3" - integrity sha512-achw5JBgx8QPcACDDn+EUUXmCYzx/zxEtOGXyjvLEvYY8GleUrnfm5D+Zb+UjShHggXKDT9AXrbkBZX6a0YSQg== +codemirror@^5.58.2: + version "5.63.3" + resolved "https://registry.npmjs.org/codemirror/-/codemirror-5.63.3.tgz#97042a242027fe0c87c09b36bc01931d37b76527" + integrity sha512-1C+LELr+5grgJYqwZKqxrcbPsHFHapVaVAloBsFBASbpLnQqLw1U8yXJ3gT5D+rhxIiSpo+kTqN+hQ+9ialIXw== codeowners-utils@^1.0.2: version "1.0.2" @@ -13358,7 +13416,12 @@ entities@^1.1.1, entities@^1.1.2: resolved "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== -entities@^2.0.0, entities@~2.0.0: +entities@^2.0.0, entities@~2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + +entities@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz#68d6084cab1b079767540d80e56a39b423e4abf4" integrity sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw== @@ -14372,11 +14435,6 @@ fecha@^4.2.0: resolved "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz#3ffb6395453e3f3efff850404f0a59b6747f5f41" integrity sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg== -fetch-blob@2.1.2: - version "2.1.2" - resolved "https://registry.npmjs.org/fetch-blob/-/fetch-blob-2.1.2.tgz#a7805db1361bd44c1ef62bb57fb5fe8ea173ef3c" - integrity sha512-YKqtUDwqLyfyMnmbw8XD6Q8j9i/HggKtPEI+pZ1+8bvheBu78biSmNaXWusx1TauGqtUUGx/cBb1mKdq2rLYow== - fetch-readablestream@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/fetch-readablestream/-/fetch-readablestream-0.2.0.tgz#eaa6d1a76b12de2d4731a343393c6ccdcfe2c795" @@ -15486,18 +15544,19 @@ grapheme-splitter@^1.0.4: integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== graphiql@^1.0.0-alpha.10: - version "1.4.2" - resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.4.2.tgz#a1dc1a4d8d35f60c90d6d8a9eb62a99756e9fd9b" - integrity sha512-TQDuuU/ZqTWV1yQDpVEiKskg0IYA+Wck37DYrrFzLlpgZWRbWiyab1PyHKiRep7J540CgScBg6C/gGCymKyO3g== + version "1.4.7" + resolved "https://registry.npmjs.org/graphiql/-/graphiql-1.4.7.tgz#6a35acf0786d7518fbb986b75bf0a3d752c19c1a" + integrity sha512-oHsBTzdWTbRJhqazbjrC6wY7YInViErAeXLqetCxdFFu2Zk5FV3V3rs7KPrCyr7kM6lW0nfXMzIfKuIgxAqx7g== dependencies: - "@graphiql/toolkit" "^0.2.0" - codemirror "^5.54.0" - codemirror-graphql "^1.0.0" + "@graphiql/toolkit" "^0.3.2" + codemirror "^5.58.2" + codemirror-graphql "^1.0.3" copy-to-clipboard "^3.2.0" dset "^3.1.0" entities "^2.0.0" - graphql-language-service "^3.1.2" - markdown-it "^10.0.0" + escape-html "^1.0.3" + graphql-language-service "^3.1.6" + markdown-it "^12.2.0" graphlib@^2.1.8: version "2.1.8" @@ -15532,7 +15591,7 @@ graphql-extensions@^0.15.0: apollo-server-env "^3.1.0" apollo-server-types "^0.9.0" -graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.8.2: +graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2.9.0: version "2.8.2" resolved "https://registry.npmjs.org/graphql-language-service-interface/-/graphql-language-service-interface-2.8.2.tgz#b3bb2aef7eaf0dff0b4ea419fa412c5f66fa268b" integrity sha512-otbOQmhgkAJU1QJgQkMztNku6SbJLu/uodoFOYOOtJsizTjrMs93vkYaHCcYnLA3oi1Goj27XcHjMnRCYQOZXQ== @@ -15542,7 +15601,7 @@ graphql-language-service-interface@2.8.2, graphql-language-service-interface@^2. graphql-language-service-utils "^2.5.1" vscode-languageserver-types "^3.15.1" -graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.9.0: +graphql-language-service-parser@1.9.0, graphql-language-service-parser@^1.10.0, graphql-language-service-parser@^1.9.0: version "1.9.0" resolved "https://registry.npmjs.org/graphql-language-service-parser/-/graphql-language-service-parser-1.9.0.tgz#79af21294119a0a7e81b6b994a1af36833bab724" integrity sha512-B5xPZLbBmIp0kHvpY1Z35I5DtPoDK9wGxQVRDIzcBaiIvAmlTrDvjo3bu7vKREdjFbYKvWNgrEWENuprMbF17Q== @@ -15554,6 +15613,11 @@ graphql-language-service-types@^1.8.0: resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.1.tgz#963810010924f2b5eaea415d5b8eb0b7d42c479b" integrity sha512-IpYS0mEHEmRsFlq+loWCpSYYYizAID7Alri6GoFN1QqUdux+8rp1Tkp2NGsGDpDmm3Dbz5ojmJWzNWQGpuwveA== +graphql-language-service-types@^1.8.2: + version "1.8.2" + resolved "https://registry.npmjs.org/graphql-language-service-types/-/graphql-language-service-types-1.8.2.tgz#50ae56f69cc24fcfc3daa129b68b0eb9421e8578" + integrity sha512-Sj07RHnMwAhEvAt7Jdt1l/x56ZpoNh+V6g+T58CF6GiYqI5l4vXqqRB4d4xHDcNQX98GpJfnf3o8BqPgP3C5Sw== + graphql-language-service-utils@^2.5.1: version "2.5.1" resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.1.tgz#832ad4b0a9da03fdded756932c27e057ccf71302" @@ -15562,13 +15626,23 @@ graphql-language-service-utils@^2.5.1: graphql-language-service-types "^1.8.0" nullthrows "^1.0.0" -graphql-language-service@^3.1.2: - version "3.1.2" - resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.1.2.tgz#6f50d5d824ea09c402cb02902b10e54b9da899d5" - integrity sha512-OiOH8mVE+uotrl3jGA2Pgt9k7rrI8lgw/8p+Cf6nwyEHbmIZj37vX9KoOWgpdFhuQlw824nNxWHSbz6k90xjWQ== +graphql-language-service-utils@^2.5.3: + version "2.5.3" + resolved "https://registry.npmjs.org/graphql-language-service-utils/-/graphql-language-service-utils-2.5.3.tgz#185f4f65cf8c010871eb9405452a3a0bfdf88748" + integrity sha512-ydevEZ0AgzEKQF3hiCbLXuS0o7189Ww/T30WtCKCLaRHDYk9Yyb2PZWdhSTWLxYZTaX2TccV6NtFWvzIC7UP3g== dependencies: - graphql-language-service-interface "^2.8.2" graphql-language-service-types "^1.8.0" + nullthrows "^1.0.0" + +graphql-language-service@^3.1.6: + version "3.2.0" + resolved "https://registry.npmjs.org/graphql-language-service/-/graphql-language-service-3.2.0.tgz#e0eb6d5dea2cab92549a253d7a6b4fa0cce178b7" + integrity sha512-xM5Ua5p7ttG/oEaDy2zk35FP2O2I9qD2N0DOrjCDUVDRC06FNDG+/CvF4qX9+i8DWOI65xch5vAhSQEfS2jFsA== + dependencies: + graphql-language-service-interface "^2.9.0" + graphql-language-service-parser "^1.10.0" + graphql-language-service-types "^1.8.2" + graphql-language-service-utils "^2.5.3" graphql-request@^3.3.0: version "3.4.0" @@ -15623,16 +15697,16 @@ 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.3.2: - version "4.9.0" - resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c" - integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag== - 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== +graphql-ws@^4.9.0: + version "4.9.0" + resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-4.9.0.tgz#5cfd8bb490b35e86583d8322f5d5d099c26e365c" + integrity sha512-sHkK9+lUm20/BGawNEWNtVAeJzhZeBg21VmvmLoT5NdGVeZWv5PdIhkcayQIAgjSyyQ17WMKmbDijIPG2On+Ag== + graphql@15.5.0: version "15.5.0" resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.0.tgz#39d19494dbe69d1ea719915b578bf920344a69d5" @@ -18761,13 +18835,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -linkify-it@^2.0.0: - version "2.2.0" - resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-2.2.0.tgz#e3b54697e78bf915c70a38acd78fd09e0058b1cf" - integrity sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== - dependencies: - uc.micro "^1.0.1" - linkify-it@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.2.tgz#f55eeb8bc1d3ae754049e124ab3bb56d97797fb8" @@ -19463,17 +19530,6 @@ markdown-escapes@^1.0.0: resolved "https://registry.npmjs.org/markdown-escapes/-/markdown-escapes-1.0.4.tgz#c95415ef451499d7602b91095f3c8e8975f78535" integrity sha512-8z4efJYk43E0upd0NbVXwgSTQs6cT3T06etieCMEg7dRbzCbxUCK/GHlX8mhHRDcp+OLlHkPKsvqQTCvsRl2cg== -markdown-it@^10.0.0: - version "10.0.0" - resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-10.0.0.tgz#abfc64f141b1722d663402044e43927f1f50a8dc" - integrity sha512-YWOP1j7UbDNz+TumYP1kpwnP0aEa711cJjrAQrzd0UXlbJfc5aAq0F/PZHjiioqDC1NKgvIMX+o+9Bk7yuM2dg== - dependencies: - argparse "^1.0.7" - entities "~2.0.0" - linkify-it "^2.0.0" - mdurl "^1.0.1" - uc.micro "^1.0.5" - markdown-it@^11.0.1: version "11.0.1" resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-11.0.1.tgz#b54f15ec2a2193efa66dda1eb4173baea08993d6" @@ -19485,6 +19541,17 @@ markdown-it@^11.0.1: mdurl "^1.0.1" uc.micro "^1.0.5" +markdown-it@^12.2.0: + version "12.2.0" + resolved "https://registry.npmjs.org/markdown-it/-/markdown-it-12.2.0.tgz#091f720fd5db206f80de7a8d1f1a7035fd0d38db" + integrity sha512-Wjws+uCrVQRqOoJvze4HCqkKl1AsSh95iFAeQDwnyfxM09divCBSXlDR1uTvyUP3Grzpn4Ru8GeCxYPM8vkCQg== + dependencies: + argparse "^2.0.1" + entities "~2.1.0" + linkify-it "^3.0.1" + mdurl "^1.0.1" + uc.micro "^1.0.5" + markdown-table@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/markdown-table/-/markdown-table-2.0.0.tgz#194a90ced26d31fe753d8b9434430214c011865b" @@ -22056,9 +22123,9 @@ passport-oauth2@1.2.0: uid2 "0.0.x" passport-oauth2@1.x.x, passport-oauth2@^1.1.2, passport-oauth2@^1.4.0, passport-oauth2@^1.5.0: - version "1.6.0" - resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.0.tgz#5f599735e0ea40ea3027643785f81a3a9b4feb50" - integrity sha512-emXPLqLcVEcLFR/QvQXZcwLmfK8e9CqvMgmOFJxcNT3okSFMtUbRRKpY20x5euD+01uHsjjCa07DYboEeLXYiw== + version "1.6.1" + resolved "https://registry.npmjs.org/passport-oauth2/-/passport-oauth2-1.6.1.tgz#c5aee8f849ce8bd436c7f81d904a3cd1666f181b" + integrity sha512-ZbV43Hq9d/SBSYQ22GOiglFsjsD1YY/qdiptA+8ej+9C1dL1TVB+mBE5kDH/D4AJo50+2i8f4bx0vg4/yDDZCQ== dependencies: base64url "3.x.x" oauth "0.9.x" @@ -28523,11 +28590,6 @@ webidl-conversions@^3.0.0: resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= -webidl-conversions@^4.0.2: - version "4.0.2" - resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad" - integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg== - webidl-conversions@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" @@ -28751,24 +28813,6 @@ whatwg-url@^5.0.0: tr46 "~0.0.3" webidl-conversions "^3.0.0" -whatwg-url@^6.4.1: - version "6.5.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8" - integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - -whatwg-url@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06" - integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg== - dependencies: - lodash.sortby "^4.7.0" - tr46 "^1.0.1" - webidl-conversions "^4.0.2" - 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"