From 35725e2b83c47a2590297d4a41e5c2f585dd1630 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 9 Nov 2023 12:03:39 +0100 Subject: [PATCH 001/152] Update dependencies in frontend plugin templates Signed-off-by: Philipp Hugenroth --- .changeset/tiny-kiwis-know.md | 5 +++++ packages/cli/templates/default-plugin/package.json.hbs | 4 ++-- .../templates/default-react-plugin-package/package.json.hbs | 4 ++-- packages/cli/templates/web-library-package/package.json.hbs | 2 +- 4 files changed, 10 insertions(+), 5 deletions(-) create mode 100644 .changeset/tiny-kiwis-know.md diff --git a/.changeset/tiny-kiwis-know.md b/.changeset/tiny-kiwis-know.md new file mode 100644 index 0000000000..b0de24ce0a --- /dev/null +++ b/.changeset/tiny-kiwis-know.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Update dependencies in `defaul-plugin` template diff --git a/packages/cli/templates/default-plugin/package.json.hbs b/packages/cli/templates/default-plugin/package.json.hbs index 011a50ba37..15b22bc8e1 100644 --- a/packages/cli/templates/default-plugin/package.json.hbs +++ b/packages/cli/templates/default-plugin/package.json.hbs @@ -45,8 +45,8 @@ "@backstage/core-app-api": "{{versionQuery '@backstage/core-app-api'}}", "@backstage/dev-utils": "{{versionQuery '@backstage/dev-utils'}}", "@backstage/test-utils": "{{versionQuery '@backstage/test-utils'}}", - "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '5.10.1'}}", - "@testing-library/react": "{{versionQuery '@testing-library/react' '12.1.3'}}", + "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '6.0.0'}}", + "@testing-library/react": "{{versionQuery '@testing-library/react' '14.0.0'}}", "@testing-library/user-event": "{{versionQuery '@testing-library/user-event' '14.0.0'}}", "msw": "{{versionQuery 'msw' '1.0.0'}}" }, diff --git a/packages/cli/templates/default-react-plugin-package/package.json.hbs b/packages/cli/templates/default-react-plugin-package/package.json.hbs index 230ecc5f03..f68bd14a1d 100644 --- a/packages/cli/templates/default-react-plugin-package/package.json.hbs +++ b/packages/cli/templates/default-react-plugin-package/package.json.hbs @@ -39,8 +39,8 @@ "devDependencies": { "@backstage/cli": "{{versionQuery '@backstage/cli'}}", "@backstage/test-utils": "{{versionQuery '@backstage/test-utils'}}", - "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '5.10.1'}}", - "@testing-library/react": "{{versionQuery '@testing-library/react' '12.1.3'}}" + "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '6.0.0'}}", + "@testing-library/react": "{{versionQuery '@testing-library/react' '14.0.0'}}" }, "files": [ "dist" diff --git a/packages/cli/templates/web-library-package/package.json.hbs b/packages/cli/templates/web-library-package/package.json.hbs index f9f703f523..271417a525 100644 --- a/packages/cli/templates/web-library-package/package.json.hbs +++ b/packages/cli/templates/web-library-package/package.json.hbs @@ -30,7 +30,7 @@ }, "devDependencies": { "@backstage/cli": "{{versionQuery '@backstage/cli'}}", - "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '5.10.1'}}" + "@testing-library/jest-dom": "{{versionQuery '@testing-library/jest-dom' '6.0.0'}}" }, "files": [ "dist" From cbc6157d486f3d1cf046a0fd000942ab6930c9bd Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 9 Nov 2023 12:03:52 +0100 Subject: [PATCH 002/152] Add changeset Signed-off-by: Philipp Hugenroth --- .changeset/tiny-kiwis-know.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/tiny-kiwis-know.md b/.changeset/tiny-kiwis-know.md index b0de24ce0a..add4736527 100644 --- a/.changeset/tiny-kiwis-know.md +++ b/.changeset/tiny-kiwis-know.md @@ -2,4 +2,4 @@ '@backstage/cli': patch --- -Update dependencies in `defaul-plugin` template +Updated dependencies in frontend plugin templates From 1ff268479ed9e5c912c1123abe64c05ba387a436 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Tue, 14 Nov 2023 09:45:55 +0100 Subject: [PATCH 003/152] Added the possibility to use custom scopes for performing login with Microsoft EntraID. Signed-off-by: Daniel Doberenz --- .changeset/rotten-lemons-cry.md | 5 +++++ docs/auth/microsoft/provider.md | 4 ++++ plugins/auth-backend-module-microsoft-provider/config.d.ts | 1 + .../src/authenticator.ts | 5 ++++- 4 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/rotten-lemons-cry.md diff --git a/.changeset/rotten-lemons-cry.md b/.changeset/rotten-lemons-cry.md new file mode 100644 index 0000000000..7664eadf2b --- /dev/null +++ b/.changeset/rotten-lemons-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-microsoft-provider': minor +--- + +Added the possibility to use custom scopes for performing login with Microsoft EntraID. diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index 999e1f40d2..a6081382e5 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -28,6 +28,7 @@ On the **API permissions** tab, click on `Add Permission`, then add the followin - `openid` - `profile` - `User.Read` +- Optional custom permissions you defined in the configuration file Your company may require you to grant [admin consent](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/user-admin-consent-overview) for these permissions. Even if your company doesn't require admin consent, you may wish to do so as it means users don't need to individually consent the first time they access backstage. @@ -54,6 +55,8 @@ auth: clientSecret: ${AZURE_CLIENT_SECRET} tenantId: ${AZURE_TENANT_ID} domainHint: ${AZURE_TENANT_ID} + scope: + - user.read ``` The Microsoft provider is a structure with three mandatory configuration keys: @@ -65,6 +68,7 @@ The Microsoft provider is a structure with three mandatory configuration keys: Leave blank if your app registration is multi tenant. When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants. For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy) +- `scope` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read']. ## Adding the provider to the Backstage frontend diff --git a/plugins/auth-backend-module-microsoft-provider/config.d.ts b/plugins/auth-backend-module-microsoft-provider/config.d.ts index aa78f72271..91fffc2361 100644 --- a/plugins/auth-backend-module-microsoft-provider/config.d.ts +++ b/plugins/auth-backend-module-microsoft-provider/config.d.ts @@ -28,6 +28,7 @@ export interface Config { clientSecret: string; domainHint?: string; callbackUrl?: string; + scope?: string[]; }; }; }; diff --git a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts index 52ef1ddd90..8446881a4d 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts @@ -31,6 +31,9 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ const clientSecret = config.getString('clientSecret'); const tenantId = config.getString('tenantId'); const domainHint = config.getOptionalString('domainHint'); + const scope: string[] = config.getOptionalStringArray('scope') || [ + 'user.read', + ]; const helper = PassportOAuthAuthenticatorHelper.from( new ExtendedMicrosoftStrategy( @@ -39,7 +42,7 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ clientSecret: clientSecret, callbackURL: callbackUrl, tenant: tenantId, - scope: ['user.read'], + scope: scope, }, ( accessToken: string, From abfaf8c5024cede9010b0820fcbbd660a663664d Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Wed, 15 Nov 2023 07:18:13 +0100 Subject: [PATCH 004/152] Changed the configuration property to additionalScopes and added a tested helper function to combine lists of scopes. Signed-off-by: Daniel Doberenz --- docs/auth/microsoft/provider.md | 4 +- .../config.d.ts | 2 +- .../src/authenticator.ts | 8 +-- .../src/scopes.test.ts | 50 +++++++++++++++++++ .../src/scopes.ts | 30 +++++++++++ 5 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts create mode 100644 plugins/auth-backend-module-microsoft-provider/src/scopes.ts diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index a6081382e5..efddcbb9ea 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -55,8 +55,8 @@ auth: clientSecret: ${AZURE_CLIENT_SECRET} tenantId: ${AZURE_TENANT_ID} domainHint: ${AZURE_TENANT_ID} - scope: - - user.read + additionalScopes: + - Mail.Send ``` The Microsoft provider is a structure with three mandatory configuration keys: diff --git a/plugins/auth-backend-module-microsoft-provider/config.d.ts b/plugins/auth-backend-module-microsoft-provider/config.d.ts index 91fffc2361..df63e9ccf4 100644 --- a/plugins/auth-backend-module-microsoft-provider/config.d.ts +++ b/plugins/auth-backend-module-microsoft-provider/config.d.ts @@ -28,7 +28,7 @@ export interface Config { clientSecret: string; domainHint?: string; callbackUrl?: string; - scope?: string[]; + additionalScopes?: string[]; }; }; }; diff --git a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts index 8446881a4d..93058b7eec 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts @@ -21,6 +21,7 @@ import { PassportProfile, } from '@backstage/plugin-auth-node'; import { ExtendedMicrosoftStrategy } from './strategy'; +import { scopeHelper } from './scopes'; /** @public */ export const microsoftAuthenticator = createOAuthAuthenticator({ @@ -31,9 +32,10 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ const clientSecret = config.getString('clientSecret'); const tenantId = config.getString('tenantId'); const domainHint = config.getOptionalString('domainHint'); - const scope: string[] = config.getOptionalStringArray('scope') || [ - 'user.read', - ]; + const scope = scopeHelper( + ['user.read'], + config.getOptionalStringArray('additionalScopes'), + ); const helper = PassportOAuthAuthenticatorHelper.from( new ExtendedMicrosoftStrategy( diff --git a/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts b/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts new file mode 100644 index 0000000000..412f87a601 --- /dev/null +++ b/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { scopeHelper } from './scopes'; + +describe('microsoftScopeHelper', () => { + it('default scope only with undefined additional scopes', () => { + const scopeResult = scopeHelper(['default']); + expect(scopeResult.length).toBe(1); + expect(scopeResult).toEqual(expect.arrayContaining(['default'])); + }); + + it('default scope only with empty additional scopes', () => { + const scopeResult = scopeHelper(['default'], []); + expect(scopeResult.length).toBe(1); + expect(scopeResult).toEqual(expect.arrayContaining(['default'])); + }); + + it('default scope with mutually exclusive additional scopes', () => { + const scopeResult = scopeHelper(['default'], ['secondScope', 'thirdScope']); + expect(scopeResult.length).toBe(3); + expect(scopeResult).toEqual( + expect.arrayContaining(['default', 'secondScope', 'thirdScope']), + ); + }); + + it('default scope with overlapping additional scopes', () => { + const scopeResult = scopeHelper( + ['default', 'secondScope'], + ['default', 'secondScope', 'thirdScope'], + ); + expect(scopeResult.length).toBe(3); + expect(scopeResult).toEqual( + expect.arrayContaining(['default', 'secondScope', 'thirdScope']), + ); + }); +}); diff --git a/plugins/auth-backend-module-microsoft-provider/src/scopes.ts b/plugins/auth-backend-module-microsoft-provider/src/scopes.ts new file mode 100644 index 0000000000..2165180382 --- /dev/null +++ b/plugins/auth-backend-module-microsoft-provider/src/scopes.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * Combine two arrays of scopes without duplicates + * @param defaultScopes Default scopes + * @param additionalScopes Optional additional scopes + * @returns List of scopes + * @public + */ +export const scopeHelper = function ( + defaultScopes: string[], + additionalScopes?: string[], +): string[] { + const scope: string[] = defaultScopes.concat(additionalScopes || []); + return scope.filter((value, index) => scope.indexOf(value) === index); +}; From 4248af8b474c994076ee9a9e40c1ea37b2331d42 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Wed, 15 Nov 2023 08:53:22 +0100 Subject: [PATCH 005/152] Fixed yarn lint error. Signed-off-by: Daniel Doberenz --- plugins/auth-backend-module-microsoft-provider/src/scopes.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/auth-backend-module-microsoft-provider/src/scopes.ts b/plugins/auth-backend-module-microsoft-provider/src/scopes.ts index 2165180382..f70f69c01f 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/scopes.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/scopes.ts @@ -21,10 +21,10 @@ * @returns List of scopes * @public */ -export const scopeHelper = function ( +export const scopeHelper = ( defaultScopes: string[], additionalScopes?: string[], -): string[] { +): string[] => { const scope: string[] = defaultScopes.concat(additionalScopes || []); return scope.filter((value, index) => scope.indexOf(value) === index); }; From 6ecec4282b41ea2731d293310398592881748095 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Thu, 16 Nov 2023 10:39:09 +0100 Subject: [PATCH 006/152] Adapt new configuration key name to the documentation. Signed-off-by: Daniel Doberenz --- docs/auth/microsoft/provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index efddcbb9ea..46da74cc31 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -68,7 +68,7 @@ The Microsoft provider is a structure with three mandatory configuration keys: Leave blank if your app registration is multi tenant. When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants. For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy) -- `scope` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read']. +- `additionalScopes` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read']. ## Adding the provider to the Backstage frontend From e6a9b2dead06c3486eabc93d826f54f4beab571b Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 12:07:40 -0800 Subject: [PATCH 007/152] Cloud build plugin list results by entity metatadata name or annotations Signed-off-by: PurseChicken --- plugins/cloudbuild/api-report.md | 2 + plugins/cloudbuild/src/api/CloudbuildApi.ts | 1 + .../cloudbuild/src/api/CloudbuildClient.ts | 3 +- .../cloudbuild/src/components/Cards/Cards.tsx | 3 ++ .../WorkflowRunsTable/WorkflowRunsTable.tsx | 3 ++ .../src/components/useCloudBuildFilter.ts | 43 +++++++++++++++++++ .../src/components/useWorkflowRuns.ts | 8 +++- 7 files changed, 60 insertions(+), 3 deletions(-) create mode 100644 plugins/cloudbuild/src/components/useCloudBuildFilter.ts diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index 3fbb15000d..f8fbd9ee92 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -58,6 +58,7 @@ export const CLOUDBUILD_ANNOTATION = 'google.com/cloudbuild-project-slug'; export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; @@ -94,6 +95,7 @@ export class CloudbuildClient implements CloudbuildApi { // (undocumented) listWorkflowRuns(options: { projectId: string; + cloudBuildFilter: string; }): Promise; // (undocumented) reRunWorkflow(options: { projectId: string; runId: string }): Promise; diff --git a/plugins/cloudbuild/src/api/CloudbuildApi.ts b/plugins/cloudbuild/src/api/CloudbuildApi.ts index cb29b49fbf..cdcfbab88d 100644 --- a/plugins/cloudbuild/src/api/CloudbuildApi.ts +++ b/plugins/cloudbuild/src/api/CloudbuildApi.ts @@ -29,6 +29,7 @@ export const cloudbuildApiRef = createApiRef({ export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; diff --git a/plugins/cloudbuild/src/api/CloudbuildClient.ts b/plugins/cloudbuild/src/api/CloudbuildClient.ts index cb6747e803..b41506f852 100644 --- a/plugins/cloudbuild/src/api/CloudbuildClient.ts +++ b/plugins/cloudbuild/src/api/CloudbuildClient.ts @@ -45,11 +45,12 @@ export class CloudbuildClient implements CloudbuildApi { async listWorkflowRuns(options: { projectId: string; + cloudBuildFilter: string; }): Promise { const workflowRuns = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, - )}/builds`, + )}/builds?filter=${encodeURIComponent(options.cloudBuildFilter)}`, { headers: new Headers({ Accept: '*/*', diff --git a/plugins/cloudbuild/src/components/Cards/Cards.tsx b/plugins/cloudbuild/src/components/Cards/Cards.tsx index 2b7a8607b7..cd15bae6b9 100644 --- a/plugins/cloudbuild/src/components/Cards/Cards.tsx +++ b/plugins/cloudbuild/src/components/Cards/Cards.tsx @@ -22,6 +22,7 @@ import { WorkflowRunStatus } from '../WorkflowRunStatus'; import { Theme, makeStyles, LinearProgress } from '@material-ui/core'; import ExternalLinkIcon from '@material-ui/icons/Launch'; import { CLOUDBUILD_ANNOTATION } from '../useProjectName'; +import { getCloudbuildFilter } from '../useCloudBuildFilter'; import { InfoCard, @@ -79,9 +80,11 @@ export const LatestWorkflowRunCard = (props: { branch: string }) => { const { entity } = useEntity(); const errorApi = useApi(errorApiRef); const projectId = entity?.metadata.annotations?.[CLOUDBUILD_ANNOTATION] || ''; + const cloudBuildFilter = getCloudbuildFilter(entity); const [{ runs, loading, error }] = useWorkflowRuns({ projectId, + cloudBuildFilter, }); const lastRun = runs?.[0] ?? ({} as WorkflowRun); useEffect(() => { diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 294847a700..210a67d0dc 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -26,6 +26,7 @@ import { buildRouteRef } from '../../routes'; import { DateTime } from 'luxon'; import { Table, TableColumn, Link } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; +import { getCloudbuildFilter } from '../useCloudBuildFilter'; const generatedColumns: TableColumn[] = [ { @@ -162,9 +163,11 @@ export const WorkflowRunsTableView = ({ export const WorkflowRunsTable = (props: { entity: Entity }) => { const { value: projectName, loading } = useProjectName(props.entity); const [projectId] = (projectName ?? '/').split('/'); + const cloudBuildFilter = getCloudbuildFilter(props.entity); const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ projectId, + cloudBuildFilter, }); return ( diff --git a/plugins/cloudbuild/src/components/useCloudBuildFilter.ts b/plugins/cloudbuild/src/components/useCloudBuildFilter.ts new file mode 100644 index 0000000000..a71cc1e9b6 --- /dev/null +++ b/plugins/cloudbuild/src/components/useCloudBuildFilter.ts @@ -0,0 +1,43 @@ +/* + * 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'; + +const CLOUDBUILD_FILTER_REPO_STRING = 'substitutions.REPO_NAME='; +const CLOUDBUILD_FILTER_REPO_ANNOTATION = 'google.com/cloudbuild-repo-name'; +const CLOUDBUILD_FILTER_TRIGGER_STRING = 'substitutions.TRIGGER_NAME='; +const CLOUDBUILD_FILTER_TRIGGER_ANNOTATION = + 'google.com/cloudbuild-trigger-name'; + +/** @public */ + +export const getCloudbuildFilter = (entity: Entity) => { + const repoAnnotation = + entity?.metadata.annotations?.[CLOUDBUILD_FILTER_REPO_ANNOTATION] ?? ''; + const triggerAnnotation = + entity?.metadata.annotations?.[CLOUDBUILD_FILTER_TRIGGER_ANNOTATION] ?? ''; + if (repoAnnotation) { + const cloudbuildFilter = CLOUDBUILD_FILTER_REPO_STRING + repoAnnotation; + return cloudbuildFilter; + } else if (triggerAnnotation) { + const cloudbuildFilter = + CLOUDBUILD_FILTER_TRIGGER_STRING + triggerAnnotation; + return cloudbuildFilter; + } + const entityName = entity?.metadata.name ?? ''; + const cloudbuildFilter = CLOUDBUILD_FILTER_REPO_STRING + entityName; + return cloudbuildFilter; +}; diff --git a/plugins/cloudbuild/src/components/useWorkflowRuns.ts b/plugins/cloudbuild/src/components/useWorkflowRuns.ts index 424f08e38d..86168018d4 100644 --- a/plugins/cloudbuild/src/components/useWorkflowRuns.ts +++ b/plugins/cloudbuild/src/components/useWorkflowRuns.ts @@ -33,8 +33,11 @@ export type WorkflowRun = { rerun: () => void; }; -export function useWorkflowRuns(options: { projectId: string }) { - const { projectId } = options; +export function useWorkflowRuns(options: { + projectId: string; + cloudBuildFilter: string; +}) { + const { projectId, cloudBuildFilter } = options; const api = useApi(cloudbuildApiRef); const errorApi = useApi(errorApiRef); @@ -51,6 +54,7 @@ export function useWorkflowRuns(options: { projectId: string }) { return api .listWorkflowRuns({ projectId, + cloudBuildFilter, }) .then( ( From 31474decad26c08499a72915ea4ec74cbae9c013 Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 12:41:14 -0800 Subject: [PATCH 008/152] Update ReadMe Signed-off-by: PurseChicken --- plugins/cloudbuild/README.md | 48 +++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/plugins/cloudbuild/README.md b/plugins/cloudbuild/README.md index 07999c85a1..edb0d69e5d 100644 --- a/plugins/cloudbuild/README.md +++ b/plugins/cloudbuild/README.md @@ -1,4 +1,4 @@ -# Google Cloud Build +# Google Cloud Build Plugin ### Welcome to the Google Cloud Build plugin! @@ -47,7 +47,7 @@ const cicdContent = ( ##### OPTIONAL -If you don't use GitHub Actions, or don't want to show it on your CI/CD page, then you can remove the switch case for it +If you don't use GitHub Actions, or don't want to show it on your CI/CD page, then you can remove the switch case for it: ```diff // packages/app/src/components/catalog/EntityPage.tsx @@ -62,9 +62,9 @@ const cicdContent = ( - ``` -### Add annotation to your component-info.yaml file. +### Add annotation(s) to your component-info.yaml file -Any component, that you would like the Cloud Build Plugin to populate for, should include the following annotation: +Any component, that you would like the Cloud Build Plugin to populate for, should include the following `cloudbuild-project-slug` annotation. This annotation sets the GCP project name to be used for pulling the cloud build details from. ```diff // component-info.yaml @@ -79,3 +79,43 @@ spec: type: website lifecycle: development ``` + +By default, the cloud build results list is filtered by repository name equal to the name you have set in your component-info.yaml file. This is `metadata.name`. So if your metadata.name is `backstage` then it will only show builds matching the backstage repo name. + +#### Change Filtering + +If you need the page to be filtered on a different repository name, then you can use the following annotation: + +```diff +// component-info.yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage + description: Backstage application. + annotations: + google.com/cloudbuild-project-slug: your-project-name ++ google.com/cloudbuild-repo-name: my-backstage +spec: + type: website + lifecycle: development +``` + +You can also automatically filter the results based on trigger name instead of repository name. To so so, use the following annotation: + +```diff +// component-info.yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage + description: Backstage application. + annotations: + google.com/cloudbuild-project-slug: your-project-name ++ google.com/cloudbuild-trigger-name: my-trigger-name +spec: + type: website + lifecycle: development +``` + +`Note:` The `cloudbuild-repo-name` annotation takes precedence over the `cloudbuild-trigger-name` annotation. So if you happen to use both annotations, cloudbuild-repo-name will be used. It is recommended to use one or the other if required. From c55d501b564ee5ce369629a6e60206cccc26720e Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 12:47:55 -0800 Subject: [PATCH 009/152] Use REF_NAME instead of BRANCH_NAME Signed-off-by: PurseChicken --- plugins/cloudbuild/api-report.md | 2 +- plugins/cloudbuild/src/api/types.ts | 2 +- .../src/components/WorkflowRunDetails/WorkflowRunDetails.tsx | 4 ++-- .../components/WorkflowRunsTable/WorkflowRunsTable.test.tsx | 2 +- .../src/components/WorkflowRunsTable/WorkflowRunsTable.tsx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index f8fbd9ee92..0e152baa0f 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -237,7 +237,7 @@ export interface StorageSource { // @public (undocumented) export interface Substitutions { // (undocumented) - BRANCH_NAME: string; + REF_NAME: string; // (undocumented) COMMIT_SHA: string; // (undocumented) diff --git a/plugins/cloudbuild/src/api/types.ts b/plugins/cloudbuild/src/api/types.ts index 1dd03265bc..b721e8dca1 100644 --- a/plugins/cloudbuild/src/api/types.ts +++ b/plugins/cloudbuild/src/api/types.ts @@ -81,7 +81,7 @@ export interface Options { export interface Substitutions { COMMIT_SHA: string; SHORT_SHA: string; - BRANCH_NAME: string; + REF_NAME: string; REPO_NAME: string; REVISION_ID: string; } diff --git a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index a35d9b6f6c..ee282848bc 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -97,9 +97,9 @@ export const WorkflowRunDetails = (props: { entity: Entity }) => { - Branch + Ref - {details.value?.substitutions.BRANCH_NAME} + {details.value?.substitutions.REF_NAME} diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx index 0152f9d964..92a806bee7 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.test.tsx @@ -35,7 +35,7 @@ describe('', () => { substitutions: { COMMIT_SHA: 'e3adasd2e3adasd2e3adasd2', SHORT_SHA: 'f12j1231', - BRANCH_NAME: 'main', + REF_NAME: 'main', REPO_NAME: 'backstage', REVISION_ID: 'g123123', }, diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index 210a67d0dc..dbefffc617 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -72,7 +72,7 @@ const generatedColumns: TableColumn[] = [ title: 'Ref', render: (row: Partial) => ( - {row.substitutions?.BRANCH_NAME} + {row.substitutions?.REF_NAME} ), }, From 0328d1b6b921d870f2676ea899b541eb97e606b0 Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 13:04:23 -0800 Subject: [PATCH 010/152] Write Changeset Signed-off-by: PurseChicken --- .changeset/tough-drinks-scream.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .changeset/tough-drinks-scream.md diff --git a/.changeset/tough-drinks-scream.md b/.changeset/tough-drinks-scream.md new file mode 100644 index 0000000000..fc174f3158 --- /dev/null +++ b/.changeset/tough-drinks-scream.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-cloudbuild': minor +--- + +Changed build list view to automatically filter repositories based on component-info's metadata.name. +Added `google.com/cloudbuild-repo-name` annotation which allows you to specify a different repository to filter on. +Added `google.com/cloudbuild-trigger-name` annotation which allows you to filter based on a trigger name instead of a repo name. +Updated the ReadMe with information about the filtering and some other minor verbiage updates. +Changed `substitutions.BRANCH_NAME` to `substitutions.REF_NAME` so that the Ref field is populated properly. From fcaa2ce61f6ade93443c52af88006a77c4ab4d83 Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 13:18:08 -0800 Subject: [PATCH 011/152] Fix type in ReadMe Signed-off-by: PurseChicken --- plugins/cloudbuild/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/cloudbuild/README.md b/plugins/cloudbuild/README.md index edb0d69e5d..cde383b8c4 100644 --- a/plugins/cloudbuild/README.md +++ b/plugins/cloudbuild/README.md @@ -101,7 +101,7 @@ spec: lifecycle: development ``` -You can also automatically filter the results based on trigger name instead of repository name. To so so, use the following annotation: +You can also automatically filter the results based on trigger name instead of repository name. To do so, use the following annotation: ```diff // component-info.yaml From 568121143a10db1a54ca6a394be49522de3fd206 Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Fri, 17 Nov 2023 13:25:37 -0800 Subject: [PATCH 012/152] Run build:api-reports Signed-off-by: PurseChicken --- plugins/cloudbuild/api-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index 0e152baa0f..5a8f2ed1db 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -236,11 +236,11 @@ export interface StorageSource { // @public (undocumented) export interface Substitutions { - // (undocumented) - REF_NAME: string; // (undocumented) COMMIT_SHA: string; // (undocumented) + REF_NAME: string; + // (undocumented) REPO_NAME: string; // (undocumented) REVISION_ID: string; From ab8ad0f9830057b7d71fbd44857ac40b03740ed1 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Thu, 23 Nov 2023 10:10:37 +0100 Subject: [PATCH 013/152] Extended test cases to validate the use of additionalScopes Signed-off-by: Daniel Doberenz --- .../src/authenticator.test.ts | 1 + .../auth-backend-module-microsoft-provider/src/module.test.ts | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-microsoft-provider/src/authenticator.test.ts b/plugins/auth-backend-module-microsoft-provider/src/authenticator.test.ts index 35d35c464c..21a60a229b 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/authenticator.test.ts @@ -113,6 +113,7 @@ describe('microsoftAuthenticator', () => { tenantId: 'tenantId', clientId: 'clientId', clientSecret: 'clientSecret', + additionalScopes: ['User.Read.All'], }), }); }); diff --git a/plugins/auth-backend-module-microsoft-provider/src/module.test.ts b/plugins/auth-backend-module-microsoft-provider/src/module.test.ts index 21f9a7e845..2ef92c0b44 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/module.test.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/module.test.ts @@ -38,6 +38,7 @@ describe('authModuleMicrosoftProvider', () => { clientId: 'my-client-id', clientSecret: 'my-client-secret', tenantId: 'my-tenant-id', + additionalScopes: ['User.Read.All'], }, }, }, @@ -66,7 +67,7 @@ describe('authModuleMicrosoftProvider', () => { expect(startUrl.pathname).toBe('/my-tenant-id/oauth2/v2.0/authorize'); expect(Object.fromEntries(startUrl.searchParams)).toEqual({ response_type: 'code', - scope: 'user.read', + scope: 'user.read User.Read.All', client_id: 'my-client-id', redirect_uri: `http://localhost:${server.port()}/api/auth/microsoft/handler/frame`, state: expect.any(String), From 8745a6fa8a908f451902492c064247bcc7879f71 Mon Sep 17 00:00:00 2001 From: PurseChicken Date: Mon, 27 Nov 2023 11:36:48 -0800 Subject: [PATCH 014/152] Update plugin to support different Build locations. Update changeset and ReadMe. Signed-off-by: PurseChicken --- .changeset/tough-drinks-scream.md | 9 ++++-- plugins/cloudbuild/README.md | 21 ++++++++++++- plugins/cloudbuild/api-report.md | 13 +++++++- plugins/cloudbuild/src/api/CloudbuildApi.ts | 4 +++ .../cloudbuild/src/api/CloudbuildClient.ts | 12 ++++++++ .../cloudbuild/src/components/Cards/Cards.tsx | 3 ++ .../WorkflowRunDetails/WorkflowRunDetails.tsx | 4 ++- .../useWorkflowRunsDetails.ts | 3 +- .../WorkflowRunsTable/WorkflowRunsTable.tsx | 4 ++- .../cloudbuild/src/components/useLocation.ts | 30 +++++++++++++++++++ .../src/components/useWorkflowRuns.ts | 5 +++- 11 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 plugins/cloudbuild/src/components/useLocation.ts diff --git a/.changeset/tough-drinks-scream.md b/.changeset/tough-drinks-scream.md index fc174f3158..3d2f154016 100644 --- a/.changeset/tough-drinks-scream.md +++ b/.changeset/tough-drinks-scream.md @@ -2,8 +2,11 @@ '@backstage/plugin-cloudbuild': minor --- -Changed build list view to automatically filter repositories based on component-info's metadata.name. -Added `google.com/cloudbuild-repo-name` annotation which allows you to specify a different repository to filter on. -Added `google.com/cloudbuild-trigger-name` annotation which allows you to filter based on a trigger name instead of a repo name. +Changed build list view to automatically filter builds based on repository name matching component-info's metadata.name. +Added optional `google.com/cloudbuild-repo-name` annotation which allows you to specify a different repository to filter on. +Added optional `google.com/cloudbuild-trigger-name` annotation which allows you to filter based on a trigger name instead of a repo name. Updated the ReadMe with information about the filtering and some other minor verbiage updates. Changed `substitutions.BRANCH_NAME` to `substitutions.REF_NAME` so that the Ref field is populated properly. +Added optional `google.com/cloudbuild-location` annotation which allows you to specify the Cloud Build location of your builds. Default is global scope. +Changed build list view to show builds in a specific location if the location annotation is used. +Updated ReadMe with information about the use of the location filtering. diff --git a/plugins/cloudbuild/README.md b/plugins/cloudbuild/README.md index cde383b8c4..2677d02b13 100644 --- a/plugins/cloudbuild/README.md +++ b/plugins/cloudbuild/README.md @@ -64,7 +64,7 @@ const cicdContent = ( ### Add annotation(s) to your component-info.yaml file -Any component, that you would like the Cloud Build Plugin to populate for, should include the following `cloudbuild-project-slug` annotation. This annotation sets the GCP project name to be used for pulling the cloud build details from. +Any component, that you would like the Cloud Build Plugin to populate for, should include the following `cloudbuild-project-slug` annotation. This annotation sets the GCP project name to be used for pulling the Cloud Build details from. ```diff // component-info.yaml @@ -82,6 +82,8 @@ spec: By default, the cloud build results list is filtered by repository name equal to the name you have set in your component-info.yaml file. This is `metadata.name`. So if your metadata.name is `backstage` then it will only show builds matching the backstage repo name. +Additionally, build results are pulled from the `global` region by default. + #### Change Filtering If you need the page to be filtered on a different repository name, then you can use the following annotation: @@ -119,3 +121,20 @@ spec: ``` `Note:` The `cloudbuild-repo-name` annotation takes precedence over the `cloudbuild-trigger-name` annotation. So if you happen to use both annotations, cloudbuild-repo-name will be used. It is recommended to use one or the other if required. + +If you need to pull Cloud Build results from a location or region other than the global scope, then use the following annotation: + +```diff +// component-info.yaml +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage + description: Backstage application. + annotations: + google.com/cloudbuild-project-slug: your-project-name ++ google.com/cloudbuild-location: us-central1 +spec: + type: website + lifecycle: development +``` diff --git a/plugins/cloudbuild/api-report.md b/plugins/cloudbuild/api-report.md index 5a8f2ed1db..2ce42d5e34 100644 --- a/plugins/cloudbuild/api-report.md +++ b/plugins/cloudbuild/api-report.md @@ -58,18 +58,22 @@ export const CLOUDBUILD_ANNOTATION = 'google.com/cloudbuild-project-slug'; export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + location: string; cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; + location: string; id: string; }) => Promise; getWorkflowRun: (options: { projectId: string; + location: string; id: string; }) => Promise; reRunWorkflow: (options: { projectId: string; + location: string; runId: string; }) => Promise; }; @@ -85,20 +89,27 @@ export class CloudbuildClient implements CloudbuildApi { // (undocumented) getWorkflow(options: { projectId: string; + location: string; id: string; }): Promise; // (undocumented) getWorkflowRun(options: { projectId: string; + location: string; id: string; }): Promise; // (undocumented) listWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }): Promise; // (undocumented) - reRunWorkflow(options: { projectId: string; runId: string }): Promise; + reRunWorkflow(options: { + projectId: string; + location: string; + runId: string; + }): Promise; } // @public (undocumented) diff --git a/plugins/cloudbuild/src/api/CloudbuildApi.ts b/plugins/cloudbuild/src/api/CloudbuildApi.ts index cdcfbab88d..9e2d63fc01 100644 --- a/plugins/cloudbuild/src/api/CloudbuildApi.ts +++ b/plugins/cloudbuild/src/api/CloudbuildApi.ts @@ -29,18 +29,22 @@ export const cloudbuildApiRef = createApiRef({ export type CloudbuildApi = { listWorkflowRuns: (options: { projectId: string; + location: string; cloudBuildFilter: string; }) => Promise; getWorkflow: (options: { projectId: string; + location: string; id: string; }) => Promise; getWorkflowRun: (options: { projectId: string; + location: string; id: string; }) => Promise; reRunWorkflow: (options: { projectId: string; + location: string; runId: string; }) => Promise; }; diff --git a/plugins/cloudbuild/src/api/CloudbuildClient.ts b/plugins/cloudbuild/src/api/CloudbuildClient.ts index b41506f852..3717e3d1db 100644 --- a/plugins/cloudbuild/src/api/CloudbuildClient.ts +++ b/plugins/cloudbuild/src/api/CloudbuildClient.ts @@ -27,11 +27,14 @@ export class CloudbuildClient implements CloudbuildApi { async reRunWorkflow(options: { projectId: string; + location: string; runId: string; }): Promise { await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.runId)}:retry`, { method: 'POST', @@ -45,11 +48,14 @@ export class CloudbuildClient implements CloudbuildApi { async listWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }): Promise { const workflowRuns = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds?filter=${encodeURIComponent(options.cloudBuildFilter)}`, { headers: new Headers({ @@ -67,11 +73,14 @@ export class CloudbuildClient implements CloudbuildApi { async getWorkflow(options: { projectId: string; + location: string; id: string; }): Promise { const workflow = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.id)}`, { headers: new Headers({ @@ -88,11 +97,14 @@ export class CloudbuildClient implements CloudbuildApi { async getWorkflowRun(options: { projectId: string; + location: string; id: string; }): Promise { const workflow = await fetch( `https://cloudbuild.googleapis.com/v1/projects/${encodeURIComponent( options.projectId, + )}/locations/${encodeURIComponent( + options.location, )}/builds/${encodeURIComponent(options.id)}`, { headers: new Headers({ diff --git a/plugins/cloudbuild/src/components/Cards/Cards.tsx b/plugins/cloudbuild/src/components/Cards/Cards.tsx index cd15bae6b9..e1d9199b7e 100644 --- a/plugins/cloudbuild/src/components/Cards/Cards.tsx +++ b/plugins/cloudbuild/src/components/Cards/Cards.tsx @@ -22,6 +22,7 @@ import { WorkflowRunStatus } from '../WorkflowRunStatus'; import { Theme, makeStyles, LinearProgress } from '@material-ui/core'; import ExternalLinkIcon from '@material-ui/icons/Launch'; import { CLOUDBUILD_ANNOTATION } from '../useProjectName'; +import { getLocation } from '../useLocation'; import { getCloudbuildFilter } from '../useCloudBuildFilter'; import { @@ -80,10 +81,12 @@ export const LatestWorkflowRunCard = (props: { branch: string }) => { const { entity } = useEntity(); const errorApi = useApi(errorApiRef); const projectId = entity?.metadata.annotations?.[CLOUDBUILD_ANNOTATION] || ''; + const location = getLocation(entity); const cloudBuildFilter = getCloudbuildFilter(entity); const [{ runs, loading, error }] = useWorkflowRuns({ projectId, + location, cloudBuildFilter, }); const lastRun = runs?.[0] ?? ({} as WorkflowRun); diff --git a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx index ee282848bc..5be2401ae4 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunDetails/WorkflowRunDetails.tsx @@ -35,6 +35,7 @@ import { useProjectName } from '../useProjectName'; import { WorkflowRunStatus } from '../WorkflowRunStatus'; import { useWorkflowRunsDetails } from './useWorkflowRunsDetails'; import { Breadcrumbs, Link, WarningPanel } from '@backstage/core-components'; +import { getLocation } from '../useLocation'; const useStyles = makeStyles(theme => ({ root: { @@ -64,8 +65,9 @@ const useStyles = makeStyles(theme => ({ export const WorkflowRunDetails = (props: { entity: Entity }) => { const { value: projectName, loading, error } = useProjectName(props.entity); const [projectId] = (projectName ?? '/').split('/'); + const location = getLocation(props.entity); - const details = useWorkflowRunsDetails(projectId); + const details = useWorkflowRunsDetails(projectId, location); const classes = useStyles(); if (error) { diff --git a/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts b/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts index 549ab1eaf2..abc146fb6c 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts +++ b/plugins/cloudbuild/src/components/WorkflowRunDetails/useWorkflowRunsDetails.ts @@ -18,13 +18,14 @@ import { cloudbuildApiRef } from '../../api'; import { useApi, useRouteRefParams } from '@backstage/core-plugin-api'; import { buildRouteRef } from '../../routes'; -export const useWorkflowRunsDetails = (projectId: string) => { +export const useWorkflowRunsDetails = (projectId: string, location: string) => { const api = useApi(cloudbuildApiRef); const { id } = useRouteRefParams(buildRouteRef); const details = useAsync(async () => { return projectId ? api.getWorkflowRun({ projectId, + location, id: id, }) : Promise.reject('No projectId provided'); diff --git a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx index dbefffc617..65b5ca149f 100644 --- a/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx +++ b/plugins/cloudbuild/src/components/WorkflowRunsTable/WorkflowRunsTable.tsx @@ -26,6 +26,7 @@ import { buildRouteRef } from '../../routes'; import { DateTime } from 'luxon'; import { Table, TableColumn, Link } from '@backstage/core-components'; import { useRouteRef } from '@backstage/core-plugin-api'; +import { getLocation } from '../useLocation'; import { getCloudbuildFilter } from '../useCloudBuildFilter'; const generatedColumns: TableColumn[] = [ @@ -163,10 +164,11 @@ export const WorkflowRunsTableView = ({ export const WorkflowRunsTable = (props: { entity: Entity }) => { const { value: projectName, loading } = useProjectName(props.entity); const [projectId] = (projectName ?? '/').split('/'); + const location = getLocation(props.entity); const cloudBuildFilter = getCloudbuildFilter(props.entity); - const [tableProps, { retry, setPage, setPageSize }] = useWorkflowRuns({ projectId, + location, cloudBuildFilter, }); diff --git a/plugins/cloudbuild/src/components/useLocation.ts b/plugins/cloudbuild/src/components/useLocation.ts new file mode 100644 index 0000000000..54a3c8f776 --- /dev/null +++ b/plugins/cloudbuild/src/components/useLocation.ts @@ -0,0 +1,30 @@ +/* + * 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'; + +const CLOUDBUILD_LOCATION_ANNOTATION = 'google.com/cloudbuild-location'; + +/** @public */ + +export const getLocation = (entity: Entity) => { + const locationAnnotation = + entity?.metadata.annotations?.[CLOUDBUILD_LOCATION_ANNOTATION] ?? ''; + if (locationAnnotation) { + return locationAnnotation; + } + return 'global'; +}; diff --git a/plugins/cloudbuild/src/components/useWorkflowRuns.ts b/plugins/cloudbuild/src/components/useWorkflowRuns.ts index 86168018d4..7e3e148dd1 100644 --- a/plugins/cloudbuild/src/components/useWorkflowRuns.ts +++ b/plugins/cloudbuild/src/components/useWorkflowRuns.ts @@ -35,9 +35,10 @@ export type WorkflowRun = { export function useWorkflowRuns(options: { projectId: string; + location: string; cloudBuildFilter: string; }) { - const { projectId, cloudBuildFilter } = options; + const { projectId, location, cloudBuildFilter } = options; const api = useApi(cloudbuildApiRef); const errorApi = useApi(errorApiRef); @@ -54,6 +55,7 @@ export function useWorkflowRuns(options: { return api .listWorkflowRuns({ projectId, + location, cloudBuildFilter, }) .then( @@ -69,6 +71,7 @@ export function useWorkflowRuns(options: { try { await api.reRunWorkflow({ projectId, + location, runId: run.id, }); } catch (e) { From 8462a2e3d0b32a72bf4e428158b7243b0e04150a Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Tue, 5 Dec 2023 13:07:03 +0100 Subject: [PATCH 015/152] Use loadash instead of own implementation and fixed documentation. Signed-off-by: Daniel Doberenz --- docs/auth/microsoft/provider.md | 2 +- .../src/authenticator.ts | 4 +- .../src/scopes.test.ts | 50 ------------------- .../src/scopes.ts | 30 ----------- 4 files changed, 3 insertions(+), 83 deletions(-) delete mode 100644 plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts delete mode 100644 plugins/auth-backend-module-microsoft-provider/src/scopes.ts diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index 46da74cc31..245e3ffe75 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -28,7 +28,7 @@ On the **API permissions** tab, click on `Add Permission`, then add the followin - `openid` - `profile` - `User.Read` -- Optional custom permissions you defined in the configuration file +- Optional custom scopes you defined in the app-config.yaml file. Your company may require you to grant [admin consent](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/user-admin-consent-overview) for these permissions. Even if your company doesn't require admin consent, you may wish to do so as it means users don't need to individually consent the first time they access backstage. diff --git a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts index 93058b7eec..1b6eb87fae 100644 --- a/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-microsoft-provider/src/authenticator.ts @@ -21,7 +21,7 @@ import { PassportProfile, } from '@backstage/plugin-auth-node'; import { ExtendedMicrosoftStrategy } from './strategy'; -import { scopeHelper } from './scopes'; +import { union } from 'lodash'; /** @public */ export const microsoftAuthenticator = createOAuthAuthenticator({ @@ -32,7 +32,7 @@ export const microsoftAuthenticator = createOAuthAuthenticator({ const clientSecret = config.getString('clientSecret'); const tenantId = config.getString('tenantId'); const domainHint = config.getOptionalString('domainHint'); - const scope = scopeHelper( + const scope = union( ['user.read'], config.getOptionalStringArray('additionalScopes'), ); diff --git a/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts b/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts deleted file mode 100644 index 412f87a601..0000000000 --- a/plugins/auth-backend-module-microsoft-provider/src/scopes.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { scopeHelper } from './scopes'; - -describe('microsoftScopeHelper', () => { - it('default scope only with undefined additional scopes', () => { - const scopeResult = scopeHelper(['default']); - expect(scopeResult.length).toBe(1); - expect(scopeResult).toEqual(expect.arrayContaining(['default'])); - }); - - it('default scope only with empty additional scopes', () => { - const scopeResult = scopeHelper(['default'], []); - expect(scopeResult.length).toBe(1); - expect(scopeResult).toEqual(expect.arrayContaining(['default'])); - }); - - it('default scope with mutually exclusive additional scopes', () => { - const scopeResult = scopeHelper(['default'], ['secondScope', 'thirdScope']); - expect(scopeResult.length).toBe(3); - expect(scopeResult).toEqual( - expect.arrayContaining(['default', 'secondScope', 'thirdScope']), - ); - }); - - it('default scope with overlapping additional scopes', () => { - const scopeResult = scopeHelper( - ['default', 'secondScope'], - ['default', 'secondScope', 'thirdScope'], - ); - expect(scopeResult.length).toBe(3); - expect(scopeResult).toEqual( - expect.arrayContaining(['default', 'secondScope', 'thirdScope']), - ); - }); -}); diff --git a/plugins/auth-backend-module-microsoft-provider/src/scopes.ts b/plugins/auth-backend-module-microsoft-provider/src/scopes.ts deleted file mode 100644 index f70f69c01f..0000000000 --- a/plugins/auth-backend-module-microsoft-provider/src/scopes.ts +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * Combine two arrays of scopes without duplicates - * @param defaultScopes Default scopes - * @param additionalScopes Optional additional scopes - * @returns List of scopes - * @public - */ -export const scopeHelper = ( - defaultScopes: string[], - additionalScopes?: string[], -): string[] => { - const scope: string[] = defaultScopes.concat(additionalScopes || []); - return scope.filter((value, index) => scope.indexOf(value) === index); -}; From 78c0190eb024b4190f0bb9c6a95bfc8fa32f2b60 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Tue, 5 Dec 2023 13:35:21 +0100 Subject: [PATCH 016/152] Added loadash to plugin package.json. Signed-off-by: Daniel Doberenz --- .../auth-backend-module-microsoft-provider/package.json | 1 + yarn.lock | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 4b94acbbbf..082ca6246b 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -28,6 +28,7 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "jose": "^4.6.0", + "loadash": "^1.0.0", "node-fetch": "^2.6.7", "passport": "^0.6.0", "passport-microsoft": "^1.0.0" diff --git a/yarn.lock b/yarn.lock index 6534e7d908..8a35a2a062 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4994,6 +4994,7 @@ __metadata: "@types/passport-microsoft": ^1.0.0 express: ^4.18.2 jose: ^4.6.0 + loadash: ^1.0.0 msw: ^1.0.0 node-fetch: ^2.6.7 passport: ^0.6.0 @@ -33780,6 +33781,13 @@ __metadata: languageName: node linkType: hard +"loadash@npm:^1.0.0": + version: 1.0.0 + resolution: "loadash@npm:1.0.0" + checksum: 05a105fa9773a7c15963ea783507daa4d488a7a5b782ba77d84dbfaa40b01eb6a3a6cb1a160e67c4af339c7aa9209f9bfb8ef35eefffe98838930f05752ea550 + languageName: node + linkType: hard + "loader-runner@npm:^4.2.0": version: 4.2.0 resolution: "loader-runner@npm:4.2.0" From 17e7834724458c32c69b5434ef347e72c13e354d Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Wed, 6 Dec 2023 06:58:42 +0100 Subject: [PATCH 017/152] Added loadash to plugin package.json the correct way. Signed-off-by: Daniel Doberenz --- package.json | 3 ++- .../auth-backend-module-microsoft-provider/package.json | 1 - yarn.lock | 9 +-------- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index a13b7463f2..a676d2c2d3 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,8 @@ "@backstage/errors": "workspace:^", "@changesets/changelog-github": "^0.4.8", "@manypkg/get-packages": "^1.1.3", - "@useoptic/optic": "^0.50.10" + "@useoptic/optic": "^0.50.10", + "lodash": "^4.17.21" }, "devDependencies": { "@backstage/cli": "workspace:*", diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 082ca6246b..4b94acbbbf 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -28,7 +28,6 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "jose": "^4.6.0", - "loadash": "^1.0.0", "node-fetch": "^2.6.7", "passport": "^0.6.0", "passport-microsoft": "^1.0.0" diff --git a/yarn.lock b/yarn.lock index 8a35a2a062..6c8f6e2b0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4994,7 +4994,6 @@ __metadata: "@types/passport-microsoft": ^1.0.0 express: ^4.18.2 jose: ^4.6.0 - loadash: ^1.0.0 msw: ^1.0.0 node-fetch: ^2.6.7 passport: ^0.6.0 @@ -33781,13 +33780,6 @@ __metadata: languageName: node linkType: hard -"loadash@npm:^1.0.0": - version: 1.0.0 - resolution: "loadash@npm:1.0.0" - checksum: 05a105fa9773a7c15963ea783507daa4d488a7a5b782ba77d84dbfaa40b01eb6a3a6cb1a160e67c4af339c7aa9209f9bfb8ef35eefffe98838930f05752ea550 - languageName: node - linkType: hard - "loader-runner@npm:^4.2.0": version: 4.2.0 resolution: "loader-runner@npm:4.2.0" @@ -41327,6 +41319,7 @@ __metadata: fs-extra: 10.1.0 husky: ^8.0.0 lint-staged: ^15.0.0 + lodash: ^4.17.21 minimist: ^1.2.5 node-gyp: ^10.0.0 prettier: ^2.2.1 From f3b9bcf2c98f1818cdd1fe9d8ae6e9b75be94b5c Mon Sep 17 00:00:00 2001 From: Tzahi Ben Shabat Date: Fri, 8 Dec 2023 11:46:02 -0500 Subject: [PATCH 018/152] Update aws-app-development.yaml Signed-off-by: Tzahi Ben Shabat --- microsite/data/plugins/aws-app-development.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/microsite/data/plugins/aws-app-development.yaml b/microsite/data/plugins/aws-app-development.yaml index 3944bbb4e0..47b40b8b3b 100644 --- a/microsite/data/plugins/aws-app-development.yaml +++ b/microsite/data/plugins/aws-app-development.yaml @@ -1,10 +1,10 @@ --- -title: AWS App Developer Tools +title: OPA on AWS author: Amazon Web Services authorUrl: https://aws.amazon.com/ category: Infrastructure -description: Create and manage AWS Apps within Backstage -documentation: https://github.com/awslabs/app-development-for-backstage-io-on-aws#readme -iconUrl: https://github.com/awslabs/app-development-for-backstage-io-on-aws/blob/main/docs/images/AWS_logo.png?raw=true +description: Orchestrate Platforms and Applications (OPA) allows customers to build and manage AWS Apps & Environments within Backstage +documentation: https://opaonaws.io +iconUrl: https://github.com/awslabs/app-development-for-backstage-io-on-aws/blob/main/website/static/img/white_OPA_text02.png?raw=true npmPackageName: '@aws/plugin-aws-apps-for-backstage' addedDate: '2023-05-10' From ba3a413525a256a17ca21de0a165424b7f039c47 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz <136574802+LichtBlick-PENG-Daniel@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:44:22 +0100 Subject: [PATCH 019/152] Change from minor update to patch Co-authored-by: Philipp Hugenroth Signed-off-by: Daniel Doberenz <136574802+LichtBlick-PENG-Daniel@users.noreply.github.com> --- .changeset/rotten-lemons-cry.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/rotten-lemons-cry.md b/.changeset/rotten-lemons-cry.md index 7664eadf2b..2bcc4c2ea7 100644 --- a/.changeset/rotten-lemons-cry.md +++ b/.changeset/rotten-lemons-cry.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-auth-backend-module-microsoft-provider': minor +'@backstage/plugin-auth-backend-module-microsoft-provider': patch --- Added the possibility to use custom scopes for performing login with Microsoft EntraID. From 131ffdcea34bf2eb18e141bd1141c2c5a8cecea9 Mon Sep 17 00:00:00 2001 From: Pablo Martinez Date: Thu, 14 Dec 2023 15:56:51 -0300 Subject: [PATCH 020/152] Fix position of the ReportIssue component when is displaying at the top of the container Signed-off-by: Pablo Martinez --- .changeset/tame-rockets-sin.md | 5 +++++ .../src/ReportIssue/ReportIssue.tsx | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 .changeset/tame-rockets-sin.md diff --git a/.changeset/tame-rockets-sin.md b/.changeset/tame-rockets-sin.md new file mode 100644 index 0000000000..30c7289871 --- /dev/null +++ b/.changeset/tame-rockets-sin.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs-module-addons-contrib': patch +--- + +Fix position of the ReportIssue component when is displaying at the top of the container. diff --git a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.tsx b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.tsx index c81eacb1ee..a6d8021b9d 100644 --- a/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.tsx +++ b/plugins/techdocs-module-addons-contrib/src/ReportIssue/ReportIssue.tsx @@ -111,8 +111,25 @@ export const ReportIssueAddon = ({ const mainContentPosition = mainContent!.getBoundingClientRect(); const selectionPosition = selection.getRangeAt(0).getBoundingClientRect(); + // Calculating the distance between the selection's top and the main content's top + const distanceFromTop = selectionPosition.top - mainContentPosition.top; + const minDistanceFromTop = 50; + + // Defining a base value for 'top' + let top = distanceFromTop < minDistanceFromTop ? 101 : distanceFromTop - 16; + + // Checking if the main content is off-screen towards the top + if (mainContentPosition.top < 0) { + const absMainContentTop = Math.abs(mainContentPosition.top); + + // Adjusting 'top' if the selection is close to the top edge and the main content is off-screen + if (distanceFromTop - absMainContentTop < minDistanceFromTop) { + top += 89; + } + } + setStyle({ - top: `${selectionPosition.top - mainContentPosition.top - 16}px`, + top: `${top}px`, left: `${selectionPosition.left + selectionPosition.width / 2}px`, }); // eslint-disable-next-line react-hooks/exhaustive-deps From 14e01166323e55d78536c64a97f0c4fa18832843 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Tue, 19 Dec 2023 06:48:06 +0100 Subject: [PATCH 021/152] Installed loadash to plugin Signed-off-by: Daniel Doberenz --- plugins/auth-backend-module-microsoft-provider/package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 4b94acbbbf..027d112c0d 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -30,7 +30,8 @@ "jose": "^4.6.0", "node-fetch": "^2.6.7", "passport": "^0.6.0", - "passport-microsoft": "^1.0.0" + "passport-microsoft": "^1.0.0", + "lodash": "^4.17.21" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", From 133858966bc960f75d0287dcbf5427363c5c8f4b Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Tue, 19 Dec 2023 06:56:13 +0100 Subject: [PATCH 022/152] Specified allowed scopes in the documentation Signed-off-by: Daniel Doberenz --- docs/auth/microsoft/provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/auth/microsoft/provider.md b/docs/auth/microsoft/provider.md index 245e3ffe75..bb8a7db6e4 100644 --- a/docs/auth/microsoft/provider.md +++ b/docs/auth/microsoft/provider.md @@ -28,7 +28,7 @@ On the **API permissions** tab, click on `Add Permission`, then add the followin - `openid` - `profile` - `User.Read` -- Optional custom scopes you defined in the app-config.yaml file. +- Optional custom scopes of the `Microsoft Graph` API defined in the app-config.yaml file. Your company may require you to grant [admin consent](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/user-admin-consent-overview) for these permissions. Even if your company doesn't require admin consent, you may wish to do so as it means users don't need to individually consent the first time they access backstage. From 797a32953b68191e0882d0cac1cc721739277a15 Mon Sep 17 00:00:00 2001 From: Aizat Faiz Date: Sun, 24 Dec 2023 17:51:24 +0800 Subject: [PATCH 023/152] ux: use consistent icons for sytem and template Signed-off-by: Aizat Faiz --- .changeset/loud-walls-compare.md | 5 +++++ packages/app-defaults/src/defaults/icons.tsx | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 .changeset/loud-walls-compare.md diff --git a/.changeset/loud-walls-compare.md b/.changeset/loud-walls-compare.md new file mode 100644 index 0000000000..3616006675 --- /dev/null +++ b/.changeset/loud-walls-compare.md @@ -0,0 +1,5 @@ +--- +'@backstage/app-defaults': patch +--- + +Fixed inconsistencies in icons used for System and Template diff --git a/packages/app-defaults/src/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx index fbdfb14e5b..9f83b01988 100644 --- a/packages/app-defaults/src/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -17,7 +17,7 @@ import { IconComponent } from '@backstage/core-plugin-api'; import MuiApartmentIcon from '@material-ui/icons/Apartment'; import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; -import MuiCategoryIcon from '@material-ui/icons/Category'; +import MuiBusinessIcon from '@material-ui/icons/Business'; import MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder'; import MuiSubjectIcon from '@material-ui/icons/Subject'; import MuiSearchIcon from '@material-ui/icons/Search'; @@ -35,7 +35,7 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; import MuiWorkIcon from '@material-ui/icons/Work'; -import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList'; +import MuiLibraryAddIcon from '@material-ui/icons/LibraryAdd'; export const icons = { brokenImage: MuiBrokenImageIcon as IconComponent, @@ -56,10 +56,10 @@ export const icons = { 'kind:domain': MuiApartmentIcon as IconComponent, 'kind:group': MuiPeopleIcon as IconComponent, 'kind:location': MuiLocationOnIcon as IconComponent, - 'kind:system': MuiCategoryIcon as IconComponent, + 'kind:system': MuiBusinessIcon as IconComponent, 'kind:user': MuiPersonIcon as IconComponent, 'kind:resource': MuiWorkIcon as IconComponent, - 'kind:template': MuiFeaturedPlayListIcon as IconComponent, + 'kind:template': MuiLibraryAddIcon as IconComponent, user: MuiPersonIcon as IconComponent, warning: MuiWarningIcon as IconComponent, }; From f24207eb6c1a1b1dc977bb1b14616d1a05b92de5 Mon Sep 17 00:00:00 2001 From: GoFightNguyen Date: Sun, 24 Dec 2023 22:08:15 +0000 Subject: [PATCH 024/152] catalog: export defaultColumnsFunc Signed-off-by: GoFightNguyen --- plugins/catalog/api-report.md | 3 +++ .../catalog/src/components/CatalogTable/CatalogTable.tsx | 6 +++++- plugins/catalog/src/components/CatalogTable/index.ts | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index bef24b50d8..e2225f5ba6 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -245,6 +245,9 @@ export interface DefaultCatalogPageProps { tableOptions?: TableProps['options']; } +// @public (undocumented) +export const defaultColumnsFunc: CatalogTableColumnsFunc; + // @public export class DefaultEntityPresentationApi implements EntityPresentationApi { static create( diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index 006210bb1d..c68e1b06a6 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -77,7 +77,11 @@ const refCompare = (a: Entity, b: Entity) => { return toRef(a).localeCompare(toRef(b)); }; -const defaultColumnsFunc: CatalogTableColumnsFunc = ({ filters, entities }) => { +/** @public */ +export const defaultColumnsFunc: CatalogTableColumnsFunc = ({ + filters, + entities, +}) => { const showTypeColumn = filters.type === undefined; return [ diff --git a/plugins/catalog/src/components/CatalogTable/index.ts b/plugins/catalog/src/components/CatalogTable/index.ts index f5d1ca1bb7..e1d96a6f8f 100644 --- a/plugins/catalog/src/components/CatalogTable/index.ts +++ b/plugins/catalog/src/components/CatalogTable/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { CatalogTable } from './CatalogTable'; +export { CatalogTable, defaultColumnsFunc } from './CatalogTable'; export type { CatalogTableProps } from './CatalogTable'; export type { CatalogTableRow, CatalogTableColumnsFunc } from './types'; From 9f734489937f13fb6ea080b5001b31c8c87045e4 Mon Sep 17 00:00:00 2001 From: GoFightNguyen Date: Sun, 24 Dec 2023 22:17:42 +0000 Subject: [PATCH 025/152] catalog: rename to defaultCatalogTableColumnsFunc from defaultColumnsFunc Signed-off-by: GoFightNguyen --- plugins/catalog/api-report.md | 2 +- plugins/catalog/src/components/CatalogTable/CatalogTable.tsx | 4 ++-- plugins/catalog/src/components/CatalogTable/index.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index e2225f5ba6..718e974302 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -246,7 +246,7 @@ export interface DefaultCatalogPageProps { } // @public (undocumented) -export const defaultColumnsFunc: CatalogTableColumnsFunc; +export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc; // @public export class DefaultEntityPresentationApi implements EntityPresentationApi { diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index c68e1b06a6..fe79607a0c 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -78,7 +78,7 @@ const refCompare = (a: Entity, b: Entity) => { }; /** @public */ -export const defaultColumnsFunc: CatalogTableColumnsFunc = ({ +export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc = ({ filters, entities, }) => { @@ -126,7 +126,7 @@ export const defaultColumnsFunc: CatalogTableColumnsFunc = ({ /** @public */ export const CatalogTable = (props: CatalogTableProps) => { const { - columns = defaultColumnsFunc, + columns = defaultCatalogTableColumnsFunc, tableOptions, subtitle, emptyContent, diff --git a/plugins/catalog/src/components/CatalogTable/index.ts b/plugins/catalog/src/components/CatalogTable/index.ts index e1d96a6f8f..a33cc4a9b5 100644 --- a/plugins/catalog/src/components/CatalogTable/index.ts +++ b/plugins/catalog/src/components/CatalogTable/index.ts @@ -14,6 +14,6 @@ * limitations under the License. */ -export { CatalogTable, defaultColumnsFunc } from './CatalogTable'; +export { CatalogTable, defaultCatalogTableColumnsFunc } from './CatalogTable'; export type { CatalogTableProps } from './CatalogTable'; export type { CatalogTableRow, CatalogTableColumnsFunc } from './types'; From ebc733e67774e1ddbd94321bc3fd7857f72e123b Mon Sep 17 00:00:00 2001 From: GoFightNguyen Date: Sun, 24 Dec 2023 22:28:52 +0000 Subject: [PATCH 026/152] catalog: extract defaultCatalogTableColumnsFunc to separate file Signed-off-by: GoFightNguyen --- .../components/CatalogTable/CatalogTable.tsx | 47 +------------- .../defaultCatalogTableColumnsFunc.tsx | 65 +++++++++++++++++++ .../src/components/CatalogTable/index.ts | 3 +- 3 files changed, 68 insertions(+), 47 deletions(-) create mode 100644 plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index fe79607a0c..d21cca27c6 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -47,6 +47,7 @@ import React, { ReactNode, useMemo } from 'react'; import { columnFactories } from './columns'; import { CatalogTableColumnsFunc, CatalogTableRow } from './types'; import { PaginatedCatalogTable } from './PaginatedCatalogTable'; +import { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc'; /** * Props for {@link CatalogTable}. @@ -77,52 +78,6 @@ const refCompare = (a: Entity, b: Entity) => { return toRef(a).localeCompare(toRef(b)); }; -/** @public */ -export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc = ({ - filters, - entities, -}) => { - const showTypeColumn = filters.type === undefined; - - return [ - columnFactories.createTitleColumn({ hidden: true }), - columnFactories.createNameColumn({ defaultKind: filters.kind?.value }), - ...createEntitySpecificColumns(), - columnFactories.createMetadataDescriptionColumn(), - columnFactories.createTagsColumn(), - ]; - - function createEntitySpecificColumns(): TableColumn[] { - const baseColumns = [ - columnFactories.createSystemColumn(), - columnFactories.createOwnerColumn(), - columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), - columnFactories.createSpecLifecycleColumn(), - ]; - switch (filters.kind?.value) { - case 'user': - return []; - case 'domain': - case 'system': - return [columnFactories.createOwnerColumn()]; - case 'group': - case 'template': - return [ - columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), - ]; - case 'location': - return [ - columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), - columnFactories.createSpecTargetsColumn(), - ]; - default: - return entities.every(entity => entity.metadata.namespace === 'default') - ? baseColumns - : [...baseColumns, columnFactories.createNamespaceColumn()]; - } - } -}; - /** @public */ export const CatalogTable = (props: CatalogTableProps) => { const { diff --git a/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx b/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx new file mode 100644 index 0000000000..5948dedee6 --- /dev/null +++ b/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx @@ -0,0 +1,65 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { TableColumn } from '@backstage/core-components'; +import { columnFactories } from './columns'; +import { CatalogTableColumnsFunc, CatalogTableRow } from './types'; + +/** @public */ +export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc = ({ + filters, + entities, +}) => { + const showTypeColumn = filters.type === undefined; + + return [ + columnFactories.createTitleColumn({ hidden: true }), + columnFactories.createNameColumn({ defaultKind: filters.kind?.value }), + ...createEntitySpecificColumns(), + columnFactories.createMetadataDescriptionColumn(), + columnFactories.createTagsColumn(), + ]; + + function createEntitySpecificColumns(): TableColumn[] { + const baseColumns = [ + columnFactories.createSystemColumn(), + columnFactories.createOwnerColumn(), + columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), + columnFactories.createSpecLifecycleColumn(), + ]; + switch (filters.kind?.value) { + case 'user': + return []; + case 'domain': + case 'system': + return [columnFactories.createOwnerColumn()]; + case 'group': + case 'template': + return [ + columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), + ]; + case 'location': + return [ + columnFactories.createSpecTypeColumn({ hidden: !showTypeColumn }), + columnFactories.createSpecTargetsColumn(), + ]; + default: + return entities.every(entity => entity.metadata.namespace === 'default') + ? baseColumns + : [...baseColumns, columnFactories.createNamespaceColumn()]; + } + } +}; diff --git a/plugins/catalog/src/components/CatalogTable/index.ts b/plugins/catalog/src/components/CatalogTable/index.ts index a33cc4a9b5..d69ed0b3a2 100644 --- a/plugins/catalog/src/components/CatalogTable/index.ts +++ b/plugins/catalog/src/components/CatalogTable/index.ts @@ -14,6 +14,7 @@ * limitations under the License. */ -export { CatalogTable, defaultCatalogTableColumnsFunc } from './CatalogTable'; +export { CatalogTable } from './CatalogTable'; +export { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc'; export type { CatalogTableProps } from './CatalogTable'; export type { CatalogTableRow, CatalogTableColumnsFunc } from './types'; From e541c0ec551cee11c12278ea865830f5c29c5f5a Mon Sep 17 00:00:00 2001 From: GoFightNguyen Date: Sun, 24 Dec 2023 23:25:19 +0000 Subject: [PATCH 027/152] catalog: changeset Signed-off-by: GoFightNguyen --- .changeset/seven-dots-serve.md | 35 ++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 .changeset/seven-dots-serve.md diff --git a/.changeset/seven-dots-serve.md b/.changeset/seven-dots-serve.md new file mode 100644 index 0000000000..79f8f62abb --- /dev/null +++ b/.changeset/seven-dots-serve.md @@ -0,0 +1,35 @@ +--- +'@backstage/plugin-catalog': minor +--- + +Exported `defaultCatalogTableColumnsFunc` to create a seam for defining the columns in [CatalogTable](https://github.com/backstage/backstage/blob/master/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx) of some Kinds while using the default columns for the others. +This is useful for defining the columns of a custom Kind or to redefine the columns for a built-in Kind. + +```diff +// packages/app/src/App.tsx + +import { + CatalogEntityPage, + CatalogIndexPage, + catalogPlugin, ++ CatalogTable, ++ CatalogTableColumnsFunc, ++ defaultCatalogTableColumnsFunc, +} from '@backstage/plugin-catalog'; + ++ const myColumnsFunc: CatalogTableColumnsFunc = (entityListContext) => { ++ if (entityListContext.filters.kind?.value === 'MyKind') { ++ return [ ++ CatalogTable.columns.createNameColumn(), ++ CatalogTable.columns.createOwnerColumn() ++ ]; ++ } ++ ++ return defaultCatalogTableColumnsFunc(entityListContext); ++ }; + +... + +- } /> ++ } /> +``` From 93ce28dfa3d153942d7bc0c8dbc24052eb7f5844 Mon Sep 17 00:00:00 2001 From: Jason Nguyen Date: Sun, 24 Dec 2023 17:10:39 -0700 Subject: [PATCH 028/152] remove relative URL from changeset Signed-off-by: Jason Nguyen --- .changeset/seven-dots-serve.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/seven-dots-serve.md b/.changeset/seven-dots-serve.md index 79f8f62abb..1518fb20cc 100644 --- a/.changeset/seven-dots-serve.md +++ b/.changeset/seven-dots-serve.md @@ -2,7 +2,7 @@ '@backstage/plugin-catalog': minor --- -Exported `defaultCatalogTableColumnsFunc` to create a seam for defining the columns in [CatalogTable](https://github.com/backstage/backstage/blob/master/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx) of some Kinds while using the default columns for the others. +Exported `defaultCatalogTableColumnsFunc` to create a seam for defining the columns in `` of some Kinds while using the default columns for the others. This is useful for defining the columns of a custom Kind or to redefine the columns for a built-in Kind. ```diff From b37b31ec8a4358ee3d3741226fffa842b1da9e23 Mon Sep 17 00:00:00 2001 From: Aizat Faiz Date: Mon, 25 Dec 2023 08:18:52 +0800 Subject: [PATCH 029/152] switch icons Signed-off-by: Aizat Faiz --- packages/app-defaults/src/defaults/icons.tsx | 8 ++++---- .../catalog/src/apis/EntityPresentationApi/defaults.tsx | 8 ++++---- plugins/catalog/src/components/AboutCard/AboutCard.tsx | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/app-defaults/src/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx index 9f83b01988..fbdfb14e5b 100644 --- a/packages/app-defaults/src/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -17,7 +17,7 @@ import { IconComponent } from '@backstage/core-plugin-api'; import MuiApartmentIcon from '@material-ui/icons/Apartment'; import MuiBrokenImageIcon from '@material-ui/icons/BrokenImage'; -import MuiBusinessIcon from '@material-ui/icons/Business'; +import MuiCategoryIcon from '@material-ui/icons/Category'; import MuiCreateNewFolderIcon from '@material-ui/icons/CreateNewFolder'; import MuiSubjectIcon from '@material-ui/icons/Subject'; import MuiSearchIcon from '@material-ui/icons/Search'; @@ -35,7 +35,7 @@ import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; import MuiWorkIcon from '@material-ui/icons/Work'; -import MuiLibraryAddIcon from '@material-ui/icons/LibraryAdd'; +import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList'; export const icons = { brokenImage: MuiBrokenImageIcon as IconComponent, @@ -56,10 +56,10 @@ export const icons = { 'kind:domain': MuiApartmentIcon as IconComponent, 'kind:group': MuiPeopleIcon as IconComponent, 'kind:location': MuiLocationOnIcon as IconComponent, - 'kind:system': MuiBusinessIcon as IconComponent, + 'kind:system': MuiCategoryIcon as IconComponent, 'kind:user': MuiPersonIcon as IconComponent, 'kind:resource': MuiWorkIcon as IconComponent, - 'kind:template': MuiLibraryAddIcon as IconComponent, + 'kind:template': MuiFeaturedPlayListIcon as IconComponent, user: MuiPersonIcon as IconComponent, warning: MuiWarningIcon as IconComponent, }; diff --git a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx index 84f04c6f83..c2abeb5e6a 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx +++ b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx @@ -18,10 +18,10 @@ import { IconComponent } from '@backstage/core-plugin-api'; import { defaultEntityPresentation } from '@backstage/plugin-catalog-react'; import { HumanDuration } from '@backstage/types'; import ApartmentIcon from '@material-ui/icons/Apartment'; -import BusinessIcon from '@material-ui/icons/Business'; +import CategoryIcon from '@material-ui/icons/Category'; import ExtensionIcon from '@material-ui/icons/Extension'; import HelpIcon from '@material-ui/icons/Help'; -import LibraryAddIcon from '@material-ui/icons/LibraryAdd'; +import FeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList'; import LocationOnIcon from '@material-ui/icons/LocationOn'; import MemoryIcon from '@material-ui/icons/Memory'; import PeopleIcon from '@material-ui/icons/People'; @@ -38,13 +38,13 @@ export const UNKNOWN_KIND_ICON: IconComponent = HelpIcon; export const DEFAULT_ICONS: Record = { api: ExtensionIcon, component: MemoryIcon, - system: BusinessIcon, + system: CategoryIcon, resource: WorkIcon, domain: ApartmentIcon, location: LocationOnIcon, user: PersonIcon, group: PeopleIcon, - template: LibraryAddIcon, + template: FeaturedPlayListIcon, }; export function createDefaultRenderer(options: { diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 763f6fc094..2bbbc20760 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -166,7 +166,7 @@ export function AboutCard(props: AboutCardProps) { const subHeaderLinks = [viewInSource, viewInTechDocs]; if (isTemplateEntityV1beta3(entity)) { - const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon; + const Icon = app.getSystemIcon('kind:template') ?? CreateComponentIcon; const launchTemplate: IconLinkVerticalProps = { label: 'Launch Template', From 7db77f9487244c1c27f805d998cb66c80e3860e6 Mon Sep 17 00:00:00 2001 From: GoFightNguyen Date: Tue, 26 Dec 2023 21:09:38 +0000 Subject: [PATCH 030/152] catalog: export defaultCatalogTableColumnsFunc through CatalogTable.defaultColumnsFunc instead of directly Signed-off-by: GoFightNguyen --- .changeset/seven-dots-serve.md | 5 ++--- plugins/catalog/api-report.md | 4 +--- plugins/catalog/src/components/CatalogTable/CatalogTable.tsx | 1 + .../CatalogTable/defaultCatalogTableColumnsFunc.tsx | 2 ++ plugins/catalog/src/components/CatalogTable/index.ts | 1 - 5 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.changeset/seven-dots-serve.md b/.changeset/seven-dots-serve.md index 1518fb20cc..f48ab1f19c 100644 --- a/.changeset/seven-dots-serve.md +++ b/.changeset/seven-dots-serve.md @@ -2,7 +2,7 @@ '@backstage/plugin-catalog': minor --- -Exported `defaultCatalogTableColumnsFunc` to create a seam for defining the columns in `` of some Kinds while using the default columns for the others. +Exported `CatalogTable.defaultColumnsFunc` to create a seam for defining the columns in `` of some Kinds while using the default columns for the others. This is useful for defining the columns of a custom Kind or to redefine the columns for a built-in Kind. ```diff @@ -14,7 +14,6 @@ import { catalogPlugin, + CatalogTable, + CatalogTableColumnsFunc, -+ defaultCatalogTableColumnsFunc, } from '@backstage/plugin-catalog'; + const myColumnsFunc: CatalogTableColumnsFunc = (entityListContext) => { @@ -25,7 +24,7 @@ import { + ]; + } + -+ return defaultCatalogTableColumnsFunc(entityListContext); ++ return CatalogTable.defaultColumnsFunc(entityListContext); + }; ... diff --git a/plugins/catalog/api-report.md b/plugins/catalog/api-report.md index 718e974302..feda3fd70d 100644 --- a/plugins/catalog/api-report.md +++ b/plugins/catalog/api-report.md @@ -182,6 +182,7 @@ export const CatalogTable: { ): TableColumn; createNamespaceColumn(): TableColumn; }>; + defaultColumnsFunc: CatalogTableColumnsFunc; }; // @public @@ -245,9 +246,6 @@ export interface DefaultCatalogPageProps { tableOptions?: TableProps['options']; } -// @public (undocumented) -export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc; - // @public export class DefaultEntityPresentationApi implements EntityPresentationApi { static create( diff --git a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx index d21cca27c6..7da12d7138 100644 --- a/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx +++ b/plugins/catalog/src/components/CatalogTable/CatalogTable.tsx @@ -226,6 +226,7 @@ export const CatalogTable = (props: CatalogTableProps) => { }; CatalogTable.columns = columnFactories; +CatalogTable.defaultColumnsFunc = defaultCatalogTableColumnsFunc; function toEntityRow(entity: Entity) { const partOfSystemRelations = getEntityRelations(entity, RELATION_PART_OF, { diff --git a/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx b/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx index 5948dedee6..85f44d3f51 100644 --- a/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx +++ b/plugins/catalog/src/components/CatalogTable/defaultCatalogTableColumnsFunc.tsx @@ -18,6 +18,8 @@ import { TableColumn } from '@backstage/core-components'; import { columnFactories } from './columns'; import { CatalogTableColumnsFunc, CatalogTableRow } from './types'; +// The defaultCatalogTableColumnsFunc symbol is not directly exported, but through the +// CatalogTable.defaultColumnsFunc field. /** @public */ export const defaultCatalogTableColumnsFunc: CatalogTableColumnsFunc = ({ filters, diff --git a/plugins/catalog/src/components/CatalogTable/index.ts b/plugins/catalog/src/components/CatalogTable/index.ts index d69ed0b3a2..f5d1ca1bb7 100644 --- a/plugins/catalog/src/components/CatalogTable/index.ts +++ b/plugins/catalog/src/components/CatalogTable/index.ts @@ -15,6 +15,5 @@ */ export { CatalogTable } from './CatalogTable'; -export { defaultCatalogTableColumnsFunc } from './defaultCatalogTableColumnsFunc'; export type { CatalogTableProps } from './CatalogTable'; export type { CatalogTableRow, CatalogTableColumnsFunc } from './types'; From 976612c68af75ac445143b3056660a183f0607bf Mon Sep 17 00:00:00 2001 From: Aizat Faiz Date: Wed, 27 Dec 2023 10:20:05 +0800 Subject: [PATCH 031/152] change impacted package Signed-off-by: Aizat Faiz --- .changeset/loud-walls-compare.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/loud-walls-compare.md b/.changeset/loud-walls-compare.md index 3616006675..495ad933d9 100644 --- a/.changeset/loud-walls-compare.md +++ b/.changeset/loud-walls-compare.md @@ -1,5 +1,5 @@ --- -'@backstage/app-defaults': patch +'@backstage/plugin-catalog': patch --- Fixed inconsistencies in icons used for System and Template From ae05256e11b2af474e84112f52e4331042710c2e Mon Sep 17 00:00:00 2001 From: Aditya Singhal Date: Sat, 30 Dec 2023 04:38:03 +0530 Subject: [PATCH 032/152] chore: add github codespces plugin to microsite Signed-off-by: Aditya Singhal --- microsite/data/plugins/github-codespaces.yaml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 microsite/data/plugins/github-codespaces.yaml diff --git a/microsite/data/plugins/github-codespaces.yaml b/microsite/data/plugins/github-codespaces.yaml new file mode 100644 index 0000000000..f6a59888e7 --- /dev/null +++ b/microsite/data/plugins/github-codespaces.yaml @@ -0,0 +1,15 @@ +--- +title: GitHub Codespaces +author: Aditya Singhal +authorUrl: https://github.com/adityasinghal26 +category: Development +description: Integrates GitHub Codespaces for a Backstage component with the Authenticated User. +documentation: https://github.com/adityasinghal26/backstage-plugins/tree/main/plugins/github-codespaces +iconUrl: https://github.com/adityasinghal26/backstage-plugins/blob/00c9c00ba9acc3135014d6454ccf04f573195eef/plugins/github-codespaces/images/GitHubLogo.png +npmPackageName: '@adityasinghal26/plugin-github-codespaces' +tags: + - github + - codespaces + - development + - devcontainers +addedDate: '2023-12-30' \ No newline at end of file From d099a3badf103ad1d52d825046c3c836151f3c79 Mon Sep 17 00:00:00 2001 From: Aditya Singhal Date: Sat, 30 Dec 2023 18:58:58 +0530 Subject: [PATCH 033/152] fix: remove extra whitespace Signed-off-by: Aditya Singhal --- microsite/data/plugins/github-codespaces.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/github-codespaces.yaml b/microsite/data/plugins/github-codespaces.yaml index f6a59888e7..e4480cf4e2 100644 --- a/microsite/data/plugins/github-codespaces.yaml +++ b/microsite/data/plugins/github-codespaces.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/adityasinghal26 category: Development description: Integrates GitHub Codespaces for a Backstage component with the Authenticated User. documentation: https://github.com/adityasinghal26/backstage-plugins/tree/main/plugins/github-codespaces -iconUrl: https://github.com/adityasinghal26/backstage-plugins/blob/00c9c00ba9acc3135014d6454ccf04f573195eef/plugins/github-codespaces/images/GitHubLogo.png +iconUrl: https://github.com/adityasinghal26/backstage-plugins/blob/00c9c00ba9acc3135014d6454ccf04f573195eef/plugins/github-codespaces/images/GitHubLogo.png npmPackageName: '@adityasinghal26/plugin-github-codespaces' tags: - github From 71c6d7a0de7e50fa047ffae6942abc6b984ed798 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Tue, 2 Jan 2024 13:30:47 +0100 Subject: [PATCH 034/152] Add a fix for overflowing labels in owner picker in catalog + hover feature Signed-off-by: Navya Govind --- .changeset/empty-ligers-hang.md | 5 +++ .../EntityOwnerPicker/EntityOwnerPicker.tsx | 37 ++++++++++++------- 2 files changed, 29 insertions(+), 13 deletions(-) create mode 100644 .changeset/empty-ligers-hang.md diff --git a/.changeset/empty-ligers-hang.md b/.changeset/empty-ligers-hang.md new file mode 100644 index 0000000000..c17609712a --- /dev/null +++ b/.changeset/empty-ligers-hang.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-react': patch +--- + +Overflowing labels in OwnerPicker (Catalog) are now truncated. Hovering over them shows the full label diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index ed22e126c5..1b6773662e 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -26,6 +26,7 @@ import { TextField, Typography, makeStyles, + Tooltip, } from '@material-ui/core'; import CheckBoxIcon from '@material-ui/icons/CheckBox'; import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank'; @@ -161,7 +162,10 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { filterOptions={x => x} renderOption={(entity, { selected }) => { const isGroup = entity.kind.toLocaleLowerCase('en-US') === 'group'; - + const humanizedName = humanizeEntity( + entity, + humanizeEntityRef(entity, { defaultKind: entity.kind }), + ); return ( { } onClick={event => event.preventDefault()} label={ - - {isGroup ? ( - - ) : ( - - )} -   - {humanizeEntity( - entity, - humanizeEntityRef(entity, { defaultKind: entity.kind }), - )} - + + + {isGroup ? ( + + ) : ( + + )} +   + + {humanizedName} + + + } /> ); From 445511769f57c6ab7ee4c3d5c5036c1250c56d38 Mon Sep 17 00:00:00 2001 From: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> Date: Fri, 5 Jan 2024 10:36:44 -0600 Subject: [PATCH 035/152] Update github-apps.md Provide information on troubleshooting a specific error with GitHub Apps integration Signed-off-by: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> --- docs/integrations/github/github-apps.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/integrations/github/github-apps.md b/docs/integrations/github/github-apps.md index cc84704799..495f789f7e 100644 --- a/docs/integrations/github/github-apps.md +++ b/docs/integrations/github/github-apps.md @@ -139,3 +139,13 @@ integration: - `Variables`: `Read & write` (if templates include GitHub Action Repository Variables) - `Secrets`: `Read & write` (if templates include GitHub Action Repository Secrets) - `Environments`: `Read & write` (if templates include GitHub Environments) + + ### Troubleshooting + + `HttpError: This endpoint requires you to be authenticated.` + +This message tends to wrap a `NotFoundError: No app installation found` under the hood, which +is the reuslt of not installing the app in your organization. Even if created via the `backstage-cli` +as a member and app manager of your organization, the app will not automatically install. You +must posses the `Owner` role in the organization to see the `Install` menu under your +app settings, then manually press `Install` to authorize the application. From 5de1455df0de0b6cb828b66501bc4df7aae19d9a Mon Sep 17 00:00:00 2001 From: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> Date: Fri, 5 Jan 2024 10:38:52 -0600 Subject: [PATCH 036/152] Update github-apps.md Fix indentation Signed-off-by: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> --- docs/integrations/github/github-apps.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/integrations/github/github-apps.md b/docs/integrations/github/github-apps.md index 495f789f7e..5e55e638ff 100644 --- a/docs/integrations/github/github-apps.md +++ b/docs/integrations/github/github-apps.md @@ -140,9 +140,9 @@ integration: - `Secrets`: `Read & write` (if templates include GitHub Action Repository Secrets) - `Environments`: `Read & write` (if templates include GitHub Environments) - ### Troubleshooting +### Troubleshooting - `HttpError: This endpoint requires you to be authenticated.` +`HttpError: This endpoint requires you to be authenticated.` This message tends to wrap a `NotFoundError: No app installation found` under the hood, which is the reuslt of not installing the app in your organization. Even if created via the `backstage-cli` From fe30d259228002a725820166d5c5384919b15f2e Mon Sep 17 00:00:00 2001 From: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> Date: Fri, 5 Jan 2024 11:21:30 -0600 Subject: [PATCH 037/152] Update docs/integrations/github/github-apps.md Co-authored-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Signed-off-by: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> --- docs/integrations/github/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/github/github-apps.md b/docs/integrations/github/github-apps.md index 5e55e638ff..bdff95c8e9 100644 --- a/docs/integrations/github/github-apps.md +++ b/docs/integrations/github/github-apps.md @@ -145,7 +145,7 @@ integration: `HttpError: This endpoint requires you to be authenticated.` This message tends to wrap a `NotFoundError: No app installation found` under the hood, which -is the reuslt of not installing the app in your organization. Even if created via the `backstage-cli` +is the result of not installing the app in your organization. Even if created via the `backstage-cli` as a member and app manager of your organization, the app will not automatically install. You must posses the `Owner` role in the organization to see the `Install` menu under your app settings, then manually press `Install` to authorize the application. From cd5114c6a57e081af733268c2532c333db90eeef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Prokopowicz?= Date: Sat, 6 Jan 2024 21:03:56 +0100 Subject: [PATCH 038/152] auth-backend-module-okta-provider: add "additionalScopes" to config schema MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michał Prokopowicz --- .changeset/brown-items-hammer.md | 5 +++++ plugins/auth-backend-module-okta-provider/config.d.ts | 1 + 2 files changed, 6 insertions(+) create mode 100644 .changeset/brown-items-hammer.md diff --git a/.changeset/brown-items-hammer.md b/.changeset/brown-items-hammer.md new file mode 100644 index 0000000000..9d26d4f417 --- /dev/null +++ b/.changeset/brown-items-hammer.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-okta-provider': patch +--- + +Added missing "additionalScopes" option to configuration schema. diff --git a/plugins/auth-backend-module-okta-provider/config.d.ts b/plugins/auth-backend-module-okta-provider/config.d.ts index f047c96739..42688528ee 100644 --- a/plugins/auth-backend-module-okta-provider/config.d.ts +++ b/plugins/auth-backend-module-okta-provider/config.d.ts @@ -29,6 +29,7 @@ export interface Config { authServerId?: string; idp?: string; callbackUrl?: string; + additionalScopes?: string; }; }; }; From cb0afaad2b30e4dbbe98a3317a9fe051f24b8908 Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Tue, 9 Jan 2024 12:31:41 +0000 Subject: [PATCH 039/152] Improved Azure DevOps support for mono-repos Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- .changeset/wicked-elephants-scream.md | 5 ++ .../src/hooks/useBuildRuns.test.tsx | 2 +- .../src/hooks/useGitTags.test.tsx | 2 +- .../src/hooks/usePullRequests.test.tsx | 2 +- .../azure-devops/src/hooks/useReadme.test.tsx | 2 +- .../getAnnotationValuesFromEntity.test.ts | 55 +++++++++++++++++- .../utils/getAnnotationValuesFromEntity.ts | 57 ++++++++++++------- 7 files changed, 98 insertions(+), 27 deletions(-) create mode 100644 .changeset/wicked-elephants-scream.md diff --git a/.changeset/wicked-elephants-scream.md b/.changeset/wicked-elephants-scream.md new file mode 100644 index 0000000000..ab3918afba --- /dev/null +++ b/.changeset/wicked-elephants-scream.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-azure-devops': minor +--- + +Prefer dev.azure.com/build-definition annotation when it is provided, as it is more specific than dev.azure.com/project-repo diff --git a/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx b/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx index 409602eec4..f9415aea55 100644 --- a/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx +++ b/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx @@ -99,7 +99,7 @@ describe('useBuildRuns', () => { renderHook(() => useBuildRuns(entity), { wrapper: Wrapper, }), - ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + ).toThrow('"dev.azure.com" annotations are missing'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/useGitTags.test.tsx b/plugins/azure-devops/src/hooks/useGitTags.test.tsx index 1a5620e16e..fc385697ea 100644 --- a/plugins/azure-devops/src/hooks/useGitTags.test.tsx +++ b/plugins/azure-devops/src/hooks/useGitTags.test.tsx @@ -103,7 +103,7 @@ describe('useGitTags', () => { renderHook(() => useGitTags(entity), { wrapper: Wrapper, }), - ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + ).toThrow('"dev.azure.com" annotations are missing'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/usePullRequests.test.tsx b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx index 3e51c003e6..8fe6c6b312 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.test.tsx +++ b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx @@ -102,7 +102,7 @@ describe('usePullRequests', () => { renderHook(() => usePullRequests(entity), { wrapper: Wrapper, }), - ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + ).toThrow('"dev.azure.com" annotations are missing'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/useReadme.test.tsx b/plugins/azure-devops/src/hooks/useReadme.test.tsx index ffc33c136b..76cf2cf0cf 100644 --- a/plugins/azure-devops/src/hooks/useReadme.test.tsx +++ b/plugins/azure-devops/src/hooks/useReadme.test.tsx @@ -81,7 +81,7 @@ describe('useReadme', () => { renderHook(() => useReadme(entity), { wrapper: Wrapper, }), - ).toThrow('Value for annotation "dev.azure.com/project" was not found'); + ).toThrow('"dev.azure.com" annotations are missing'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts index c13dc526d8..58481ec388 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts @@ -140,7 +140,7 @@ describe('getAnnotationValuesFromEntity', () => { }); describe('with only project annotation', () => { - it('should should throw annotation not found error', () => { + it('should throw annotation not found error', () => { const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', @@ -163,7 +163,7 @@ describe('getAnnotationValuesFromEntity', () => { }); describe('with only build-definition annotation', () => { - it('should should throw annotation not found error', () => { + it('should throw annotation not found error', () => { const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', @@ -381,4 +381,55 @@ describe('getAnnotationValuesFromEntity', () => { ); }); }); + + describe('projectRepo and buildDefinition are provided', () => { + it('should return project, repo and buildDefinition', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/build-definition': 'buildDefinitionName', + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: 'repoName', + definition: 'buildDefinitionName', + host: undefined, + org: undefined, + }); + }); + }); + + describe('project, projectRepo and buildDefinition are provided', () => { + it('should prefer project over project-repo.project and return no repo', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + annotations: { + 'dev.azure.com/project': 'projectName', + 'dev.azure.com/build-definition': 'buildDefinitionName', + 'dev.azure.com/project-repo': 'ignoredProject/repoName', + }, + }, + }; + const values = getAnnotationValuesFromEntity(entity); + expect(values).toEqual({ + project: 'projectName', + repo: undefined, + definition: 'buildDefinitionName', + host: undefined, + org: undefined, + }); + }); + }); }); diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts index 83091fb155..66a9753da4 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts @@ -29,34 +29,49 @@ export function getAnnotationValuesFromEntity(entity: Entity): { host?: string; org?: string; } { - const { host, org } = getHostOrg(entity.metadata.annotations); - - const projectRepoValues = getProjectRepo(entity.metadata.annotations); - if (projectRepoValues.project && projectRepoValues.repo) { - return { - project: projectRepoValues.project, - repo: projectRepoValues.repo, - host, - org, - }; - } - + const hostOrg = getHostOrg(entity.metadata.annotations); + const projectRepo = getProjectRepo(entity.metadata.annotations); const project = entity.metadata.annotations?.[AZURE_DEVOPS_PROJECT_ANNOTATION]; - if (!project) { + const definition = + entity.metadata.annotations?.[AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION]; + + if (definition) { + if (project) { + return { + project, + definition, + ...hostOrg, + }; + } + if (projectRepo.project) { + return { + project: projectRepo.project, + repo: projectRepo.repo, + definition, + ...hostOrg, + }; + } throw new Error( `Value for annotation "${AZURE_DEVOPS_PROJECT_ANNOTATION}" was not found`, ); + } else { + if (projectRepo.project) { + return { + project: projectRepo.project, + repo: projectRepo.repo, + ...hostOrg, + }; + } + + if (project) { + throw new Error( + `Value for annotation "${AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION}" was not found`, + ); + } } - const definition = - entity.metadata.annotations?.[AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION]; - if (!definition) { - throw new Error( - `Value for annotation "${AZURE_DEVOPS_BUILD_DEFINITION_ANNOTATION}" was not found`, - ); - } - return { project, definition, host, org }; + throw new Error('"dev.azure.com" annotations are missing'); } function getProjectRepo(annotations?: Record): { From 25e6212fedcffef69e0873480ac1470b3e456402 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Wed, 10 Jan 2024 14:41:47 +0530 Subject: [PATCH 040/152] new faq draft Signed-off-by: npiyush97 --- docs/faq/FAQ.md | 19 +++++++++ docs/faq/adoption.md | 7 +++ docs/faq/corefeatures.md | 7 +++ docs/faq/deployment.md | 7 +++ docs/faq/framework.md | 7 +++ docs/faq/product-faq.md | 68 +++++++++++++++++++++++++++++ docs/{FAQ.md => faq/technical.md} | 71 ++----------------------------- microsite/sidebars.json | 10 ++++- 8 files changed, 128 insertions(+), 68 deletions(-) create mode 100644 docs/faq/FAQ.md create mode 100644 docs/faq/adoption.md create mode 100644 docs/faq/corefeatures.md create mode 100644 docs/faq/deployment.md create mode 100644 docs/faq/framework.md create mode 100644 docs/faq/product-faq.md rename docs/{FAQ.md => faq/technical.md} (75%) diff --git a/docs/faq/FAQ.md b/docs/faq/FAQ.md new file mode 100644 index 0000000000..4b50a38818 --- /dev/null +++ b/docs/faq/FAQ.md @@ -0,0 +1,19 @@ +--- +id: FAQ +title: FAQ +description: All FAQ related to Questions +--- + +## FAQS + +[Product FAQS](../faq/product-faq.md) + +[Technical FAQS](../faq/technical.md) + +[Adoption FAQS](../faq/adoption.md) + +[Deployment FAQS](../faq/deployment.md) + +[Framework FAQS](../faq/framework.md) + +[Core features FAQS](../faq/corefeatures.md) diff --git a/docs/faq/adoption.md b/docs/faq/adoption.md new file mode 100644 index 0000000000..0939fef069 --- /dev/null +++ b/docs/faq/adoption.md @@ -0,0 +1,7 @@ +--- +id: Adoption FAQs +title: Adoption FAQs +description: All FAQs related to Adoption +--- + +## ADOPTION FAQ diff --git a/docs/faq/corefeatures.md b/docs/faq/corefeatures.md new file mode 100644 index 0000000000..8a46929797 --- /dev/null +++ b/docs/faq/corefeatures.md @@ -0,0 +1,7 @@ +--- +id: CORE FEATURES FAQs +title: Core Features FAQs +description: All FAQs related to Core Features +--- + +## Core Features FAQs diff --git a/docs/faq/deployment.md b/docs/faq/deployment.md new file mode 100644 index 0000000000..a67586bc2a --- /dev/null +++ b/docs/faq/deployment.md @@ -0,0 +1,7 @@ +--- +id: DEPLOYMENT FAQs +title: Deployment FAQs +description: All FAQs related to DEPLOYMENT +--- + +## Deployment FAQs diff --git a/docs/faq/framework.md b/docs/faq/framework.md new file mode 100644 index 0000000000..409a273bfb --- /dev/null +++ b/docs/faq/framework.md @@ -0,0 +1,7 @@ +--- +id: FRAMEWORK FAQs +title: Framework FAQs +description: All FAQ related to FRAMEWORK +--- + +## FRAMEWORK FAQs diff --git a/docs/faq/product-faq.md b/docs/faq/product-faq.md new file mode 100644 index 0000000000..68d8918ad0 --- /dev/null +++ b/docs/faq/product-faq.md @@ -0,0 +1,68 @@ +--- +id: Product FAQs +title: Product FAQs +description: All FAQs related to Product +--- + +## Product FAQs + +### Can we call Backstage something different? So that it fits our company better? + +Yes, Backstage is just a platform for building your own developer portal. We +happen to call our internal version Backstage, as well, as a reference to our +music roots. You can call your version whatever suits your team, company, or +brand. + +### Is Backstage a monitoring platform? + +No, but it can be! Backstage is designed to be a developer portal for all your +infrastructure tooling, services, and documentation. So, it's not a monitoring +platform — but that doesn't mean you can't integrate a monitoring tool into +Backstage by writing [a plugin](#what-is-a-plugin-in-backstage). + +### How is Backstage licensed? + +Backstage was released as open source software by Spotify and is licensed under +[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). + +### Why did we open source Backstage? + +We hope to see Backstage become the infrastructure standard everywhere. When we +saw how much Backstage improved developer experience and productivity +internally, we wanted to share those gains. After all, if Backstage can create +order in an engineering environment as open and diverse as ours, then we're +pretty sure it can create order (and boost productivity) anywhere. To learn +more, read our blog post, +"[What the heck is Backstage anyway?](https://backstage.io/blog/2020/03/18/what-is-backstage)" + +### Will Spotify's internal plugins be open sourced, too? + +Yes, we've already started releasing open source versions of some of the plugins +we use here, and we'll continue to do so. +[Plugins](#what-is-a-plugin-in-backstage) are the building blocks of +functionality in Backstage. We have over 120 plugins inside Spotify — many of +those are specialized for our use, so will remain internal and proprietary to +us. But we estimate that about a third of our existing plugins make good open +source candidates. (And we'll probably end up writing some brand new ones, too.) + +### What's the roadmap for Backstage? + +We envision three phases, which you can learn about in +[our project roadmap](overview/roadmap.md). Even though the open source version +of Backstage is relatively new compared to our internal version, we have already +begun work on various aspects of all three phases. Looking at the +[milestones for active issues](https://github.com/backstage/backstage/milestones) +will also give you a sense of our progress. + +### My company doesn't have thousands of developers or services. Is using Backstage excessive for our needs? + +Not at all! A core reason to adopt Backstage is to standardize how software is +built at your company. It's easier to decide on those standards as a small +company, and grows in importance as the company grows. Backstage sets a +foundation, and an early investment in your infrastructure becomes even more +valuable as you grow. + +### Our company has a strong design language system/brand that we want to incorporate. Does Backstage support this? + +Yes! The Backstage UI is built using Material UI. With the theming capabilities +of Material UI, you are able to adapt the interface to your brand guidelines. diff --git a/docs/FAQ.md b/docs/faq/technical.md similarity index 75% rename from docs/FAQ.md rename to docs/faq/technical.md index 7b935140e6..a79c673799 100644 --- a/docs/FAQ.md +++ b/docs/faq/technical.md @@ -1,73 +1,10 @@ --- -id: FAQ -title: FAQ -description: All FAQ related to Backstage +id: Technical FAQs +title: Technical FAQs +description: All FAQs related to Technical --- -## Product FAQ - -### Can we call Backstage something different? So that it fits our company better? - -Yes, Backstage is just a platform for building your own developer portal. We -happen to call our internal version Backstage, as well, as a reference to our -music roots. You can call your version whatever suits your team, company, or -brand. - -### Is Backstage a monitoring platform? - -No, but it can be! Backstage is designed to be a developer portal for all your -infrastructure tooling, services, and documentation. So, it's not a monitoring -platform — but that doesn't mean you can't integrate a monitoring tool into -Backstage by writing [a plugin](#what-is-a-plugin-in-backstage). - -### How is Backstage licensed? - -Backstage was released as open source software by Spotify and is licensed under -[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0). - -### Why did we open source Backstage? - -We hope to see Backstage become the infrastructure standard everywhere. When we -saw how much Backstage improved developer experience and productivity -internally, we wanted to share those gains. After all, if Backstage can create -order in an engineering environment as open and diverse as ours, then we're -pretty sure it can create order (and boost productivity) anywhere. To learn -more, read our blog post, -"[What the heck is Backstage anyway?](https://backstage.io/blog/2020/03/18/what-is-backstage)" - -### Will Spotify's internal plugins be open sourced, too? - -Yes, we've already started releasing open source versions of some of the plugins -we use here, and we'll continue to do so. -[Plugins](#what-is-a-plugin-in-backstage) are the building blocks of -functionality in Backstage. We have over 120 plugins inside Spotify — many of -those are specialized for our use, so will remain internal and proprietary to -us. But we estimate that about a third of our existing plugins make good open -source candidates. (And we'll probably end up writing some brand new ones, too.) - -### What's the roadmap for Backstage? - -We envision three phases, which you can learn about in -[our project roadmap](overview/roadmap.md). Even though the open source version -of Backstage is relatively new compared to our internal version, we have already -begun work on various aspects of all three phases. Looking at the -[milestones for active issues](https://github.com/backstage/backstage/milestones) -will also give you a sense of our progress. - -### My company doesn't have thousands of developers or services. Is using Backstage excessive for our needs? - -Not at all! A core reason to adopt Backstage is to standardize how software is -built at your company. It's easier to decide on those standards as a small -company, and grows in importance as the company grows. Backstage sets a -foundation, and an early investment in your infrastructure becomes even more -valuable as you grow. - -### Our company has a strong design language system/brand that we want to incorporate. Does Backstage support this? - -Yes! The Backstage UI is built using Material UI. With the theming capabilities -of Material UI, you are able to adapt the interface to your brand guidelines. - -## Technical FAQ +## Technical FAQs ### What technology does Backstage use? diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 5dd5ce93f8..81bd0daec0 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -472,7 +472,15 @@ "architecture-decisions/adrs-adr012", "architecture-decisions/adrs-adr013" ], - "FAQ": ["FAQ"], + "FAQ": [ + "faq/FAQ", + "faq/Product FAQs", + "faq/Technical FAQs", + "faq/Adoption FAQs", + "faq/DEPLOYMENT FAQs", + "faq/FRAMEWORK FAQs", + "faq/CORE FEATURES FAQs" + ], "Accessibility": ["accessibility/index"] } } From 7295dc81193c19bf4312409410f6544397d89282 Mon Sep 17 00:00:00 2001 From: Erik Minekus Date: Wed, 10 Jan 2024 11:26:52 +0100 Subject: [PATCH 041/152] Fix typo in VisitsStorageApi.ts Signed-off-by: Erik Minekus --- plugins/home/src/api/VisitsStorageApi.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/home/src/api/VisitsStorageApi.ts b/plugins/home/src/api/VisitsStorageApi.ts index c72d4c5d64..a7cd063455 100644 --- a/plugins/home/src/api/VisitsStorageApi.ts +++ b/plugins/home/src/api/VisitsStorageApi.ts @@ -130,16 +130,16 @@ export class VisitsStorageApi implements VisitsApi { } return new Promise((resolve, reject) => { - const subsription = this.storageApi + const subscription = this.storageApi .observe$(storageKey) .subscribe({ next: next => { const visits = next.value ?? []; - subsription.unsubscribe(); + subscription.unsubscribe(); resolve(visits); }, error: err => { - subsription.unsubscribe(); + subscription.unsubscribe(); reject(err); }, }); From 8c9f3a1e19a8f2837875e0947e4e1eee4b1ec436 Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Wed, 10 Jan 2024 14:11:32 +0000 Subject: [PATCH 042/152] Add additional tests around isAzurePipelinesAvailable Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- plugins/azure-devops/src/plugin.test.ts | 82 ++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/plugins/azure-devops/src/plugin.test.ts b/plugins/azure-devops/src/plugin.test.ts index f23639798f..398b19700c 100644 --- a/plugins/azure-devops/src/plugin.test.ts +++ b/plugins/azure-devops/src/plugin.test.ts @@ -13,10 +13,90 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { azureDevOpsPlugin } from './plugin'; +import { Entity } from '@backstage/catalog-model'; +import { azureDevOpsPlugin, isAzurePipelinesAvailable } from './plugin'; describe('azure-devops', () => { it('should export plugin', () => { expect(azureDevOpsPlugin).toBeDefined(); }); + + describe('isAzurePipelinesAvailable', () => { + it('should be true when project-repo annotation is present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + }, + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(true); + }); + + it('should be true when project and build-definition annotation is present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + annotations: { + 'dev.azure.com/project': 'projectName', + 'dev.azure.com/build-definition': 'buildDefinitionName', + }, + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(true); + }); + + it('should be true when project-repo and build-definition annotation is present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + annotations: { + 'dev.azure.com/project-repo': 'projectName/repoName', + 'dev.azure.com/build-definition': 'buildDefinitionName', + }, + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(true); + }); + + it('should be false when only project annotation is present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + annotations: { + 'dev.azure.com/project': 'projectName', + }, + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(false); + }); + + it('should be false when only build-definition annotation is present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + annotations: { + 'dev.azure.com/build-definition': 'buildDefinitionName', + }, + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(false); + }); + }); }); From c4f43d5cc8996b0f8f884ace0682d1fa3158ff86 Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Thu, 11 Jan 2024 16:06:53 +0000 Subject: [PATCH 043/152] PR Feedback Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- .changeset/wicked-elephants-scream.md | 2 +- plugins/azure-devops/README.md | 19 +++++++++++++++++++ .../src/hooks/useBuildRuns.test.tsx | 2 +- .../src/hooks/useGitTags.test.tsx | 2 +- .../src/hooks/usePullRequests.test.tsx | 2 +- .../azure-devops/src/hooks/useReadme.test.tsx | 2 +- plugins/azure-devops/src/plugin.test.ts | 12 ++++++++++++ .../utils/getAnnotationValuesFromEntity.ts | 2 +- 8 files changed, 37 insertions(+), 6 deletions(-) diff --git a/.changeset/wicked-elephants-scream.md b/.changeset/wicked-elephants-scream.md index ab3918afba..820c8b5d97 100644 --- a/.changeset/wicked-elephants-scream.md +++ b/.changeset/wicked-elephants-scream.md @@ -2,4 +2,4 @@ '@backstage/plugin-azure-devops': minor --- -Prefer dev.azure.com/build-definition annotation when it is provided, as it is more specific than dev.azure.com/project-repo +Prefer `dev.azure.com/build-definition` annotation when it is provided, as it is more specific than `dev.azure.com/project-repo`. This can also be used as a filter for mono-repos. diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index d7335e54c8..f867fcfe75 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -61,6 +61,25 @@ spec: # ... ``` +#### Mono repos + +If you have multiple entities within a single repo, you will need to specify which pipelines belong to each entity. + +```yaml +dev.azure.com/project-repo: / +dev.azure.com/build-definition: +``` + +#### Pipeline in different project to repo + +If your pipeline is in a different project to the source code, you will need to specify this in the project annotation. + +```yaml +dev.azure.com/project-repo: / +dev.azure.com/build-definition: +dev.azure.com/project: +``` + #### Azure Pipelines Only If you are only using Azure Pipelines along with a different SCM tool then you can use the following two annotations to see Builds: diff --git a/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx b/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx index f9415aea55..e165236fe8 100644 --- a/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx +++ b/plugins/azure-devops/src/hooks/useBuildRuns.test.tsx @@ -99,7 +99,7 @@ describe('useBuildRuns', () => { renderHook(() => useBuildRuns(entity), { wrapper: Wrapper, }), - ).toThrow('"dev.azure.com" annotations are missing'); + ).toThrow('Expected "dev.azure.com" annotations were not found'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/useGitTags.test.tsx b/plugins/azure-devops/src/hooks/useGitTags.test.tsx index fc385697ea..1c07b8868e 100644 --- a/plugins/azure-devops/src/hooks/useGitTags.test.tsx +++ b/plugins/azure-devops/src/hooks/useGitTags.test.tsx @@ -103,7 +103,7 @@ describe('useGitTags', () => { renderHook(() => useGitTags(entity), { wrapper: Wrapper, }), - ).toThrow('"dev.azure.com" annotations are missing'); + ).toThrow('Expected "dev.azure.com" annotations were not found'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/usePullRequests.test.tsx b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx index 8fe6c6b312..65579a2adf 100644 --- a/plugins/azure-devops/src/hooks/usePullRequests.test.tsx +++ b/plugins/azure-devops/src/hooks/usePullRequests.test.tsx @@ -102,7 +102,7 @@ describe('usePullRequests', () => { renderHook(() => usePullRequests(entity), { wrapper: Wrapper, }), - ).toThrow('"dev.azure.com" annotations are missing'); + ).toThrow('Expected "dev.azure.com" annotations were not found'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/hooks/useReadme.test.tsx b/plugins/azure-devops/src/hooks/useReadme.test.tsx index 76cf2cf0cf..fdb9080bba 100644 --- a/plugins/azure-devops/src/hooks/useReadme.test.tsx +++ b/plugins/azure-devops/src/hooks/useReadme.test.tsx @@ -81,7 +81,7 @@ describe('useReadme', () => { renderHook(() => useReadme(entity), { wrapper: Wrapper, }), - ).toThrow('"dev.azure.com" annotations are missing'); + ).toThrow('Expected "dev.azure.com" annotations were not found'); }); it('should return throw when annotation invalid', async () => { diff --git a/plugins/azure-devops/src/plugin.test.ts b/plugins/azure-devops/src/plugin.test.ts index 398b19700c..d69fed6bab 100644 --- a/plugins/azure-devops/src/plugin.test.ts +++ b/plugins/azure-devops/src/plugin.test.ts @@ -69,6 +69,18 @@ describe('azure-devops', () => { expect(isAzurePipelinesAvailable(entity)).toBe(true); }); + it('should be false when no annotations are present', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'sample', + }, + }; + expect(isAzurePipelinesAvailable(entity)).toBe(false); + }); + it('should be false when only project annotation is present', () => { const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts index 66a9753da4..7533c473ed 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.ts @@ -71,7 +71,7 @@ export function getAnnotationValuesFromEntity(entity: Entity): { } } - throw new Error('"dev.azure.com" annotations are missing'); + throw new Error('Expected "dev.azure.com" annotations were not found'); } function getProjectRepo(annotations?: Record): { From b45d981c435ab56cade2693ceae5ad5d07c59d0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Prokopowicz?= Date: Thu, 11 Jan 2024 18:36:16 +0100 Subject: [PATCH 044/152] Update .changeset/brown-items-hammer.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Vincenzo Scamporlino Signed-off-by: Michał Prokopowicz --- .changeset/brown-items-hammer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/brown-items-hammer.md b/.changeset/brown-items-hammer.md index 9d26d4f417..fa023a026b 100644 --- a/.changeset/brown-items-hammer.md +++ b/.changeset/brown-items-hammer.md @@ -2,4 +2,4 @@ '@backstage/plugin-auth-backend-module-okta-provider': patch --- -Added missing "additionalScopes" option to configuration schema. +Added missing `additionalScopes` option to configuration schema. From f5c0586b44b1993c2a29585b183f8470898d0ffb Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Thu, 11 Jan 2024 20:13:52 +0000 Subject: [PATCH 045/152] Update .changeset/wicked-elephants-scream.md Co-authored-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com> Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- .changeset/wicked-elephants-scream.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/wicked-elephants-scream.md b/.changeset/wicked-elephants-scream.md index 820c8b5d97..f002a48ba8 100644 --- a/.changeset/wicked-elephants-scream.md +++ b/.changeset/wicked-elephants-scream.md @@ -1,5 +1,5 @@ --- -'@backstage/plugin-azure-devops': minor +'@backstage/plugin-azure-devops': patch --- Prefer `dev.azure.com/build-definition` annotation when it is provided, as it is more specific than `dev.azure.com/project-repo`. This can also be used as a filter for mono-repos. From a3f1fa30f5edd174a9c3145639fc6410fc3e610b Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Fri, 12 Jan 2024 01:14:22 +0100 Subject: [PATCH 046/152] auth-backend: use externalized microsoft auth implementation again This reverts commit 96c4f54bf6070db12676e9af0bf75d0d479c3d72. PR #20706 fixed the issues that required the revert of the implementation. Relates-to: PR #20706 Relates-to: PR #20732 Relates-to: PR #20734 Relates-to: PR #20120 Relates-to: PR #22184 Signed-off-by: Patrick Jungermann --- .changeset/dull-fireants-repeat.md | 5 + plugins/auth-backend/api-report.md | 6 +- plugins/auth-backend/config.d.ts | 12 - plugins/auth-backend/package.json | 1 + .../microsoft/__testUtils__/fake.test.ts | 90 ---- .../providers/microsoft/__testUtils__/fake.ts | 126 ----- .../src/providers/microsoft/provider.test.ts | 450 ------------------ .../src/providers/microsoft/provider.ts | 303 ++---------- yarn.lock | 3 +- 9 files changed, 39 insertions(+), 957 deletions(-) create mode 100644 .changeset/dull-fireants-repeat.md delete mode 100644 plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts delete mode 100644 plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts delete mode 100644 plugins/auth-backend/src/providers/microsoft/provider.test.ts diff --git a/.changeset/dull-fireants-repeat.md b/.changeset/dull-fireants-repeat.md new file mode 100644 index 0000000000..4e849bfbaf --- /dev/null +++ b/.changeset/dull-fireants-repeat.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Use the externalized `auth-backend-module-microsoft-provider` again. diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index d80648a10f..f6d0716c13 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -531,9 +531,9 @@ export const providers: Readonly<{ | undefined, ) => AuthProviderFactory_2; resolvers: Readonly<{ - emailLocalPartMatchingUserEntityName: () => SignInResolver; - emailMatchingUserEntityProfileEmail: () => SignInResolver; - emailMatchingUserEntityAnnotation(): SignInResolver; + emailMatchingUserEntityProfileEmail: () => SignInResolver_2; + emailLocalPartMatchingUserEntityName: () => SignInResolver_2; + emailMatchingUserEntityAnnotation: () => SignInResolver_2; }>; }>; oauth2: Readonly<{ diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index 34139593d3..44848bf653 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -180,18 +180,6 @@ export interface Config { }; }; /** @visibility frontend */ - microsoft?: { - [authEnv: string]: { - clientId: string; - /** - * @visibility secret - */ - clientSecret: string; - tenantId: string; - callbackUrl?: string; - }; - }; - /** @visibility frontend */ onelogin?: { [authEnv: string]: { clientId: string; diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index f05551736a..0cc72db917 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -43,6 +43,7 @@ "@backstage/plugin-auth-backend-module-github-provider": "workspace:^", "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^", "@backstage/plugin-auth-backend-module-google-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-microsoft-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^", "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^", diff --git a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts deleted file mode 100644 index dee37f0f6b..0000000000 --- a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.test.ts +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { FakeMicrosoftAPI } from './fake'; - -describe('FakeMicrosoftAPI', () => { - const api = new FakeMicrosoftAPI(); - - describe('#token', () => { - it('exchanges auth codes', () => { - const { access_token } = api.token( - new URLSearchParams({ - grant_type: 'authorization_code', - code: api.generateAuthCode('User.Read'), - }), - ); - - expect(api.tokenHasScope(access_token, 'User.Read')).toBe(true); - }); - - it('supports scopes for the first requested audience only', () => { - const { access_token } = api.token( - new URLSearchParams({ - grant_type: 'authorization_code', - code: api.generateAuthCode('someaudience/somescope User.Read'), - }), - ); - - expect(api.tokenHasScope(access_token, 'User.Read')).toBe(false); - }); - - it('special openid scopes do not count towards the 1-audience limit', () => { - const { access_token } = api.token( - new URLSearchParams({ - grant_type: 'authorization_code', - code: api.generateAuthCode('openid offline_access User.Read'), - }), - ); - - expect(api.tokenHasScope(access_token, 'User.Read')).toBe(true); - }); - - it('refreshes tokens', () => { - const { access_token } = api.token( - new URLSearchParams({ - grant_type: 'refresh_token', - refresh_token: api.generateRefreshToken( - 'email openid profile User.Read', - ), - }), - ); - - expect( - api.tokenHasScope(access_token, 'email openid profile User.Read'), - ).toBe(true); - }); - it('requires `openid` scope for ID token', () => { - const { id_token } = api.token( - new URLSearchParams({ - grant_type: 'authorization_code', - code: api.generateAuthCode('User.Read'), - }), - ); - - expect(id_token).toBeUndefined(); - }); - it('requires `offline_access` scope for refresh token', () => { - const { refresh_token } = api.token( - new URLSearchParams({ - grant_type: 'authorization_code', - code: api.generateAuthCode('User.Read'), - }), - ); - - expect(refresh_token).toBeUndefined(); - }); - }); -}); diff --git a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts b/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts deleted file mode 100644 index 9b3ca09c57..0000000000 --- a/plugins/auth-backend/src/providers/microsoft/__testUtils__/fake.ts +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { decodeJwt } from 'jose'; - -type Claims = { aud: string; scp: string }; - -export class FakeMicrosoftAPI { - generateAccessToken(scope: string): string { - return this.tokenWithClaims(this.allClaimsForScope(scope)).access_token; - } - generateAuthCode(scope: string): string { - return this.encodeClaims(this.allClaimsForScope(scope)); - } - generateRefreshToken(scope: string): string { - return this.encodeClaims(this.allClaimsForScope(scope)); - } - token(formData: URLSearchParams): { - access_token: string; - scope: string; - refresh_token?: string; - id_token?: string; - } { - const scopeParameter = formData.get('scope'); - const claims = - (scopeParameter && this.allClaimsForScope(scopeParameter)) ?? - formData.get('grant_type') === 'refresh_token' - ? this.decodeClaims(formData.get('refresh_token')!) - : this.decodeClaims(formData.get('code')!); - return { - ...this.tokenWithClaims(claims), - ...(this.hasScope(claims, 'offline_access') && { - refresh_token: this.encodeClaims(claims), - }), - ...(this.hasScope(claims, 'openid') && { - id_token: 'header.e30K.microsoft', - }), - }; - } - tokenHasScope(token: string, scope: string): boolean { - const { aud, scp } = decodeJwt(token); - return this.hasScope({ aud: aud as string, scp: scp as string }, scope); - } - private tokenWithClaims(claims: Claims): { - access_token: string; - scope: string; - } { - const filteredClaims = { - ...claims, - scp: claims.scp - .split(' ') - .filter(s => s !== 'offline_access') - .join(' '), - }; - return { - access_token: `header.${Buffer.from( - JSON.stringify(filteredClaims), - ).toString('base64')}.signature`, - scope: this.scopeFromClaims(filteredClaims), - }; - } - private allClaimsForScope(scope: string): Claims { - const scopes = scope.split(' ').map(this.parseScope); - const firstAudience = scopes - .map(({ aud }) => aud) - .find(aud => aud !== 'openid'); - return { - aud: firstAudience ?? '00000003-0000-0000-c000-000000000000', - scp: scopes - .filter(({ aud }) => aud === 'openid' || aud === firstAudience) - .map(({ scp }) => scp) - .join(' '), - }; - } - // auth codes and refresh tokens in this fake system are base64-encoded JSON - // strings of claims - private encodeClaims(claims: Claims): string { - return Buffer.from(JSON.stringify(claims)).toString('base64'); - } - private decodeClaims(encoded: string): Claims { - return JSON.parse(Buffer.from(encoded, 'base64').toString()); - } - private hasScope(claims: Claims, scope: string): boolean { - return this.scopeFromClaims(claims).includes(scope); - } - private parseScope(s: string): Claims { - if (s.includes('/')) { - const [aud, scp] = s.split('/'); - return { aud, scp }; - } - switch (s) { - case 'email': - case 'openid': - case 'offline_access': - case 'profile': { - return { aud: 'openid', scp: s }; - } - default: - return { aud: '00000003-0000-0000-c000-000000000000', scp: s }; - } - } - private scopeFromClaims(claims: Claims): string { - return claims.scp - .split(' ') - .map(this.parseScope) - .map(({ aud, scp }) => - aud === 'openid' || - claims.aud === '00000003-0000-0000-c000-000000000000' - ? scp - : `${claims.aud}/${scp}`, - ) - .join(' '); - } -} diff --git a/plugins/auth-backend/src/providers/microsoft/provider.test.ts b/plugins/auth-backend/src/providers/microsoft/provider.test.ts deleted file mode 100644 index 7bbfd085d6..0000000000 --- a/plugins/auth-backend/src/providers/microsoft/provider.test.ts +++ /dev/null @@ -1,450 +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 { microsoft } from './provider'; -import { getVoidLogger } from '@backstage/backend-common'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; -import { ConfigReader } from '@backstage/config'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { AuthProviderRouteHandlers, AuthResolverContext } from '../types'; -import express from 'express'; -import crypto from 'crypto'; -import { FakeMicrosoftAPI } from './__testUtils__/fake'; - -describe('MicrosoftAuthProvider', () => { - const nonce = 'AAAAAAAAAAAAAAAAAAAAAA=='; // 16 bytes of zeros in base64 - const state = Buffer.from( - `nonce=${encodeURIComponent(nonce)}&env=development`, - ).toString('hex'); - const mockBackstageToken = `header.${Buffer.from( - JSON.stringify({ sub: 'user:default/mock' }), - 'utf8', - ).toString('base64')}.backstage`; - - const server = setupServer(); - const microsoftApi = new FakeMicrosoftAPI(); - let provider: AuthProviderRouteHandlers; - let response: jest.Mocked; - - setupRequestMockHandlers(server); - - beforeEach(() => { - provider = microsoft.create({ - signIn: { - resolver: microsoft.resolvers.emailMatchingUserEntityAnnotation(), - }, - })({ - providerId: 'microsoft', - baseUrl: 'http://backstage.test/api/auth', - appUrl: 'http://backstage.test', - isOriginAllowed: _ => true, - globalConfig: { - baseUrl: 'http://backstage.test/api/auth', - appUrl: 'http://backstage.test', - isOriginAllowed: _ => true, - }, - config: new ConfigReader({ - development: { - tenantId: 'tenantId', - clientId: 'clientId', - clientSecret: 'clientSecret', - }, - }), - logger: getVoidLogger(), - resolverContext: { - issueToken: jest.fn(), - findCatalogUser: jest.fn(), - signInWithCatalogUser: async _ => ({ - token: mockBackstageToken, - }), - } as AuthResolverContext, - }) as AuthProviderRouteHandlers; - - server.use( - rest.post( - 'https://login.microsoftonline.com/tenantId/oauth2/v2.0/token', - async (req, res, ctx) => { - return res( - ctx.json({ - ...microsoftApi.token(new URLSearchParams(await req.text())), - token_type: 'Bearer', - expires_in: 123, - ext_expires_in: 123, - }), - ); - }, - ), - rest.get('https://graph.microsoft.com/v1.0/me/', (req, res, ctx) => { - if ( - !microsoftApi.tokenHasScope( - req.headers.get('authorization')!.replace(/^Bearer /, ''), - 'User.Read', - ) - ) { - return res(ctx.status(403)); - } - return res( - ctx.json({ - id: 'conrad', - displayName: 'Conrad', - surname: 'Ribas', - givenName: 'Francisco', - mail: 'conrad@example.com', - }), - ); - }), - rest.get( - 'https://graph.microsoft.com/v1.0/me/photos/*', - async (req, res, ctx) => { - if ( - !microsoftApi.tokenHasScope( - req.headers.get('authorization')!.replace(/^Bearer /, ''), - 'User.Read', - ) - ) { - return res(ctx.status(403)); - } - const imageBuffer = new Uint8Array([104, 111, 119, 100, 121]).buffer; - return res( - ctx.set('Content-Length', imageBuffer.byteLength.toString()), - ctx.set('Content-Type', 'image/jpeg'), - ctx.body(imageBuffer), - ); - }, - ), - ); - response = { - cookie: jest.fn(), - end: jest.fn(), - json: jest.fn(), - setHeader: jest.fn(), - status: jest.fn(), - } as unknown as jest.Mocked; - response.status.mockReturnValue(response); - }); - - describe('#start', () => { - const randomBytes = jest.spyOn( - crypto, - 'randomBytes', - ) as unknown as jest.MockedFunction<(size: number) => Buffer>; - - afterEach(() => { - randomBytes.mockRestore(); - }); - - it('redirects to authorize URL', async () => { - randomBytes.mockReturnValue(Buffer.from(nonce, 'base64')); - - await provider.start( - { - query: { - env: 'development', - scope: 'email openid profile User.Read', - }, - } as unknown as express.Request, - response, - ); - - expect(response.setHeader).toHaveBeenCalledWith( - 'Location', - 'https://login.microsoftonline.com/tenantId/oauth2/v2.0/authorize' + - '?response_type=code' + - `&redirect_uri=${encodeURIComponent( - 'http://backstage.test/api/auth/microsoft/handler/frame', - )}` + - `&scope=${encodeURIComponent('email openid profile User.Read')}` + - `&state=${state}` + - '&client_id=clientId', - ); - }); - }); - - describe('#handle', () => { - it('returns provider info and profile with photo data', async () => { - await provider.frameHandler( - { - query: { - env: 'development', - code: microsoftApi.generateAuthCode( - 'email openid profile User.Read', - ), - state, - }, - cookies: { - 'microsoft-nonce': nonce, - }, - } as unknown as express.Request, - response, - ); - - expect(response.end).toHaveBeenCalledWith( - expect.stringContaining( - encodeURIComponent( - JSON.stringify({ - type: 'authorization_response', - response: { - providerInfo: { - accessToken: microsoftApi.generateAccessToken( - 'email openid profile User.Read', - ), - scope: 'email openid profile User.Read', - expiresInSeconds: 123, - idToken: 'header.e30K.microsoft', - }, - profile: { - email: 'conrad@example.com', - picture: 'data:image/jpeg;base64,aG93ZHk=', - displayName: 'Conrad', - }, - backstageIdentity: { - token: mockBackstageToken, - identity: { - type: 'user', - userEntityRef: 'user:default/mock', - ownershipEntityRefs: [], - }, - }, - }, - }), - ), - ), - ); - }); - - it('returns access token for non-microsoft graph scope', async () => { - await provider.frameHandler( - { - query: { - env: 'development', - code: microsoftApi.generateAuthCode('aks-audience/user.read'), - state, - }, - cookies: { - 'microsoft-nonce': nonce, - }, - } as unknown as express.Request, - response, - ); - - expect(response.end).toHaveBeenCalledWith( - expect.stringContaining( - encodeURIComponent( - JSON.stringify({ - type: 'authorization_response', - response: { - providerInfo: { - accessToken: microsoftApi.generateAccessToken( - 'aks-audience/user.read', - ), - scope: 'aks-audience/user.read', - expiresInSeconds: 123, - }, - profile: {}, - }, - }), - ), - ), - ); - }); - - it('sets refresh token', async () => { - await provider.frameHandler( - { - query: { - env: 'development', - code: microsoftApi.generateAuthCode( - 'email offline_access openid profile User.Read', - ), - state, - }, - cookies: { - 'microsoft-nonce': nonce, - }, - } as unknown as express.Request, - response, - ); - - expect(response.cookie).toHaveBeenCalledWith( - 'microsoft-refresh-token', - microsoftApi.generateRefreshToken( - 'email offline_access openid profile User.Read', - ), - { - domain: 'backstage.test', - httpOnly: true, - maxAge: 86400000000, - path: '/api/auth/microsoft', - sameSite: 'lax', - secure: false, - }, - ); - }); - - it('omits photo data when fetching it fails', async () => { - server.use( - rest.get('https://graph.microsoft.com/v1.0/me/photos/*', (_, res) => - res.networkError('remote hung up'), - ), - ); - - await provider.frameHandler( - { - query: { - env: 'development', - code: microsoftApi.generateAuthCode( - 'email openid profile User.Read', - ), - state, - }, - cookies: { - 'microsoft-nonce': nonce, - }, - } as unknown as express.Request, - response, - ); - - expect(response.end).toHaveBeenCalledWith( - expect.stringContaining( - encodeURIComponent( - JSON.stringify({ - type: 'authorization_response', - response: { - providerInfo: { - accessToken: microsoftApi.generateAccessToken( - 'email openid profile User.Read', - ), - scope: 'email openid profile User.Read', - expiresInSeconds: 123, - idToken: 'header.e30K.microsoft', - }, - profile: { - email: 'conrad@example.com', - displayName: 'Conrad', - }, - backstageIdentity: { - token: mockBackstageToken, - identity: { - type: 'user', - userEntityRef: 'user:default/mock', - ownershipEntityRefs: [], - }, - }, - }, - }), - ), - ), - ); - }); - }); - - describe('#refresh', () => { - it('returns provider info and profile with photo data', async () => { - await provider.refresh!( - { - query: { - env: 'development', - scope: 'email openid profile User.Read', - }, - header: jest.fn(_ => 'XMLHttpRequest'), - cookies: { - 'microsoft-refresh-token': microsoftApi.generateRefreshToken( - 'email openid profile User.Read', - ), - }, - get: jest.fn(), - } as unknown as express.Request, - response, - ); - - expect(response.json).toHaveBeenCalledWith( - expect.objectContaining({ - providerInfo: { - accessToken: microsoftApi.generateAccessToken( - 'email openid profile User.Read', - ), - scope: 'email openid profile User.Read', - expiresInSeconds: 123, - idToken: 'header.e30K.microsoft', - }, - profile: { - email: 'conrad@example.com', - picture: 'data:image/jpeg;base64,aG93ZHk=', - displayName: 'Conrad', - }, - }), - ); - }); - - it('returns access token for non-microsoft graph scope', async () => { - await provider.refresh!( - { - query: { - env: 'development', - scope: 'aks-audience/user.read', - }, - header: jest.fn(_ => 'XMLHttpRequest'), - cookies: { - 'microsoft-refresh-token': microsoftApi.generateRefreshToken( - 'aks-audience/user.read', - ), - }, - get: jest.fn(), - } as unknown as express.Request, - response, - ); - - expect(response.json).toHaveBeenCalledWith({ - providerInfo: { - accessToken: microsoftApi.generateAccessToken( - 'aks-audience/user.read', - ), - expiresInSeconds: 123, - scope: 'aks-audience/user.read', - }, - profile: {}, - }); - }); - - it('returns backstage identity', async () => { - await provider.refresh!( - { - query: { - env: 'development', - scope: 'email openid profile User.Read', - }, - header: jest.fn(_ => 'XMLHttpRequest'), - cookies: { - 'microsoft-refresh-token': microsoftApi.generateRefreshToken( - 'email openid profile User.Read', - ), - }, - get: jest.fn(), - } as unknown as express.Request, - response, - ); - - expect(response.json).toHaveBeenCalledWith( - expect.objectContaining({ - backstageIdentity: expect.objectContaining({ - token: mockBackstageToken, - }), - }), - ); - }); - }); -}); diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 8fc8459f10..8947c7432b 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -14,218 +14,25 @@ * limitations under the License. */ -import express from 'express'; -import passport from 'passport'; -import { Strategy as MicrosoftStrategy } from 'passport-microsoft'; -import { - encodeState, - OAuthAdapter, - OAuthEnvironmentHandler, - OAuthHandlers, - OAuthProviderOptions, - OAuthRefreshRequest, - OAuthResponse, - OAuthResult, - OAuthStartRequest, -} from '../../lib/oauth'; -import { - executeFetchUserProfileStrategy, - executeFrameHandlerStrategy, - executeRedirectStrategy, - executeRefreshTokenStrategy, - makeProfileInfo, - PassportDoneCallback, -} from '../../lib/passport'; -import { - AuthHandler, - OAuthStartResponse, - SignInResolver, - AuthResolverContext, -} from '../types'; +import { SignInResolver, AuthHandler } from '../types'; +import { OAuthResult } from '../../lib/oauth'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; import { - commonByEmailLocalPartResolver, - commonByEmailResolver, -} from '../resolvers'; -import { LoggerService } from '@backstage/backend-plugin-api'; -import fetch from 'node-fetch'; -import { decodeJwt } from 'jose'; -import { Profile as PassportProfile } from 'passport'; -import { BACKSTAGE_SESSION_EXPIRATION } from '../../lib/session'; - -type PrivateInfo = { - refreshToken: string; -}; - -type Options = OAuthProviderOptions & { - signInResolver?: SignInResolver; - authHandler: AuthHandler; - logger: LoggerService; - resolverContext: AuthResolverContext; - authorizationUrl?: string; - tokenUrl?: string; -}; - -export class MicrosoftAuthProvider implements OAuthHandlers { - private readonly _strategy: MicrosoftStrategy; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; - private readonly logger: LoggerService; - private readonly resolverContext: AuthResolverContext; - - constructor(options: Options) { - this.signInResolver = options.signInResolver; - this.authHandler = options.authHandler; - this.logger = options.logger; - this.resolverContext = options.resolverContext; - - this._strategy = new MicrosoftStrategy( - { - clientID: options.clientId, - clientSecret: options.clientSecret, - callbackURL: options.callbackUrl, - authorizationURL: options.authorizationUrl, - tokenURL: options.tokenUrl, - passReqToCallback: false, - skipUserProfile: ( - accessToken: string, - done: (err: unknown, skip: boolean) => void, - ) => { - done(null, this.skipUserProfile(accessToken)); - }, - }, - ( - accessToken: any, - refreshToken: any, - params: any, - fullProfile: passport.Profile, - done: PassportDoneCallback, - ) => { - done(undefined, { fullProfile, accessToken, params }, { refreshToken }); - }, - ); - } - - private skipUserProfile = (accessToken: string): boolean => { - const { aud, scp } = decodeJwt(accessToken); - const hasGraphReadScope = - aud === '00000003-0000-0000-c000-000000000000' && - (scp as string) - .split(' ') - .map(s => s.toLowerCase()) - .includes('user.read'); - return !hasGraphReadScope; - }; - - async start(req: OAuthStartRequest): Promise { - return await executeRedirectStrategy(req, this._strategy, { - scope: req.scope, - state: encodeState(req.state), - }); - } - - async handler(req: express.Request) { - const { result, privateInfo } = await executeFrameHandlerStrategy< - OAuthResult, - PrivateInfo - >(req, this._strategy); - - return { - response: await this.handleResult(result), - refreshToken: privateInfo.refreshToken, - }; - } - - async refresh(req: OAuthRefreshRequest) { - const { accessToken, refreshToken, params } = - await executeRefreshTokenStrategy( - this._strategy, - req.refreshToken, - req.scope, - ); - - return { - response: await this.handleResult({ - params, - accessToken, - ...(!this.skipUserProfile(accessToken) && { - fullProfile: await executeFetchUserProfileStrategy( - this._strategy, - accessToken, - ), - }), - }), - refreshToken, - }; - } - - private async handleResult(result: { - fullProfile?: PassportProfile; - params: { - id_token?: string; - scope: string; - expires_in: number; - }; - accessToken: string; - refreshToken?: string; - }): Promise { - let profile = {}; - if (result.fullProfile) { - const photo = await this.getUserPhoto(result.accessToken); - result.fullProfile.photos = photo ? [{ value: photo }] : undefined; - ({ profile } = await this.authHandler( - result as OAuthResult, - this.resolverContext, - )); - } - - const expiresInSeconds = - result.params.expires_in === undefined - ? BACKSTAGE_SESSION_EXPIRATION - : Math.min(result.params.expires_in, BACKSTAGE_SESSION_EXPIRATION); - - return { - providerInfo: { - accessToken: result.accessToken, - scope: result.params.scope, - expiresInSeconds, - ...{ idToken: result.params.id_token }, - }, - profile, - ...(result.fullProfile && - this.signInResolver && { - backstageIdentity: await this.signInResolver( - { result: result as OAuthResult, profile }, - this.resolverContext, - ), - }), - }; - } - - private async getUserPhoto(accessToken: string): Promise { - try { - const res = await fetch( - 'https://graph.microsoft.com/v1.0/me/photos/48x48/$value', - { - headers: { - Authorization: `Bearer ${accessToken}`, - }, - }, - ); - const data = await res.buffer(); - - return `data:image/jpeg;base64,${data.toString('base64')}`; - } catch (error) { - this.logger.warn( - `Could not retrieve user profile photo from Microsoft Graph API: ${error}`, - ); - return undefined; - } - } -} + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { + adaptLegacyOAuthHandler, + adaptLegacyOAuthSignInResolver, + adaptOAuthSignInResolverToLegacy, +} from '../../lib/legacy'; +import { + microsoftAuthenticator, + microsoftSignInResolvers, +} from '@backstage/plugin-auth-backend-module-microsoft-provider'; /** - * Auth provider integration for Microsoft auth + * Auth provider integration for GitLab auth * * @public */ @@ -241,75 +48,21 @@ export const microsoft = createAuthProviderIntegration({ * Configure sign-in for this provider, without it the provider can not be used to sign users in. */ signIn?: { - /** - * Maps an auth result to a Backstage identity for the user. - */ resolver: SignInResolver; }; }) { - return ({ providerId, globalConfig, config, logger, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const tenantId = envConfig.getString('tenantId'); - - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const authorizationUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`; - const tokenUrl = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`; - - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ fullProfile, params }) => ({ - profile: makeProfileInfo(fullProfile ?? {}, params.id_token), - }); - - const provider = new MicrosoftAuthProvider({ - clientId, - clientSecret, - callbackUrl, - authorizationUrl, - tokenUrl, - authHandler, - signInResolver: options?.signIn?.resolver, - logger, - resolverContext, - }); - - return OAuthAdapter.fromConfig(globalConfig, provider, { - providerId, - callbackUrl, - }); - }); - }, - resolvers: { - /** - * Looks up the user by matching their email local part to the entity name. - */ - emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, - /** - * Looks up the user by matching their email to the entity email. - */ - emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, - /** - * Looks up the user by matching their email to the `microsoft.com/email` annotation. - */ - emailMatchingUserEntityAnnotation(): SignInResolver { - return async (info, ctx) => { - const { profile } = info; - - if (!profile.email) { - throw new Error('Microsoft profile contained no email'); - } - - return ctx.signInWithCatalogUser({ - annotations: { - 'microsoft.com/email': profile.email, - }, - }); - }; - }, + return createOAuthProviderFactory({ + authenticator: microsoftAuthenticator, + profileTransform: adaptLegacyOAuthHandler(options?.authHandler), + signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + }); }, + resolvers: adaptOAuthSignInResolverToLegacy({ + emailLocalPartMatchingUserEntityName: + commonSignInResolvers.emailLocalPartMatchingUserEntityName(), + emailMatchingUserEntityProfileEmail: + commonSignInResolvers.emailMatchingUserEntityProfileEmail(), + emailMatchingUserEntityAnnotation: + microsoftSignInResolvers.emailMatchingUserEntityAnnotation(), + }), }); diff --git a/yarn.lock b/yarn.lock index ce3c3512b8..fa69052d87 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4724,7 +4724,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider": +"@backstage/plugin-auth-backend-module-microsoft-provider@workspace:^, @backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-microsoft-provider@workspace:plugins/auth-backend-module-microsoft-provider" dependencies: @@ -4854,6 +4854,7 @@ __metadata: "@backstage/plugin-auth-backend-module-github-provider": "workspace:^" "@backstage/plugin-auth-backend-module-gitlab-provider": "workspace:^" "@backstage/plugin-auth-backend-module-google-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-microsoft-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^" "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^" From 187b30b1ce7e6610ba0816dc9839bb0f6c54d32a Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:43:38 +0000 Subject: [PATCH 047/152] Additional review comments Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- plugins/azure-devops/README.md | 4 ++-- .../utils/getAnnotationValuesFromEntity.test.ts | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/plugins/azure-devops/README.md b/plugins/azure-devops/README.md index f867fcfe75..7119cb0980 100644 --- a/plugins/azure-devops/README.md +++ b/plugins/azure-devops/README.md @@ -75,9 +75,9 @@ dev.azure.com/build-definition: If your pipeline is in a different project to the source code, you will need to specify this in the project annotation. ```yaml -dev.azure.com/project-repo: / +dev.azure.com/project-repo: / dev.azure.com/build-definition: -dev.azure.com/project: +dev.azure.com/project: ``` #### Azure Pipelines Only diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts index 58481ec388..94f179e8fb 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts @@ -18,6 +18,22 @@ import { Entity } from '@backstage/catalog-model'; import { getAnnotationValuesFromEntity } from './getAnnotationValuesFromEntity'; describe('getAnnotationValuesFromEntity', () => { + describe('without any annotations', () => { + it('should return ???', () => { + const entity: Entity = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Component', + metadata: { + namespace: 'default', + name: 'project-repo', + }, + }; + expect(() => getAnnotationValuesFromEntity(entity)).toThrow( + 'Expected "dev.azure.com" annotations were not found', + ); + }); + }); + describe('with valid project-repo annotation', () => { it('should return project and repo', () => { const entity: Entity = { From 9da4c78e628de0918682271f81c348dd408af787 Mon Sep 17 00:00:00 2001 From: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> Date: Fri, 12 Jan 2024 13:48:42 +0000 Subject: [PATCH 048/152] Add missed file Signed-off-by: R-Beck-2020 <78100403+R-Beck-2020@users.noreply.github.com> --- .../src/utils/getAnnotationValuesFromEntity.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts index 94f179e8fb..9325a79039 100644 --- a/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts +++ b/plugins/azure-devops/src/utils/getAnnotationValuesFromEntity.test.ts @@ -19,7 +19,7 @@ import { getAnnotationValuesFromEntity } from './getAnnotationValuesFromEntity'; describe('getAnnotationValuesFromEntity', () => { describe('without any annotations', () => { - it('should return ???', () => { + it('should throw annotations not found', () => { const entity: Entity = { apiVersion: 'backstage.io/v1alpha1', kind: 'Component', From 79d13122add341aa871f5479be3ddeab3020c08f Mon Sep 17 00:00:00 2001 From: Ben Wilcock Date: Fri, 12 Jan 2024 14:12:07 +0000 Subject: [PATCH 049/152] Adding the 'Backchat' plugin to the microsite. Signed-off-by: Ben Wilcock --- microsite/data/plugins/backchat.yaml | 11 +++++++++++ microsite/static/img/backchat-logo.png | Bin 0 -> 237155 bytes 2 files changed, 11 insertions(+) create mode 100644 microsite/data/plugins/backchat.yaml create mode 100644 microsite/static/img/backchat-logo.png diff --git a/microsite/data/plugins/backchat.yaml b/microsite/data/plugins/backchat.yaml new file mode 100644 index 0000000000..d86ab81383 --- /dev/null +++ b/microsite/data/plugins/backchat.yaml @@ -0,0 +1,11 @@ +--- +--- +title: Backchat GenAI +author: benwilcock +authorUrl: https://github.com/benwilcock +category: Services +description: Use open source GenAI privately, without leaving Backstage. Access Ollama, Text Gen Web UI, ChatBot UI or Big AGI UI in your portal. Choose from hundreds of compatible LLMs. Run your inferencing servers wherever you like - local or remote, CPU or GPU - it's up to you! See the GitHub repo for more details. +documentation: https://github.com/benwilcock/backstage-plugin-backchat +iconUrl: /img/backchat-logo.png +npmPackageName: '@benbravo73/backstage-plugin-backchat' +addedDate: '2024-01-12' diff --git a/microsite/static/img/backchat-logo.png b/microsite/static/img/backchat-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..2d589480077ef370be50185748c09a6394eda1e4 GIT binary patch literal 237155 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

-- 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

QgLL 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%0A%2l#dmZcIUN>oV{cM@Cxg4mbL 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

E$w>dh`&s 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 E0KpH%yZ`_I literal 0 HcmV?d00001 From d8cd944ab7f87afda9de19ed3a8618bf3e9f8c2a Mon Sep 17 00:00:00 2001 From: Ben Wilcock Date: Sat, 13 Jan 2024 13:48:20 +0000 Subject: [PATCH 050/152] Corrected a '---' mistake in the listing that was breaking the build. Signed-off-by: Ben Wilcock --- microsite/data/plugins/backchat.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/microsite/data/plugins/backchat.yaml b/microsite/data/plugins/backchat.yaml index d86ab81383..b375ad26bc 100644 --- a/microsite/data/plugins/backchat.yaml +++ b/microsite/data/plugins/backchat.yaml @@ -1,10 +1,9 @@ --- ---- title: Backchat GenAI author: benwilcock authorUrl: https://github.com/benwilcock category: Services -description: Use open source GenAI privately, without leaving Backstage. Access Ollama, Text Gen Web UI, ChatBot UI or Big AGI UI in your portal. Choose from hundreds of compatible LLMs. Run your inferencing servers wherever you like - local or remote, CPU or GPU - it's up to you! See the GitHub repo for more details. +description: Access your favorite open source GenAI GUIs privately from Backstage. Chat wth large language models in your portal. Choose from hundreds of LLMs. Run inferencing wherever you like - local or remote, CPU or GPU - it's up to you! documentation: https://github.com/benwilcock/backstage-plugin-backchat iconUrl: /img/backchat-logo.png npmPackageName: '@benbravo73/backstage-plugin-backchat' From b89d8be689db99c1909910ca72dab72d07856940 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Wed, 27 Dec 2023 12:48:35 -0600 Subject: [PATCH 051/152] Added alpha support for the New Frontend System Signed-off-by: Andre Wanlin --- .changeset/two-singers-learn.md | 5 ++ packages/app-next/src/App.tsx | 3 +- plugins/devtools/api-report-alpha.md | 19 ++++++++ plugins/devtools/package.json | 21 ++++++-- plugins/devtools/src/alpha.ts | 18 +++++++ plugins/devtools/src/alpha/index.ts | 17 +++++++ plugins/devtools/src/alpha/plugin.tsx | 70 +++++++++++++++++++++++++++ yarn.lock | 2 + 8 files changed, 151 insertions(+), 4 deletions(-) create mode 100644 .changeset/two-singers-learn.md create mode 100644 plugins/devtools/api-report-alpha.md create mode 100644 plugins/devtools/src/alpha.ts create mode 100644 plugins/devtools/src/alpha/index.ts create mode 100644 plugins/devtools/src/alpha/plugin.tsx diff --git a/.changeset/two-singers-learn.md b/.changeset/two-singers-learn.md new file mode 100644 index 0000000000..f5c2a78ab0 --- /dev/null +++ b/.changeset/two-singers-learn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-devtools': patch +--- + +Added alpha support for the New Frontend System (Declarative Integration) diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index ec333ce38f..59e0194dc0 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -50,7 +50,7 @@ import { } from '@backstage/integration-react'; import { createSignInPageExtension } from '@backstage/frontend-plugin-api'; import { SignInPage } from '@backstage/core-components'; - +import devToolsPlugin from '@backstage/plugin-devtools/alpha'; import linguistPlugin from '@backstage/plugin-linguist/alpha'; /* @@ -127,6 +127,7 @@ const app = createApp({ userSettingsPlugin, homePlugin, appVisualizerPlugin, + devToolsPlugin, linguistPlugin, ...collectedLegacyPlugins, createExtensionOverrides({ diff --git a/plugins/devtools/api-report-alpha.md b/plugins/devtools/api-report-alpha.md new file mode 100644 index 0000000000..59aefd3698 --- /dev/null +++ b/plugins/devtools/api-report-alpha.md @@ -0,0 +1,19 @@ +## API Report File for "@backstage/plugin-devtools" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackstagePlugin } from '@backstage/frontend-plugin-api'; +import { RouteRef } from '@backstage/frontend-plugin-api'; + +// @alpha (undocumented) +const _default: BackstagePlugin< + { + root: RouteRef; + }, + {} +>; +export default _default; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/devtools/package.json b/plugins/devtools/package.json index 34ce5d7091..ef1f106dc2 100644 --- a/plugins/devtools/package.json +++ b/plugins/devtools/package.json @@ -5,9 +5,22 @@ "types": "src/index.ts", "license": "Apache-2.0", "publishConfig": { - "access": "public", - "main": "dist/index.esm.js", - "types": "dist/index.d.ts" + "access": "public" + }, + "exports": { + ".": "./src/index.ts", + "./alpha": "./src/alpha.ts", + "./package.json": "./package.json" + }, + "typesVersions": { + "*": { + "alpha": [ + "src/alpha.ts" + ], + "package.json": [ + "package.json" + ] + } }, "backstage": { "role": "frontend-plugin" @@ -29,9 +42,11 @@ "postpack": "backstage-cli package postpack" }, "dependencies": { + "@backstage/core-compat-api": "workspace:^", "@backstage/core-components": "workspace:^", "@backstage/core-plugin-api": "workspace:^", "@backstage/errors": "workspace:^", + "@backstage/frontend-plugin-api": "workspace:^", "@backstage/plugin-devtools-common": "workspace:^", "@backstage/plugin-permission-react": "workspace:^", "@material-ui/core": "^4.9.13", diff --git a/plugins/devtools/src/alpha.ts b/plugins/devtools/src/alpha.ts new file mode 100644 index 0000000000..e80f131817 --- /dev/null +++ b/plugins/devtools/src/alpha.ts @@ -0,0 +1,18 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export * from './alpha/index'; +export { default } from './alpha/index'; diff --git a/plugins/devtools/src/alpha/index.ts b/plugins/devtools/src/alpha/index.ts new file mode 100644 index 0000000000..2f137f09ee --- /dev/null +++ b/plugins/devtools/src/alpha/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +export { default } from './plugin'; diff --git a/plugins/devtools/src/alpha/plugin.tsx b/plugins/devtools/src/alpha/plugin.tsx new file mode 100644 index 0000000000..746b90a50f --- /dev/null +++ b/plugins/devtools/src/alpha/plugin.tsx @@ -0,0 +1,70 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import React from 'react'; +import { + createApiExtension, + createApiFactory, + createNavItemExtension, + createPageExtension, + createPlugin, + discoveryApiRef, + identityApiRef, +} from '@backstage/frontend-plugin-api'; + +import { devToolsApiRef, DevToolsClient } from '../api'; +import { + compatWrapper, + convertLegacyRouteRef, +} from '@backstage/core-compat-api'; +import BuildIcon from '@material-ui/icons/Build'; +import { rootRouteRef } from '../routes'; + +/** @alpha */ +export const devToolsApi = createApiExtension({ + factory: createApiFactory({ + api: devToolsApiRef, + deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef }, + factory: ({ discoveryApi, identityApi }) => + new DevToolsClient({ discoveryApi, identityApi }), + }), +}); + +/** @alpha */ +export const devToolsPage = createPageExtension({ + defaultPath: '/devtools', + routeRef: convertLegacyRouteRef(rootRouteRef), + loader: () => + import('../components/DevToolsPage').then(m => + compatWrapper(), + ), +}); + +/** @alpha */ +export const devToolsNavItem = createNavItemExtension({ + title: 'DevTools', + routeRef: convertLegacyRouteRef(rootRouteRef), + icon: BuildIcon, +}); + +/** @alpha */ +export default createPlugin({ + id: 'devtools', + routes: { + root: convertLegacyRouteRef(rootRouteRef), + }, + extensions: [devToolsApi, devToolsPage, devToolsNavItem], +}); diff --git a/yarn.lock b/yarn.lock index 230f6d3e8d..d648f33383 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6298,10 +6298,12 @@ __metadata: resolution: "@backstage/plugin-devtools@workspace:plugins/devtools" dependencies: "@backstage/cli": "workspace:^" + "@backstage/core-compat-api": "workspace:^" "@backstage/core-components": "workspace:^" "@backstage/core-plugin-api": "workspace:^" "@backstage/dev-utils": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/frontend-plugin-api": "workspace:^" "@backstage/plugin-devtools-common": "workspace:^" "@backstage/plugin-permission-react": "workspace:^" "@material-ui/core": ^4.9.13 From 25605cfe78bd78a1fcd43c2aaebe7a0d9e9834d8 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 13 Jan 2024 15:29:05 -0600 Subject: [PATCH 052/152] Removed plugin imports Signed-off-by: Andre Wanlin --- packages/app-next/src/App.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/packages/app-next/src/App.tsx b/packages/app-next/src/App.tsx index 59e0194dc0..03a6649c27 100644 --- a/packages/app-next/src/App.tsx +++ b/packages/app-next/src/App.tsx @@ -50,8 +50,6 @@ import { } from '@backstage/integration-react'; import { createSignInPageExtension } from '@backstage/frontend-plugin-api'; import { SignInPage } from '@backstage/core-components'; -import devToolsPlugin from '@backstage/plugin-devtools/alpha'; -import linguistPlugin from '@backstage/plugin-linguist/alpha'; /* @@ -127,8 +125,6 @@ const app = createApp({ userSettingsPlugin, homePlugin, appVisualizerPlugin, - devToolsPlugin, - linguistPlugin, ...collectedLegacyPlugins, createExtensionOverrides({ extensions: [ From e9cdfd382059e959bc03188c50db7e51a79f4fb2 Mon Sep 17 00:00:00 2001 From: Erik Minekus Date: Sun, 14 Jan 2024 15:41:35 +0100 Subject: [PATCH 053/152] Add changeset Signed-off-by: Erik Minekus --- .changeset/ten-planets-guess.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/ten-planets-guess.md diff --git a/.changeset/ten-planets-guess.md b/.changeset/ten-planets-guess.md new file mode 100644 index 0000000000..c8510dc6b2 --- /dev/null +++ b/.changeset/ten-planets-guess.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-home': patch +--- + +Fix typo in VisitsStorageApi From 1dcaccf5d3b77442cf9e4c8d12970551ba7600d0 Mon Sep 17 00:00:00 2001 From: Daniel Doberenz Date: Mon, 15 Jan 2024 10:09:45 +0100 Subject: [PATCH 054/152] Installed loadash to plugin Signed-off-by: Daniel Doberenz --- plugins/auth-backend-module-microsoft-provider/package.json | 4 ++-- yarn.lock | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/plugins/auth-backend-module-microsoft-provider/package.json b/plugins/auth-backend-module-microsoft-provider/package.json index 027d112c0d..8ed7533463 100644 --- a/plugins/auth-backend-module-microsoft-provider/package.json +++ b/plugins/auth-backend-module-microsoft-provider/package.json @@ -28,10 +28,10 @@ "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", "jose": "^4.6.0", + "lodash": "^4.17.21", "node-fetch": "^2.6.7", "passport": "^0.6.0", - "passport-microsoft": "^1.0.0", - "lodash": "^4.17.21" + "passport-microsoft": "^1.0.0" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", diff --git a/yarn.lock b/yarn.lock index 6c8f6e2b0f..9b0479ea9b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4994,6 +4994,7 @@ __metadata: "@types/passport-microsoft": ^1.0.0 express: ^4.18.2 jose: ^4.6.0 + lodash: ^4.17.21 msw: ^1.0.0 node-fetch: ^2.6.7 passport: ^0.6.0 From b2d71c2bd3a2fd3eeb57541c89aae0502b14eef7 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Mon, 15 Jan 2024 10:53:04 +0100 Subject: [PATCH 055/152] Make overflow dynamic Signed-off-by: Navya Govind --- .../EntityOwnerPicker/EntityOwnerPicker.tsx | 80 ++++++++++++------- 1 file changed, 49 insertions(+), 31 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 1b6773662e..c4311ce896 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -40,6 +40,7 @@ import PersonIcon from '@material-ui/icons/Person'; import GroupIcon from '@material-ui/icons/Group'; import { humanizeEntity, humanizeEntityRef } from '../EntityRefLink/humanize'; import { useFetchEntities } from './useFetchEntities'; +import { withStyles } from '@material-ui/core/styles'; /** @public */ export type CatalogReactEntityOwnerPickerClassKey = 'input'; @@ -53,6 +54,21 @@ const useStyles = makeStyles( }, ); +const FixedWidthFormControlLabel = withStyles( + _theme => ({ + root: { + width: '100%', + }, + label: { + root: { + width: '100%', + }, + width: '100%', + }, + }), + { name: 'FixedWidthFormControlLabel' }, +)(FormControlLabel); + const icon = ; const checkedIcon = ; @@ -154,7 +170,6 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { if (typeof e !== 'string') { cache.setEntity(e); } - return entityRef; }), ); @@ -167,37 +182,40 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { humanizeEntityRef(entity, { defaultKind: entity.kind }), ); return ( - - } - onClick={event => event.preventDefault()} - label={ - - - {isGroup ? ( - - ) : ( - - )} -   - - {humanizedName} + + + } + onClick={event => event.preventDefault()} + label={ + + + {isGroup ? ( + + ) : ( + + )} +   + + {humanizedName} + - - - } - /> + + } + /> + ); }} size="small" From ce9dd1460f5e702a25d8129436e32ef2025765d8 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Mon, 15 Jan 2024 11:30:34 +0100 Subject: [PATCH 056/152] refactor css stuff Signed-off-by: Navya Govind --- .../EntityOwnerPicker/EntityOwnerPicker.tsx | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index c4311ce896..685279b83e 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -48,6 +48,12 @@ export type CatalogReactEntityOwnerPickerClassKey = 'input'; const useStyles = makeStyles( { input: {}, + fullWidth: { width: '100%' }, + boxLabel: { + width: '100%', + textOverflow: 'ellipsis', + overflow: 'hidden', + }, }, { name: 'CatalogReactEntityOwnerPicker', @@ -56,13 +62,7 @@ const useStyles = makeStyles( const FixedWidthFormControlLabel = withStyles( _theme => ({ - root: { - width: '100%', - }, label: { - root: { - width: '100%', - }, width: '100%', }, }), @@ -182,9 +182,9 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { humanizeEntityRef(entity, { defaultKind: entity.kind }), ); return ( - + { )}   - + {humanizedName} From 87ca41827b954ccd9ba3dc1d61bd22663b846737 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Mon, 15 Jan 2024 13:18:22 +0100 Subject: [PATCH 057/152] use presentation api Signed-off-by: Navya Govind --- .../EntityOwnerPicker/EntityOwnerPicker.tsx | 73 ++++++++++--------- 1 file changed, 38 insertions(+), 35 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 685279b83e..6a4a6d2dd0 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -41,6 +41,7 @@ import GroupIcon from '@material-ui/icons/Group'; import { humanizeEntity, humanizeEntityRef } from '../EntityRefLink/humanize'; import { useFetchEntities } from './useFetchEntities'; import { withStyles } from '@material-ui/core/styles'; +import { useEntityPresentation } from '../../apis'; /** @public */ export type CatalogReactEntityOwnerPickerClassKey = 'input'; @@ -79,6 +80,42 @@ export type EntityOwnerPickerProps = { mode?: 'owners-only' | 'all'; }; +function WrappedOption(props: { entity: Entity; isSelected: boolean }) { + const classes = useStyles(); + const isGroup = props.entity.kind.toLocaleLowerCase('en-US') === 'group'; + const { primaryTitle } = useEntityPresentation(props.entity); + return ( + + + } + onClick={event => event.preventDefault()} + label={ + + + {isGroup ? ( + + ) : ( + + )} +   + + {primaryTitle} + + + + } + /> + + ); +} + /** @public */ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { const classes = useStyles(); @@ -176,41 +213,7 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { }} filterOptions={x => x} renderOption={(entity, { selected }) => { - const isGroup = entity.kind.toLocaleLowerCase('en-US') === 'group'; - const humanizedName = humanizeEntity( - entity, - humanizeEntityRef(entity, { defaultKind: entity.kind }), - ); - return ( - - - } - onClick={event => event.preventDefault()} - label={ - - - {isGroup ? ( - - ) : ( - - )} -   - - {humanizedName} - - - - } - /> - - ); + return ; }} size="small" popupIcon={} From 983a93fc4a6317f730e5c0e5a3cdf7adbb1114e6 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Mon, 15 Jan 2024 13:26:47 +0100 Subject: [PATCH 058/152] better naming Signed-off-by: Navya Govind --- .../components/EntityOwnerPicker/EntityOwnerPicker.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 6a4a6d2dd0..8876266db2 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -80,10 +80,10 @@ export type EntityOwnerPickerProps = { mode?: 'owners-only' | 'all'; }; -function WrappedOption(props: { entity: Entity; isSelected: boolean }) { +function RenderOptionLabel(props: { entity: Entity; isSelected: boolean }) { const classes = useStyles(); const isGroup = props.entity.kind.toLocaleLowerCase('en-US') === 'group'; - const { primaryTitle } = useEntityPresentation(props.entity); + const { primaryTitle: title } = useEntityPresentation(props.entity); return ( event.preventDefault()} label={ - + {isGroup ? ( @@ -106,7 +106,7 @@ function WrappedOption(props: { entity: Entity; isSelected: boolean }) { )}   - {primaryTitle} + {title} @@ -213,7 +213,7 @@ export const EntityOwnerPicker = (props?: EntityOwnerPickerProps) => { }} filterOptions={x => x} renderOption={(entity, { selected }) => { - return ; + return ; }} size="small" popupIcon={} From d6415acbaa6be9d6cb04b21368b9e698c3aefb27 Mon Sep 17 00:00:00 2001 From: Gauri Maheshwari <100439627+gauriimaheshwarii@users.noreply.github.com> Date: Tue, 16 Jan 2024 21:38:25 +0530 Subject: [PATCH 059/152] [fix]: export module as a default Signed-off-by: Gauri Maheshwari <100439627+gauriimaheshwarii@users.noreply.github.com> --- packages/cli/templates/default-backend-module/src/index.ts.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/templates/default-backend-module/src/index.ts.hbs b/packages/cli/templates/default-backend-module/src/index.ts.hbs index 0cf1f00d42..7075598e13 100644 --- a/packages/cli/templates/default-backend-module/src/index.ts.hbs +++ b/packages/cli/templates/default-backend-module/src/index.ts.hbs @@ -5,4 +5,4 @@ * @packageDocumentation */ -export { {{moduleVar}} } from './module'; +export { {{moduleVar}} as default } from './module'; From 57c115c86b29eb18d84a70f646996d123f24ba30 Mon Sep 17 00:00:00 2001 From: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> Date: Tue, 16 Jan 2024 14:00:49 -0600 Subject: [PATCH 060/152] Update docs/integrations/github/github-apps.md Co-authored-by: Frank Kong <50030060+Zaperex@users.noreply.github.com> Signed-off-by: Jake Taylor <147415933+jrtaylorJH@users.noreply.github.com> --- docs/integrations/github/github-apps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/integrations/github/github-apps.md b/docs/integrations/github/github-apps.md index bdff95c8e9..96363bc719 100644 --- a/docs/integrations/github/github-apps.md +++ b/docs/integrations/github/github-apps.md @@ -147,5 +147,5 @@ integration: This message tends to wrap a `NotFoundError: No app installation found` under the hood, which is the result of not installing the app in your organization. Even if created via the `backstage-cli` as a member and app manager of your organization, the app will not automatically install. You -must posses the `Owner` role in the organization to see the `Install` menu under your +must possess the `Owner` role in the organization to see the `Install` menu under your app settings, then manually press `Install` to authorize the application. From fd3d51caafbd8fb4caebe8ea793e8c55d6dad4a7 Mon Sep 17 00:00:00 2001 From: Patrick Jungermann Date: Mon, 8 Jan 2024 00:33:46 +0100 Subject: [PATCH 061/152] feat: support new backend system at explore-backend Add the backend plugin `explorePlugin` as default export that registers the router for the explore backend with the `StaticExploreToolProvider` loading its data from the config `explore.tools`. Signed-off-by: Patrick Jungermann --- .changeset/cyan-icons-rest.md | 14 ++++++ plugins/explore-backend/README.md | 30 +++++++++++++ plugins/explore-backend/api-report.md | 5 +++ plugins/explore-backend/package.json | 2 + plugins/explore-backend/src/index.ts | 1 + plugins/explore-backend/src/plugin.test.ts | 50 ++++++++++++++++++++++ plugins/explore-backend/src/plugin.ts | 49 +++++++++++++++++++++ yarn.lock | 2 + 8 files changed, 153 insertions(+) create mode 100644 .changeset/cyan-icons-rest.md create mode 100644 plugins/explore-backend/src/plugin.test.ts create mode 100644 plugins/explore-backend/src/plugin.ts diff --git a/.changeset/cyan-icons-rest.md b/.changeset/cyan-icons-rest.md new file mode 100644 index 0000000000..3092c1bc90 --- /dev/null +++ b/.changeset/cyan-icons-rest.md @@ -0,0 +1,14 @@ +--- +'@backstage/plugin-explore-backend': patch +--- + +Add support for the new backend system. + +A new backend plugin for the explore backend +was added and exported as `default`. + +You can use it with the new backend system like + +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-explore-backend')); +``` diff --git a/plugins/explore-backend/README.md b/plugins/explore-backend/README.md index 543e8e2fa2..aae3ff94b8 100644 --- a/plugins/explore-backend/README.md +++ b/plugins/explore-backend/README.md @@ -9,6 +9,36 @@ for these tools. ### Adding the plugin to your `packages/backend` +Install dependencies + +```bash +# From your Backstage root directory +yarn add --cwd packages/backend @backstage/plugin-explore-backend +``` + +Add feature + +```ts title="packages/backend/src/index.ts" +backend.add(import('@backstage/plugin-explore-backend')); +``` + +Config: + +```yaml +explore: + tools: + - title: New Relic + description: new relic plugin + url: /newrelic + image: https://i.imgur.com/L37ikrX.jpg + tags: + - newrelic + - proxy + - nerdGraph +``` + +### Adding the plugin to your `packages/backend` (old) + #### Tools as Config Install dependencies diff --git a/plugins/explore-backend/api-report.md b/plugins/explore-backend/api-report.md index b652f5515b..05a057fa7b 100644 --- a/plugins/explore-backend/api-report.md +++ b/plugins/explore-backend/api-report.md @@ -3,6 +3,7 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; import { Config } from '@backstage/config'; import { ExploreTool } from '@backstage/plugin-explore-common'; import express from 'express'; @@ -16,6 +17,10 @@ import type { ToolDocumentCollatorFactoryOptions as ToolDocumentCollatorFactoryO // @public (undocumented) export function createRouter(options: RouterOptions): Promise; +// @public +const explorePlugin: () => BackendFeature; +export default explorePlugin; + // @public (undocumented) export interface ExploreToolProvider { getTools(request: GetExploreToolsRequest): Promise; diff --git a/plugins/explore-backend/package.json b/plugins/explore-backend/package.json index 5af891fb02..36d0c2b7af 100644 --- a/plugins/explore-backend/package.json +++ b/plugins/explore-backend/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", "@backstage/plugin-explore-common": "workspace:^", "@backstage/plugin-search-backend-module-explore": "workspace:^", @@ -42,6 +43,7 @@ "yn": "^4.0.0" }, "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", "@types/supertest": "^2.0.8", "supertest": "^6.2.4" diff --git a/plugins/explore-backend/src/index.ts b/plugins/explore-backend/src/index.ts index 15659d6ab6..ae3ffd182b 100644 --- a/plugins/explore-backend/src/index.ts +++ b/plugins/explore-backend/src/index.ts @@ -20,6 +20,7 @@ * @packageDocumentation */ +export { explorePlugin as default } from './plugin'; export * from './service'; export * from './tools'; diff --git a/plugins/explore-backend/src/plugin.test.ts b/plugins/explore-backend/src/plugin.test.ts new file mode 100644 index 0000000000..391c00ba88 --- /dev/null +++ b/plugins/explore-backend/src/plugin.test.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { ExploreTool } from '@backstage/plugin-explore-common'; +import { explorePlugin } from './plugin'; + +describe('explorePlugin', () => { + it('should register explore plugin and its router', async () => { + const tool: ExploreTool = { + title: 'Tool Title', + image: 'https://example.com/image.png', + url: 'https://example.com', + lifecycle: 'production', + tags: ['tag1', 'tag2'], + }; + + const httpRouterMock = mockServices.httpRouter.mock(); + + await startTestBackend({ + extensionPoints: [], + features: [ + explorePlugin(), + httpRouterMock.factory, + mockServices.rootConfig.factory({ + data: { + explore: { + tools: [tool], + }, + }, + }), + ], + }); + + expect(httpRouterMock.use).toHaveBeenCalledTimes(1); + }); +}); diff --git a/plugins/explore-backend/src/plugin.ts b/plugins/explore-backend/src/plugin.ts new file mode 100644 index 0000000000..d81493c85a --- /dev/null +++ b/plugins/explore-backend/src/plugin.ts @@ -0,0 +1,49 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { loggerToWinstonLogger } from '@backstage/backend-common'; +import { + coreServices, + createBackendPlugin, +} from '@backstage/backend-plugin-api'; +import { createRouter } from './service'; +import { StaticExploreToolProvider } from './tools'; + +/** + * The explore backend plugin. + * + * @public + */ +export const explorePlugin = createBackendPlugin({ + pluginId: 'explore', + register(env) { + env.registerInit({ + deps: { + config: coreServices.rootConfig, + httpRouter: coreServices.httpRouter, + logger: coreServices.logger, + }, + async init({ config, httpRouter, logger }) { + httpRouter.use( + await createRouter({ + logger: loggerToWinstonLogger(logger), + toolProvider: StaticExploreToolProvider.fromConfig(config), + }), + ); + }, + }); + }, +}); diff --git a/yarn.lock b/yarn.lock index 30474455e6..6624c3bc82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6412,6 +6412,8 @@ __metadata: resolution: "@backstage/plugin-explore-backend@workspace:plugins/explore-backend" dependencies: "@backstage/backend-common": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" "@backstage/cli": "workspace:^" "@backstage/config": "workspace:^" "@backstage/plugin-explore-common": "workspace:^" From ff2d42806c48914688fa3f09673e41cc847516ea Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Wed, 17 Jan 2024 11:11:02 +0530 Subject: [PATCH 062/152] Updated readme Signed-off-by: AmbrishRamachandiran --- contrib/terraform/techdocs-s3-storage/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/terraform/techdocs-s3-storage/readme.md b/contrib/terraform/techdocs-s3-storage/readme.md index 9615cc8d13..8031c05028 100644 --- a/contrib/terraform/techdocs-s3-storage/readme.md +++ b/contrib/terraform/techdocs-s3-storage/readme.md @@ -1 +1 @@ -This terraform file should create a S3 bucket and setup IAM with a user with an inline policy which gives the user access to the bucket. After you have created the bucket, user and policy you should go to the user in the AWS console and create an access key. This access key should be used as the env variables in step 3a [here](https://backstage.io/docs/features/techdocs/using-cloud-storage#configuring-aws-s3-bucket-with-techdocs). +This Terraform file should build an S3 bucket and configure IAM with a user and an inline policy that grants the user access to the bucket. After you've established the bucket, user, and policy, go to the user's AWS dashboard and generate an access key. This access key should be used to set the environment variables in step 3a [here](https://backstage.io/docs/features/techdocs/using-cloud-storage#configuring-aws-s3-bucket-with-techdocs). From f89777d76f7d2811a2afc6ae2c7853ed6b5c4110 Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Fri, 8 Sep 2023 10:59:17 -0400 Subject: [PATCH 063/152] WIP:Copying gitlab folder into new oidc module Signed-off-by: Ruben Vallejo --- .../.eslintrc.js | 1 + .../README.md | 8 ++ .../catalog-info.yaml | 10 +++ .../config.d.ts | 34 ++++++++ .../dev/index.ts | 26 ++++++ .../package.json | 45 +++++++++++ .../src/authenticator.ts | 77 ++++++++++++++++++ .../src/index.ts | 25 ++++++ .../src/module.test.ts | 79 +++++++++++++++++++ .../src/module.ts | 48 +++++++++++ .../src/resolvers.ts | 50 ++++++++++++ .../src/types.d.ts | 25 ++++++ 12 files changed, 428 insertions(+) create mode 100644 plugins/auth-backend-module-oidc-provider/.eslintrc.js create mode 100644 plugins/auth-backend-module-oidc-provider/README.md create mode 100644 plugins/auth-backend-module-oidc-provider/catalog-info.yaml create mode 100644 plugins/auth-backend-module-oidc-provider/config.d.ts create mode 100644 plugins/auth-backend-module-oidc-provider/dev/index.ts create mode 100644 plugins/auth-backend-module-oidc-provider/package.json create mode 100644 plugins/auth-backend-module-oidc-provider/src/authenticator.ts create mode 100644 plugins/auth-backend-module-oidc-provider/src/index.ts create mode 100644 plugins/auth-backend-module-oidc-provider/src/module.test.ts create mode 100644 plugins/auth-backend-module-oidc-provider/src/module.ts create mode 100644 plugins/auth-backend-module-oidc-provider/src/resolvers.ts create mode 100644 plugins/auth-backend-module-oidc-provider/src/types.d.ts diff --git a/plugins/auth-backend-module-oidc-provider/.eslintrc.js b/plugins/auth-backend-module-oidc-provider/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/auth-backend-module-oidc-provider/README.md b/plugins/auth-backend-module-oidc-provider/README.md new file mode 100644 index 0000000000..968c8a57dd --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/README.md @@ -0,0 +1,8 @@ +# Auth Module: GitLab Provider + +This module provides an GitLab auth provider implementation for `@backstage/plugin-auth-backend`. + +## Links + +- [Repository](https://oidc.com/backstage/backstage/tree/master/plugins/auth-backend-module-oidc-provider) +- [Backstage Project Homepage](https://backstage.io) diff --git a/plugins/auth-backend-module-oidc-provider/catalog-info.yaml b/plugins/auth-backend-module-oidc-provider/catalog-info.yaml new file mode 100644 index 0000000000..896738898b --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-auth-backend-module-oidc-provider + title: '@backstage/plugin-auth-backend-module-oidc-provider' + description: The oidc-provider backend module for the auth plugin. +spec: + lifecycle: experimental + type: backstage-backend-plugin-module + owner: maintainers diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts new file mode 100644 index 0000000000..ba141d9555 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -0,0 +1,34 @@ +/* + * 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. + */ + +export interface Config { + auth?: { + providers?: { + /** @visibility frontend */ + oidc?: { + [authEnv: string]: { + clientId: string; + /** + * @visibility secret + */ + clientSecret: string; + audience?: string; + callbackUrl?: string; + }; + }; + }; + }; +} diff --git a/plugins/auth-backend-module-oidc-provider/dev/index.ts b/plugins/auth-backend-module-oidc-provider/dev/index.ts new file mode 100644 index 0000000000..4d027a19c2 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/dev/index.ts @@ -0,0 +1,26 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { createBackend } from '@backstage/backend-defaults'; +import { authPlugin } from '@backstage/plugin-auth-backend'; +import { authModuleOidcProvider } from '../src'; + +const backend = createBackend(); + +backend.add(authPlugin); +backend.add(authModuleOidcProvider); + +backend.start(); diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json new file mode 100644 index 0000000000..4bb750cd3f --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -0,0 +1,45 @@ +{ + "name": "@backstage/plugin-auth-backend-module-oidc-provider", + "description": "The oidc-provider backend module for the auth plugin.", + "version": "0.1.0-next.1", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "publishConfig": { + "access": "public", + "main": "dist/index.cjs.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "backend-plugin-module" + }, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/backend-common": "workspace:^", + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/plugin-auth-node": "workspace:^", + "express": "^4.18.2", + "passport": "^0.6.0", + "passport-oidc2": "^5.0.0" + }, + "devDependencies": { + "@backstage/backend-defaults": "workspace:^", + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/plugin-auth-backend": "workspace:^", + "supertest": "^6.3.3" + }, + "configSchema": "config.d.ts", + "files": [ + "dist", + "config.d.ts" + ] +} diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts new file mode 100644 index 0000000000..a68b7b269d --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -0,0 +1,77 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { Strategy as GitlabStrategy } from 'passport-oidc2'; +import { + createOAuthAuthenticator, + PassportOAuthAuthenticatorHelper, + PassportOAuthDoneCallback, + PassportProfile, +} from '@backstage/plugin-auth-node'; + +/** @public */ +export const oidcAuthenticator = createOAuthAuthenticator({ + defaultProfileTransform: + PassportOAuthAuthenticatorHelper.defaultProfileTransform, + initialize({ callbackUrl, config }) { + const clientId = config.getString('clientId'); + const clientSecret = config.getString('clientSecret'); + const baseUrl = + config.getOptionalString('audience') || 'https://oidc.com'; + + return PassportOAuthAuthenticatorHelper.from( + new GitlabStrategy( + { + clientID: clientId, + clientSecret: clientSecret, + callbackURL: callbackUrl, + baseURL: baseUrl, + authorizationURL: `${baseUrl}/oauth/authorize`, + tokenURL: `${baseUrl}/oauth/token`, + profileURL: `${baseUrl}/api/v4/user`, + }, + ( + accessToken: string, + refreshToken: string, + params: any, + fullProfile: PassportProfile, + done: PassportOAuthDoneCallback, + ) => { + done( + undefined, + { fullProfile, params, accessToken }, + { refreshToken }, + ); + }, + ), + ); + }, + + async start(input, helper) { + return helper.start(input, { + accessType: 'offline', + prompt: 'consent', + }); + }, + + async authenticate(input, helper) { + return helper.authenticate(input); + }, + + async refresh(input, helper) { + return helper.refresh(input); + }, +}); diff --git a/plugins/auth-backend-module-oidc-provider/src/index.ts b/plugins/auth-backend-module-oidc-provider/src/index.ts new file mode 100644 index 0000000000..4b5ddc123a --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/index.ts @@ -0,0 +1,25 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The oidc-provider backend module for the auth plugin. + * + * @packageDocumentation + */ + +export { oidcAuthenticator } from './authenticator'; +export { authModuleOidcProvider } from './module'; +export { oidcSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts new file mode 100644 index 0000000000..a8e19e362a --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -0,0 +1,79 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; +import { authPlugin } from '@backstage/plugin-auth-backend'; +import { authModuleOidcProvider } from './module'; +import request from 'supertest'; +import { decodeOAuthState } from '@backstage/plugin-auth-node'; + +describe('authModuleOidcProvider', () => { + it('should start', async () => { + const { server } = await startTestBackend({ + features: [ + authPlugin, + authModuleOidcProvider, + mockServices.rootConfig.factory({ + data: { + app: { + baseUrl: 'http://localhost:3000', + }, + auth: { + providers: { + oidc: { + development: { + clientId: 'my-client-id', + clientSecret: 'my-client-secret', + }, + }, + }, + }, + }, + }), + ], + }); + + const agent = request.agent(server); + + const res = await agent.get('/api/auth/oidc/start?env=development'); + + expect(res.status).toEqual(302); + + const nonceCookie = agent.jar.getCookie('oidc-nonce', { + domain: 'localhost', + path: '/api/auth/oidc/handler', + script: false, + secure: false, + }); + expect(nonceCookie).toBeDefined(); + + const startUrl = new URL(res.get('location')); + expect(startUrl.origin).toBe('https://oidc.com'); + expect(startUrl.pathname).toBe('/oauth/authorize'); + expect(Object.fromEntries(startUrl.searchParams)).toEqual({ + response_type: 'code', + scope: 'read_user', + client_id: 'my-client-id', + redirect_uri: `http://localhost:${server.port()}/api/auth/oidc/handler/frame`, + state: expect.any(String), + }); + + expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ + env: 'development', + nonce: decodeURIComponent(nonceCookie.value), + }); + }); +}); diff --git a/plugins/auth-backend-module-oidc-provider/src/module.ts b/plugins/auth-backend-module-oidc-provider/src/module.ts new file mode 100644 index 0000000000..790d522494 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/module.ts @@ -0,0 +1,48 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { + authProvidersExtensionPoint, + commonSignInResolvers, + createOAuthProviderFactory, +} from '@backstage/plugin-auth-node'; +import { gitlabAuthenticator } from './authenticator'; +import { gitlabSignInResolvers } from './resolvers'; + +/** @public */ +export const authModuleGitlabProvider = createBackendModule({ + pluginId: 'auth', + moduleId: 'gitlab-provider', + register(reg) { + reg.registerInit({ + deps: { + providers: authProvidersExtensionPoint, + }, + async init({ providers }) { + providers.registerProvider({ + providerId: 'gitlab', + factory: createOAuthProviderFactory({ + authenticator: gitlabAuthenticator, + signInResolverFactories: { + ...gitlabSignInResolvers, + ...commonSignInResolvers, + }, + }), + }); + }, + }); + }, +}); diff --git a/plugins/auth-backend-module-oidc-provider/src/resolvers.ts b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts new file mode 100644 index 0000000000..755ed08aa0 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts @@ -0,0 +1,50 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { + createSignInResolverFactory, + OAuthAuthenticatorResult, + PassportProfile, + SignInInfo, +} from '@backstage/plugin-auth-node'; + +/** + * Available sign-in resolvers for the GitLab auth provider. + * + * @public + */ +export namespace gitlabSignInResolvers { + /** + * Looks up the user by matching their GitLab username to the entity name. + */ + export const usernameMatchingUserEntityName = createSignInResolverFactory({ + create() { + return async ( + info: SignInInfo>, + ctx, + ) => { + const { result } = info; + + const id = result.fullProfile.username; + if (!id) { + throw new Error(`GitLab user profile does not contain a username`); + } + + return ctx.signInWithCatalogUser({ entityRef: { name: id } }); + }; + }, + }); +} diff --git a/plugins/auth-backend-module-oidc-provider/src/types.d.ts b/plugins/auth-backend-module-oidc-provider/src/types.d.ts new file mode 100644 index 0000000000..96535294ee --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/types.d.ts @@ -0,0 +1,25 @@ +/* + * 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. + */ + +declare module 'passport-gitlab2' { + import { Request } from 'express'; + import { StrategyCreated } from 'passport'; + + export class Strategy { + constructor(options: any, verify: any); + authenticate(this: StrategyCreated, req: Request, options?: any): any; + } +} From c541201b1b2856b1cde74a079d90aad7bcad51a4 Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Mon, 18 Sep 2023 11:38:10 -0400 Subject: [PATCH 064/152] WIP: oidc auth provider module migration Signed-off-by: Ruben Vallejo --- .../README.md | 4 +- .../package.json | 12 +- .../src/authenticator.ts | 100 ++++++--- .../src/module.test.ts | 211 +++++++++++++++--- .../src/module.ts | 14 +- .../src/resolvers.ts | 29 ++- .../src/types.d.ts | 25 --- yarn.lock | 54 +++++ 8 files changed, 352 insertions(+), 97 deletions(-) delete mode 100644 plugins/auth-backend-module-oidc-provider/src/types.d.ts diff --git a/plugins/auth-backend-module-oidc-provider/README.md b/plugins/auth-backend-module-oidc-provider/README.md index 968c8a57dd..e586f32fd7 100644 --- a/plugins/auth-backend-module-oidc-provider/README.md +++ b/plugins/auth-backend-module-oidc-provider/README.md @@ -1,6 +1,6 @@ -# Auth Module: GitLab Provider +# Auth Module: Oidc Provider -This module provides an GitLab auth provider implementation for `@backstage/plugin-auth-backend`. +This module provides an Oidc auth provider implementation for `@backstage/plugin-auth-backend`. ## Links diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 4bb750cd3f..09bd1033a0 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -25,16 +25,22 @@ "dependencies": { "@backstage/backend-common": "workspace:^", "@backstage/backend-plugin-api": "workspace:^", + "@backstage/plugin-auth-backend": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "express": "^4.18.2", - "passport": "^0.6.0", - "passport-oidc2": "^5.0.0" + "openid-client": "^5.5.0", + "passport": "^0.6.0" }, "devDependencies": { "@backstage/backend-defaults": "workspace:^", "@backstage/backend-test-utils": "workspace:^", "@backstage/cli": "workspace:^", - "@backstage/plugin-auth-backend": "workspace:^", + "@backstage/config": "workspace:^", + "cookie-parser": "^1.4.6", + "express-promise-router": "^4.1.1", + "express-session": "^1.17.3", + "jose": "^4.14.6", + "msw": "^1.3.1", "supertest": "^6.3.3" }, "configSchema": "config.d.ts", diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index a68b7b269d..7a7d57d7e2 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -14,64 +14,110 @@ * limitations under the License. */ -import { Strategy as GitlabStrategy } from 'passport-oidc2'; +import { + Issuer, + ClientAuthMethod, + TokenSet, + UserinfoResponse, + Strategy as OidcStrategy, +} from 'openid-client'; import { createOAuthAuthenticator, + decodeOAuthState, + encodeOAuthState, + PassportDoneCallback, PassportOAuthAuthenticatorHelper, PassportOAuthDoneCallback, + PassportOAuthPrivateInfo, PassportProfile, } from '@backstage/plugin-auth-node'; +import { OidcAuthResult } from '@backstage/plugin-auth-backend'; /** @public */ export const oidcAuthenticator = createOAuthAuthenticator({ defaultProfileTransform: PassportOAuthAuthenticatorHelper.defaultProfileTransform, - initialize({ callbackUrl, config }) { + async initialize({ callbackUrl, config }) { const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); - const baseUrl = - config.getOptionalString('audience') || 'https://oidc.com'; + const customCallbackUrl = config.getOptionalString('callbackUrl'); + const callbackUrl2 = customCallbackUrl || callbackUrl; + const metadataUrl = config.getString('metadataUrl'); + const tokenEndpointAuthMethod = config.getOptionalString( + 'tokenEndpointAuthMethod', + ) as ClientAuthMethod; + const tokenSignedResponseAlg = config.getOptionalString( + 'tokenSignedResponseAlg', + ); + const initializedScope = config.getOptionalString('scope'); + const initializedPrompt = config.getOptionalString('prompt'); + const issuer = await Issuer.discover( + `${metadataUrl}/.well-known/openid-configuration`, + ); + const client = new issuer.Client({ + access_type: 'offline', // this option must be passed to provider to receive a refresh token + client_id: clientId, + client_secret: clientSecret, + redirect_uris: [callbackUrl2], + response_types: ['code'], + token_endpoint_auth_method: + tokenEndpointAuthMethod || 'client_secret_basic', + id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256', + scope: initializedScope || '', + }); - return PassportOAuthAuthenticatorHelper.from( - new GitlabStrategy( + const helper = PassportOAuthAuthenticatorHelper.from( + new OidcStrategy( { - clientID: clientId, - clientSecret: clientSecret, - callbackURL: callbackUrl, - baseURL: baseUrl, - authorizationURL: `${baseUrl}/oauth/authorize`, - tokenURL: `${baseUrl}/oauth/token`, - profileURL: `${baseUrl}/api/v4/user`, + client, + passReqToCallback: false, }, ( - accessToken: string, - refreshToken: string, - params: any, - fullProfile: PassportProfile, - done: PassportOAuthDoneCallback, + tokenset: TokenSet, + userinfo: UserinfoResponse, + done: PassportDoneCallback, ) => { + if (typeof done !== 'function') { + throw new Error( + 'OIDC IdP must provide a userinfo_endpoint in the metadata response', + ); + } done( undefined, - { fullProfile, params, accessToken }, - { refreshToken }, + { tokenset, userinfo }, + { + refreshToken: tokenset.refresh_token, + }, ); }, ), ); + + return { helper, client, initializedScope, initializedPrompt }; }, - async start(input, helper) { + async start(input, implementation) { + const { initializedScope, initializedPrompt, helper } = + await implementation; + const options: Record = { + scope: input.scope || initializedScope || 'openid profile email', + state: input.state, + }; + const prompt = initializedPrompt || 'none'; + if (prompt !== 'auto') { + options.prompt = prompt; + } + return helper.start(input, { - accessType: 'offline', - prompt: 'consent', + ...options, }); }, - async authenticate(input, helper) { - return helper.authenticate(input); + async authenticate(input, implementation) { + return (await implementation).helper.authenticate(input); }, - async refresh(input, helper) { - return helper.refresh(input); + async refresh(input, implementation) { + return (await implementation).helper.refresh(input); }, }); diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index a8e19e362a..406cc3229e 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -18,39 +18,190 @@ import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; import { authPlugin } from '@backstage/plugin-auth-backend'; import { authModuleOidcProvider } from './module'; import request from 'supertest'; -import { decodeOAuthState } from '@backstage/plugin-auth-node'; +import { + AuthProviderRouteHandlers, + AuthResolverContext, + createOAuthRouteHandlers, + decodeOAuthState, +} from '@backstage/plugin-auth-node'; +import { setupServer } from 'msw'; +import { ClientMetadata, IssuerMetadata } from 'openid-client'; +import { rest } from 'msw'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { Server } from 'http'; +import { AddressInfo } from 'net'; +import express from 'express'; +import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; +import cookieParser from 'cookie-parser'; +import passport from 'passport'; +import session from 'express-session'; +import { oidcAuthenticator } from './authenticator'; +import { ConfigReader } from '@backstage/config'; +import Router from 'express-promise-router'; describe('authModuleOidcProvider', () => { - it('should start', async () => { - const { server } = await startTestBackend({ - features: [ - authPlugin, - authModuleOidcProvider, - mockServices.rootConfig.factory({ - data: { - app: { - baseUrl: 'http://localhost:3000', - }, - auth: { - providers: { - oidc: { - development: { - clientId: 'my-client-id', - clientSecret: 'my-client-secret', - }, - }, - }, - }, - }, + let app: express.Express; + let backstageServer: Server; + let appUrl: string; + let providerRouteHandler: AuthProviderRouteHandlers; + let idToken: string; + let publicKey: JWK; + + const mswServer = setupServer(); + setupRequestMockHandlers(mswServer); + + const issuerMetadata = { + issuer: 'https://oidc.test', + authorization_endpoint: 'https://oidc.test/as/authorization.oauth2', + token_endpoint: 'https://oidc.test/as/token.oauth2', + revocation_endpoint: 'https://oidc.test/as/revoke_token.oauth2', + userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', + introspection_endpoint: 'https://oidc.test/as/introspect.oauth2', + jwks_uri: 'https://oidc.test/pf/JWKS', + scopes_supported: ['openid'], + claims_supported: ['email'], + response_types_supported: ['code'], + id_token_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], + token_endpoint_auth_signing_alg_values_supported: [ + 'RS256', + 'RS512', + 'HS256', + ], + request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], + }; + + const clientMetadata = { + authHandler: async input => ({ + profile: { + displayName: input.userinfo.email, + }, + }), + resolverContext: {} as AuthResolverContext, + callbackUrl: 'https://oidc.test/callback', + clientId: 'testclientid', + clientSecret: 'testclientsecret', + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + tokenEndpointAuthMethod: 'none', + tokenSignedResponseAlg: 'none', + }; + + beforeAll(async () => { + const keyPair = await generateKeyPair('ES256'); + const privateKey = await exportJWK(keyPair.privateKey); + publicKey = await exportJWK(keyPair.publicKey); + publicKey.alg = privateKey.alg = 'ES256'; + + idToken = await new SignJWT({ + sub: 'test', + iss: 'https://pinniped.test', + iat: Date.now(), + aud: 'clientId', + exp: Date.now() + 10000, + }) + .setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid }) + .sign(keyPair.privateKey); + }); + + beforeEach(async () => { + jest.clearAllMocks(); + + mswServer.use( + rest.get( + 'https://oidc.test/.well-known/openid-configuration', + (_req, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(issuerMetadata), + ), + ), + rest.get('https://oidc.test/oauth2/authorize', async (req, res, ctx) => { + const callbackUrl = new URL(req.url.searchParams.get('redirect_uri')!); + callbackUrl.searchParams.set('code', 'authorization_code'); + callbackUrl.searchParams.set( + 'state', + req.url.searchParams.get('state')!, + ); + callbackUrl.searchParams.set('scope', 'test-scope'); + return res( + ctx.status(302), + ctx.set('Location', callbackUrl.toString()), + ); + }), + ); + + const secret = 'secret'; + app = express() + .use(cookieParser(secret)) + .use( + session({ + secret, + saveUninitialized: false, + resave: false, + cookie: { secure: false }, }), - ], + ) + .use(passport.initialize()) + .use(passport.session()); + await new Promise(resolve => { + backstageServer = app.listen(0, '0.0.0.0', () => { + appUrl = `http://127.0.0.1:${ + (backstageServer.address() as AddressInfo).port + }`; + resolve(null); + }); }); - const agent = request.agent(server); + mswServer.use(rest.all(`${appUrl}/*`, req => req.passthrough())); - const res = await agent.get('/api/auth/oidc/start?env=development'); + providerRouteHandler = createOAuthRouteHandlers({ + authenticator: oidcAuthenticator, + appUrl, + baseUrl: `${appUrl}/api/auth`, + isOriginAllowed: _ => true, + providerId: 'oidc', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test', + clientId: 'clientId', + clientSecret: 'clientSecret', + }), + resolverContext: { + issueToken: async _ => ({ token: '' }), + findCatalogUser: async _ => ({ + entity: { + apiVersion: '', + kind: '', + metadata: { name: '' }, + }, + }), + signInWithCatalogUser: async _ => ({ token: '' }), + }, + }); - expect(res.status).toEqual(302); + const router = Router(); + router + .use( + '/api/auth/pinniped/start', + providerRouteHandler.start.bind(providerRouteHandler), + ) + .use( + '/api/auth/pinniped/handler/frame', + providerRouteHandler.frameHandler.bind(providerRouteHandler), + ); + app.use(router); + }); + + afterEach(() => { + backstageServer.close(); + }); + + it('should start', async () => { + const agent = request.agent(''); + + const startResponse = await agent.get( + `${appUrl}/api/auth/oidc/start?env=development`, + ); + expect(startResponse.status).toEqual(302); const nonceCookie = agent.jar.getCookie('oidc-nonce', { domain: 'localhost', @@ -60,14 +211,16 @@ describe('authModuleOidcProvider', () => { }); expect(nonceCookie).toBeDefined(); - const startUrl = new URL(res.get('location')); + const startUrl = new URL(startResponse.get('location')); expect(startUrl.origin).toBe('https://oidc.com'); expect(startUrl.pathname).toBe('/oauth/authorize'); expect(Object.fromEntries(startUrl.searchParams)).toEqual({ response_type: 'code', scope: 'read_user', client_id: 'my-client-id', - redirect_uri: `http://localhost:${server.port()}/api/auth/oidc/handler/frame`, + redirect_uri: `http://localhost:${ + (backstageServer.address() as AddressInfo).port + }/api/auth/oidc/handler/frame`, state: expect.any(String), }); @@ -75,5 +228,5 @@ describe('authModuleOidcProvider', () => { env: 'development', nonce: decodeURIComponent(nonceCookie.value), }); - }); + }, 70000); }); diff --git a/plugins/auth-backend-module-oidc-provider/src/module.ts b/plugins/auth-backend-module-oidc-provider/src/module.ts index 790d522494..5680cd8603 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.ts @@ -19,13 +19,13 @@ import { commonSignInResolvers, createOAuthProviderFactory, } from '@backstage/plugin-auth-node'; -import { gitlabAuthenticator } from './authenticator'; -import { gitlabSignInResolvers } from './resolvers'; +import { oidcAuthenticator } from './authenticator'; +import { oidcSignInResolvers } from './resolvers'; /** @public */ -export const authModuleGitlabProvider = createBackendModule({ +export const authModuleOidcProvider = createBackendModule({ pluginId: 'auth', - moduleId: 'gitlab-provider', + moduleId: 'oidc-provider', register(reg) { reg.registerInit({ deps: { @@ -33,11 +33,11 @@ export const authModuleGitlabProvider = createBackendModule({ }, async init({ providers }) { providers.registerProvider({ - providerId: 'gitlab', + providerId: 'oidc', factory: createOAuthProviderFactory({ - authenticator: gitlabAuthenticator, + authenticator: oidcAuthenticator, signInResolverFactories: { - ...gitlabSignInResolvers, + ...oidcSignInResolvers, ...commonSignInResolvers, }, }), diff --git a/plugins/auth-backend-module-oidc-provider/src/resolvers.ts b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts index 755ed08aa0..930ff462f2 100644 --- a/plugins/auth-backend-module-oidc-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts @@ -22,13 +22,13 @@ import { } from '@backstage/plugin-auth-node'; /** - * Available sign-in resolvers for the GitLab auth provider. + * Available sign-in resolvers for the Oidc auth provider. * * @public */ -export namespace gitlabSignInResolvers { +export namespace oidcSignInResolvers { /** - * Looks up the user by matching their GitLab username to the entity name. + * Looks up the user by matching their Oidc username to the entity name. */ export const usernameMatchingUserEntityName = createSignInResolverFactory({ create() { @@ -40,11 +40,32 @@ export namespace gitlabSignInResolvers { const id = result.fullProfile.username; if (!id) { - throw new Error(`GitLab user profile does not contain a username`); + throw new Error(`Oidc user profile does not contain a username`); } return ctx.signInWithCatalogUser({ entityRef: { name: id } }); }; }, }); + + /** + * Looks up the user by matching their email to the `google.com/email` annotation. Still working out this resolver..... + */ + export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ + create() { + return async (info: SignInInfo, ctx) => { + const email = info.result.iapToken.email; + + if (!email) { + throw new Error('Google IAP sign-in result is missing email'); + } + + return ctx.signInWithCatalogUser({ + annotations: { + 'google.com/email': email, + }, + }); + }; + }, + }); } diff --git a/plugins/auth-backend-module-oidc-provider/src/types.d.ts b/plugins/auth-backend-module-oidc-provider/src/types.d.ts deleted file mode 100644 index 96535294ee..0000000000 --- a/plugins/auth-backend-module-oidc-provider/src/types.d.ts +++ /dev/null @@ -1,25 +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. - */ - -declare module 'passport-gitlab2' { - import { Request } from 'express'; - import { StrategyCreated } from 'passport'; - - export class Strategy { - constructor(options: any, verify: any); - authenticate(this: StrategyCreated, req: Request, options?: any): any; - } -} diff --git a/yarn.lock b/yarn.lock index 53cd19f848..6e6721b99e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4672,6 +4672,30 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider": + version: 0.0.0-use.local + resolution: "@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider" + dependencies: + "@backstage/backend-common": "workspace:^" + "@backstage/backend-defaults": "workspace:^" + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/plugin-auth-backend": "workspace:^" + "@backstage/plugin-auth-node": "workspace:^" + cookie-parser: ^1.4.6 + express: ^4.18.2 + express-promise-router: ^4.1.1 + express-session: ^1.17.3 + jose: ^4.14.6 + msw: ^1.3.1 + openid-client: ^5.5.0 + passport: ^0.6.0 + supertest: ^6.3.3 + languageName: unknown + linkType: soft + "@backstage/plugin-auth-backend-module-okta-provider@workspace:^, @backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-okta-provider@workspace:plugins/auth-backend-module-okta-provider" @@ -31245,6 +31269,13 @@ __metadata: languageName: node linkType: hard +"jose@npm:^4.14.4": + version: 4.14.6 + resolution: "jose@npm:4.14.6" + checksum: eae81a234e7bf1446b1bd80722b3462b014e3835b155c3a7799c1c5043163a53a0dc28d347004151b031e6b7b863403aabf8814d9cc217ce21f8c2f3ebd4b335 + languageName: node + linkType: hard + "jose@npm:^4.14.6, jose@npm:^4.15.4, jose@npm:^4.6.0": version: 4.15.4 resolution: "jose@npm:4.15.4" @@ -35636,6 +35667,18 @@ __metadata: languageName: node linkType: hard +"openid-client@npm:^5.5.0": + version: 5.5.0 + resolution: "openid-client@npm:5.5.0" + dependencies: + jose: ^4.14.4 + lru-cache: ^6.0.0 + object-hash: ^2.2.0 + oidc-token-hash: ^5.0.3 + checksum: d2617b5bb0d9a0da338aeb7489bcbe3a79df9681189c7b61c2a3284289eee7110dfee2b04b49a9fdd4f064b7e2057ddb0becfedd9c19388e7788ae15b24c8e4c + languageName: node + linkType: hard + "oppa@npm:^0.4.0": version: 0.4.0 resolution: "oppa@npm:0.4.0" @@ -36273,6 +36316,17 @@ __metadata: languageName: node linkType: hard +"passport@npm:^0.6.0": + version: 0.6.0 + resolution: "passport@npm:0.6.0" + dependencies: + passport-strategy: 1.x.x + pause: 0.0.1 + utils-merge: ^1.0.1 + checksum: ef932ad671d50de34765c7a53cd1e058d8331a82a6df09265a9c6c1168911aee4a7b5215803d0101110ab7f317e096b4954ca7e18fb2c33b9929f0bd17dbe159 + languageName: node + linkType: hard + "passport@npm:^0.7.0": version: 0.7.0 resolution: "passport@npm:0.7.0" From 55c639839a42a5d38354267ef6018c1c208ee8b0 Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Mon, 18 Sep 2023 15:48:06 -0400 Subject: [PATCH 065/152] Working module tests Signed-off-by: Ruben Vallejo --- .../src/module.test.ts | 60 +++++++------------ 1 file changed, 22 insertions(+), 38 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index 406cc3229e..f85afec0c3 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -14,9 +14,6 @@ * limitations under the License. */ -import { mockServices, startTestBackend } from '@backstage/backend-test-utils'; -import { authPlugin } from '@backstage/plugin-auth-backend'; -import { authModuleOidcProvider } from './module'; import request from 'supertest'; import { AuthProviderRouteHandlers, @@ -24,8 +21,7 @@ import { createOAuthRouteHandlers, decodeOAuthState, } from '@backstage/plugin-auth-node'; -import { setupServer } from 'msw'; -import { ClientMetadata, IssuerMetadata } from 'openid-client'; +import { setupServer } from 'msw/node'; import { rest } from 'msw'; import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; import { Server } from 'http'; @@ -52,12 +48,12 @@ describe('authModuleOidcProvider', () => { const issuerMetadata = { issuer: 'https://oidc.test', - authorization_endpoint: 'https://oidc.test/as/authorization.oauth2', - token_endpoint: 'https://oidc.test/as/token.oauth2', - revocation_endpoint: 'https://oidc.test/as/revoke_token.oauth2', + authorization_endpoint: 'https://oidc.test/oauth2/authorize', + token_endpoint: 'https://oidc.test/oauth2/token', + revocation_endpoint: 'https://oidc.test/oauth2/revoke_token', userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', introspection_endpoint: 'https://oidc.test/as/introspect.oauth2', - jwks_uri: 'https://oidc.test/pf/JWKS', + jwks_uri: 'https://oidc.test/jwks.json', scopes_supported: ['openid'], claims_supported: ['email'], response_types_supported: ['code'], @@ -70,21 +66,6 @@ describe('authModuleOidcProvider', () => { request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], }; - const clientMetadata = { - authHandler: async input => ({ - profile: { - displayName: input.userinfo.email, - }, - }), - resolverContext: {} as AuthResolverContext, - callbackUrl: 'https://oidc.test/callback', - clientId: 'testclientid', - clientSecret: 'testclientsecret', - metadataUrl: 'https://oidc.test/.well-known/openid-configuration', - tokenEndpointAuthMethod: 'none', - tokenSignedResponseAlg: 'none', - }; - beforeAll(async () => { const keyPair = await generateKeyPair('ES256'); const privateKey = await exportJWK(keyPair.privateKey); @@ -93,7 +74,7 @@ describe('authModuleOidcProvider', () => { idToken = await new SignJWT({ sub: 'test', - iss: 'https://pinniped.test', + iss: 'https://oidc.test', iat: Date.now(), aud: 'clientId', exp: Date.now() + 10000, @@ -181,11 +162,11 @@ describe('authModuleOidcProvider', () => { const router = Router(); router .use( - '/api/auth/pinniped/start', + '/api/auth/oidc/start', providerRouteHandler.start.bind(providerRouteHandler), ) .use( - '/api/auth/pinniped/handler/frame', + '/api/auth/oidc/handler/frame', providerRouteHandler.frameHandler.bind(providerRouteHandler), ); app.use(router); @@ -196,15 +177,15 @@ describe('authModuleOidcProvider', () => { }); it('should start', async () => { - const agent = request.agent(''); + const agent = request.agent(backstageServer); const startResponse = await agent.get( - `${appUrl}/api/auth/oidc/start?env=development`, + `/api/auth/oidc/start?env=development`, ); expect(startResponse.status).toEqual(302); const nonceCookie = agent.jar.getCookie('oidc-nonce', { - domain: 'localhost', + domain: '127.0.0.1', path: '/api/auth/oidc/handler', script: false, secure: false, @@ -212,21 +193,24 @@ describe('authModuleOidcProvider', () => { expect(nonceCookie).toBeDefined(); const startUrl = new URL(startResponse.get('location')); - expect(startUrl.origin).toBe('https://oidc.com'); - expect(startUrl.pathname).toBe('/oauth/authorize'); + expect(startUrl.origin).toBe('https://oidc.test'); + expect(startUrl.pathname).toBe('/oauth2/authorize'); expect(Object.fromEntries(startUrl.searchParams)).toEqual({ response_type: 'code', - scope: 'read_user', - client_id: 'my-client-id', - redirect_uri: `http://localhost:${ - (backstageServer.address() as AddressInfo).port - }/api/auth/oidc/handler/frame`, + scope: 'openid profile email', + client_id: 'clientId', + redirect_uri: `${appUrl}/api/auth/oidc/handler/frame`, state: expect.any(String), + prompt: 'none', + code_challenge: expect.any(String), + code_challenge_method: `S256`, }); expect(decodeOAuthState(startUrl.searchParams.get('state')!)).toEqual({ env: 'development', nonce: decodeURIComponent(nonceCookie.value), }); - }, 70000); + }); + + // TODO: This seems to be the place for integration testing, so far only have test that hit metadata endpoints along with /start but might be missing responses for handler? }); From a3c911e6365b60e45392b71df1a03e0384d90e24 Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Wed, 27 Sep 2023 17:47:22 -0400 Subject: [PATCH 066/152] authenticator alongside passing unit tests Signed-off-by: Ruben Vallejo --- .../config.d.ts | 5 +- .../src/authenticator.test.ts | 394 ++++++++++++++ .../src/authenticator.ts | 124 +++-- .../src/module.test.ts | 69 ++- .../src/resolvers.ts | 51 +- plugins/auth-backend/package.json | 1 + .../src/providers/oidc/provider.test.ts | 189 ------- .../src/providers/oidc/provider.ts | 486 +++++++++--------- yarn.lock | 38 +- 9 files changed, 825 insertions(+), 532 deletions(-) create mode 100644 plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts delete mode 100644 plugins/auth-backend/src/providers/oidc/provider.test.ts diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index ba141d9555..803b9add73 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -25,8 +25,11 @@ export interface Config { * @visibility secret */ clientSecret: string; - audience?: string; + metadataUrl: string; callbackUrl?: string; + tokenSignedResponseAlg?: string; + scope?: string; + prompt?: string; }; }; }; diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts new file mode 100644 index 0000000000..6f22517361 --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -0,0 +1,394 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { + OAuthAuthenticatorAuthenticateInput, + OAuthAuthenticatorRefreshInput, + OAuthAuthenticatorStartInput, + OAuthState, + decodeOAuthState, + encodeOAuthState, +} from '@backstage/plugin-auth-node'; +import { oidcAuthenticator } from './authenticator'; +import { setupServer } from 'msw/node'; +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { ConfigReader } from '@backstage/config'; +import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; +import { rest } from 'msw'; +import express from 'express'; + +describe('oidcAuthenticator', () => { + let implementation: any; + let oauthState: OAuthState; + let idToken: string; + let publicKey: JWK; + + const mswServer = setupServer(); + setupRequestMockHandlers(mswServer); + + const issuerMetadata = { + issuer: 'https://oidc.test', + authorization_endpoint: 'https://oidc.test/oauth2/authorize', + token_endpoint: 'https://oidc.test/oauth2/token', + revocation_endpoint: 'https://oidc.test/oauth2/revoke_token', + userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', + introspection_endpoint: 'https://oidc.test/introspect.oauth2', + jwks_uri: 'https://oidc.test/jwks.json', + scopes_supported: [ + 'openid', + 'offline_access', + 'oidc:request-audience', + 'username', + 'groups', + ], + claims_supported: ['email', 'username', 'groups', 'additionalClaims'], + response_types_supported: ['code'], + id_token_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], + token_endpoint_auth_signing_alg_values_supported: [ + 'RS256', + 'RS512', + 'HS256', + ], + request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], + }; + + beforeAll(async () => { + const keyPair = await generateKeyPair('RS256'); + const privateKey = await exportJWK(keyPair.privateKey); + publicKey = await exportJWK(keyPair.publicKey); + publicKey.alg = privateKey.alg = 'RS256'; + + idToken = await new SignJWT({ + sub: 'test', + iss: 'https://oidc.test', + iat: Date.now(), + aud: 'clientId', + exp: Date.now() + 10000, + }) + .setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid }) + .sign(keyPair.privateKey); + }); + + beforeEach(() => { + mswServer.use( + rest.get( + 'https://oidc.test/.well-known/openid-configuration', + (_req, res, ctx) => + res( + ctx.status(200), + ctx.set('Content-Type', 'application/json'), + ctx.json(issuerMetadata), + ), + ), + rest.get('https://oidc.test/jwks.json', async (_req, res, ctx) => + res(ctx.status(200), ctx.json({ keys: [{ ...publicKey }] })), + ), + rest.post('https://oidc.test/oauth2/token', async (req, res, ctx) => { + return res( + req.headers.get('Authorization') + ? ctx.json({ + access_token: 'accessToken', + id_token: idToken, + refresh_token: 'refreshToken', + scope: 'testScope', + token_type: '', + }) + : ctx.status(401), + ); + }), + rest.get( + 'https://oidc.test/idp/userinfo.openid', + async (_req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + sub: 'test', + name: 'Alice Adams', + given_name: 'Alice', + family_name: 'Adams', + email: 'alice@test.com', + }), + ), + ), + ); + + implementation = oidcAuthenticator.initialize({ + callbackUrl: 'https://backstage.test/callback', + config: new ConfigReader({ + metadataUrl: 'https://oidc.test', + clientId: 'clientId', + clientSecret: 'clientSecret', + }), + }); + + oauthState = { + nonce: 'nonce', + env: 'env', + }; + }); + + afterEach(() => { + jest.clearAllMocks(); + }); + + describe('#start', () => { + let fakeSession: Record; + let startRequest: OAuthAuthenticatorStartInput; + + beforeEach(() => { + fakeSession = {}; + startRequest = { + state: encodeOAuthState(oauthState), + req: { + method: 'GET', + url: 'test', + session: fakeSession, + }, + } as unknown as OAuthAuthenticatorStartInput; + }); + + it('redirects to authorization endpoint returned from OIDC metadata endpoint', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const url = new URL(startResponse.url); + + expect(url.protocol).toBe('https:'); + expect(url.hostname).toBe('oidc.test'); + expect(url.pathname).toBe('/oauth2/authorize'); + }); + + it('initiates authorization code grant', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('response_type')).toBe('code'); + }); + + it('passes client ID from config', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('client_id')).toBe('clientId'); + }); + + it('passes callback URL from config', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('redirect_uri')).toBe( + 'https://backstage.test/callback', + ); + }); + + it('generates PKCE challenge', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + + expect(searchParams.get('code_challenge_method')).toBe('S256'); + expect(searchParams.get('code_challenge')).not.toBeNull(); + }); + + it('stores PKCE verifier in session', async () => { + await oidcAuthenticator.start(startRequest, implementation); + expect(fakeSession['oidc:oidc.test'].code_verifier).toBeDefined(); + }); + + // without the offline_access scope refresh should not work should we add in the test that is a required scope to test for? curently dont see that by defualt in the provider implementation. + it('requests default scopes if none are provided in config', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + const scopes = searchParams.get('scope')?.split(' ') ?? []; + + expect(scopes).toEqual( + expect.arrayContaining(['openid', 'profile', 'email']), + ); + }); + + it('encodes OAuth state in query param', async () => { + const startResponse = await oidcAuthenticator.start( + startRequest, + implementation, + ); + const { searchParams } = new URL(startResponse.url); + const stateParam = searchParams.get('state'); + const decodedState = decodeOAuthState(stateParam!); + + expect(decodedState).toMatchObject(oauthState); + }); + + it('fails when request has no session', async () => { + return expect( + oidcAuthenticator.start( + { + state: encodeOAuthState(oauthState), + req: { + method: 'GET', + url: 'test', + }, + } as unknown as OAuthAuthenticatorStartInput, + implementation, + ), + ).rejects.toThrow('authentication requires session support'); + }); + }); + + describe('#authenticate', () => { + let handlerRequest: OAuthAuthenticatorAuthenticateInput; + + beforeEach(() => { + handlerRequest = { + req: { + method: 'GET', + url: `https://test?code=authorization_code&state=${encodeOAuthState( + oauthState, + )}`, + session: { + 'oidc:oidc.test': { + state: encodeOAuthState(oauthState), + }, + }, + } as unknown as express.Request, + }; + }); + + it('exchanges authorization code for access token', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const accessToken = handlerResponse.session.accessToken; + + expect(accessToken).toEqual('accessToken'); + }); + + it('exchanges authorization code for refresh token', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const refreshToken = handlerResponse.session.refreshToken; + + expect(refreshToken).toEqual('refreshToken'); + }); + + it('returns granted scope', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const responseScope = handlerResponse.session.scope; + + expect(responseScope).toEqual('testScope'); + }); + + it('fails without authorization code', async () => { + handlerRequest.req.url = 'https://test.com'; + return expect( + oidcAuthenticator.authenticate(handlerRequest, implementation), + ).rejects.toThrow('Unexpected redirect'); + }); + + it('fails without oauth state', async () => { + return expect( + oidcAuthenticator.authenticate( + { + req: { + method: 'GET', + url: `https://test?code=authorization_code}`, + session: { + ['oidc:pinniped.test']: { + state: { handle: 'sessionid', code_verifier: 'foo' }, + }, + }, + } as unknown as express.Request, + }, + implementation, + ), + ).rejects.toThrow( + 'Authentication failed, did not find expected authorization request details in session, req.session["oidc:oidc.test"] is undefined', + ); + }); + + it('fails when request has no session', async () => { + return expect( + oidcAuthenticator.authenticate( + { + req: { + method: 'GET', + url: 'https://test.com', + } as unknown as express.Request, + }, + implementation, + ), + ).rejects.toThrow('authentication requires session support'); + }); + }); + + describe('#refresh', () => { + let refreshRequest: OAuthAuthenticatorRefreshInput; + + beforeEach(() => { + refreshRequest = { + scope: '', + refreshToken: 'otherRefreshToken', + req: {} as express.Request, + }; + }); + + it('gets new refresh token', async () => { + const refreshResponse = await oidcAuthenticator.refresh( + refreshRequest, + implementation, + ); + + expect(refreshResponse.session.refreshToken).toBe('refreshToken'); + }); + + it('gets access token', async () => { + const refreshResponse = await oidcAuthenticator.refresh( + refreshRequest, + implementation, + ); + + expect(refreshResponse.session.accessToken).toBe('accessToken'); + }); + + it('gets id token', async () => { + const refreshResponse = await oidcAuthenticator.refresh( + refreshRequest, + implementation, + ); + + expect(refreshResponse.session.idToken).toBe(idToken); + }); + }); +}); diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index 7a7d57d7e2..7a7ebafe5c 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -23,15 +23,10 @@ import { } from 'openid-client'; import { createOAuthAuthenticator, - decodeOAuthState, - encodeOAuthState, - PassportDoneCallback, PassportOAuthAuthenticatorHelper, PassportOAuthDoneCallback, - PassportOAuthPrivateInfo, - PassportProfile, } from '@backstage/plugin-auth-node'; -import { OidcAuthResult } from '@backstage/plugin-auth-backend'; +// import { BACKSTAGE_SESSION_EXPIRATION } from '@backstage/plugin-auth-backend/src/lib/session'; /** @public */ export const oidcAuthenticator = createOAuthAuthenticator({ @@ -40,9 +35,9 @@ export const oidcAuthenticator = createOAuthAuthenticator({ async initialize({ callbackUrl, config }) { const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); + const metadataUrl = config.getString('metadataUrl'); const customCallbackUrl = config.getOptionalString('callbackUrl'); const callbackUrl2 = customCallbackUrl || callbackUrl; - const metadataUrl = config.getString('metadataUrl'); const tokenEndpointAuthMethod = config.getOptionalString( 'tokenEndpointAuthMethod', ) as ClientAuthMethod; @@ -66,39 +61,54 @@ export const oidcAuthenticator = createOAuthAuthenticator({ scope: initializedScope || '', }); - const helper = PassportOAuthAuthenticatorHelper.from( - new OidcStrategy( - { - client, - passReqToCallback: false, - }, - ( - tokenset: TokenSet, - userinfo: UserinfoResponse, - done: PassportDoneCallback, - ) => { - if (typeof done !== 'function') { - throw new Error( - 'OIDC IdP must provide a userinfo_endpoint in the metadata response', - ); - } - done( - undefined, - { tokenset, userinfo }, - { - refreshToken: tokenset.refresh_token, - }, + const strategy = new OidcStrategy( + { + client, + passReqToCallback: false, + }, + ( + tokenset: TokenSet, + userinfo: UserinfoResponse, + done: PassportOAuthDoneCallback, + ) => { + if (typeof done !== 'function') { + throw new Error( + 'OIDC IdP must provide a userinfo_endpoint in the metadata response', ); - }, - ), + } + + const expiresInSeconds = !tokenset.expires_in + ? 3600 + : Math.min(tokenset.expires_in!, 3600); + + done( + undefined, + { + fullProfile: { + provider: 'oidc', + id: userinfo.sub, + displayName: userinfo.name!, + }, + accessToken: tokenset.access_token!, + params: { + scope: tokenset.scope!, + expires_in: expiresInSeconds, + }, + }, + { + refreshToken: tokenset.refresh_token, + }, + ); + }, ); - return { helper, client, initializedScope, initializedPrompt }; + const helper = PassportOAuthAuthenticatorHelper.from(strategy); + + return { helper, client, initializedScope, initializedPrompt, strategy }; }, - async start(input, implementation) { - const { initializedScope, initializedPrompt, helper } = - await implementation; + async start(input, ctx) { + const { initializedScope, initializedPrompt, helper, strategy } = await ctx; const options: Record = { scope: input.scope || initializedScope || 'openid profile email', state: input.state, @@ -108,16 +118,48 @@ export const oidcAuthenticator = createOAuthAuthenticator({ options.prompt = prompt; } - return helper.start(input, { - ...options, + return new Promise((resolve, reject) => { + strategy.error = reject; + + return helper + .start(input, { + ...options, + }) + .then(resolve); }); }, - async authenticate(input, implementation) { - return (await implementation).helper.authenticate(input); + async authenticate(input, ctx) { + return (await ctx).helper.authenticate(input); }, - async refresh(input, implementation) { - return (await implementation).helper.refresh(input); + async refresh(input, ctx) { + const { client } = await ctx; + const tokenset = await client.refresh(input.refreshToken); + if (!tokenset.access_token) { + throw new Error('Refresh failed'); + } + const userinfo = await client.userinfo(tokenset.access_token); + + return new Promise((resolve, reject) => { + if (!tokenset.access_token) { + reject(new Error('Refresh Failed')); + } + resolve({ + fullProfile: { + provider: 'oidc', + id: userinfo.sub, + displayName: userinfo.name!, + }, + session: { + accessToken: tokenset.access_token!, + tokenType: tokenset.token_type ?? 'bearer', + scope: tokenset.scope!, + expiresInSeconds: tokenset.expires_in, + idToken: tokenset.id_token, + refreshToken: tokenset.refresh_token, + }, + }); + }); }, }); diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index f85afec0c3..5e01ae343f 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -67,10 +67,10 @@ describe('authModuleOidcProvider', () => { }; beforeAll(async () => { - const keyPair = await generateKeyPair('ES256'); + const keyPair = await generateKeyPair('RS256'); const privateKey = await exportJWK(keyPair.privateKey); publicKey = await exportJWK(keyPair.publicKey); - publicKey.alg = privateKey.alg = 'ES256'; + publicKey.alg = privateKey.alg = 'RS256'; idToken = await new SignJWT({ sub: 'test', @@ -109,6 +109,36 @@ describe('authModuleOidcProvider', () => { ctx.set('Location', callbackUrl.toString()), ); }), + rest.get('https://oidc.test/jwks.json', async (_req, res, ctx) => + res(ctx.status(200), ctx.json({ keys: [{ ...publicKey }] })), + ), + rest.post('https://oidc.test/oauth2/token', async (req, res, ctx) => { + return res( + req.headers.get('Authorization') + ? ctx.json({ + access_token: 'accessToken', + id_token: idToken, + refresh_token: 'refreshToken', + scope: 'testScope', + token_type: '', + }) + : ctx.status(401), + ); + }), + rest.get( + 'https://oidc.test/idp/userinfo.openid', + async (_req, res, ctx) => + res( + ctx.status(200), + ctx.json({ + sub: 'test', + name: 'Alice Adams', + given_name: 'Alice', + family_name: 'Adams', + email: 'alice@test.com', + }), + ), + ), ); const secret = 'secret'; @@ -212,5 +242,38 @@ describe('authModuleOidcProvider', () => { }); }); - // TODO: This seems to be the place for integration testing, so far only have test that hit metadata endpoints along with /start but might be missing responses for handler? + it('#authenticate exchanges authorization code for a access_token', async () => { + const agent = request.agent(''); + + // make /start request with audience parameter + const startResponse = await agent.get( + `${appUrl}/api/auth/oidc/start?env=development`, + ); + // follow redirect to authorization endpoint + const authorizationResponse = await agent.get( + startResponse.header.location, + ); + // follow redirect to token_endpoint + const handlerResponse = await agent.get( + authorizationResponse.header.location, + ); + + expect(handlerResponse.text).toContain( + encodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + response: { + profile: { + displayName: 'Alice Adams', + }, + providerInfo: { + accessToken: 'accessToken', + scope: 'testScope', + expiresInSeconds: 3600, + }, + }, + }), + ), + ); + }); }); diff --git a/plugins/auth-backend-module-oidc-provider/src/resolvers.ts b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts index 930ff462f2..5367ab5216 100644 --- a/plugins/auth-backend-module-oidc-provider/src/resolvers.ts +++ b/plugins/auth-backend-module-oidc-provider/src/resolvers.ts @@ -14,12 +14,7 @@ * limitations under the License. */ -import { - createSignInResolverFactory, - OAuthAuthenticatorResult, - PassportProfile, - SignInInfo, -} from '@backstage/plugin-auth-node'; +import { commonSignInResolvers } from '@backstage/plugin-auth-node'; /** * Available sign-in resolvers for the Oidc auth provider. @@ -28,44 +23,16 @@ import { */ export namespace oidcSignInResolvers { /** - * Looks up the user by matching their Oidc username to the entity name. + * A oidc resolver that looks up the user using the local part of + * their email address as the entity name. */ - export const usernameMatchingUserEntityName = createSignInResolverFactory({ - create() { - return async ( - info: SignInInfo>, - ctx, - ) => { - const { result } = info; - - const id = result.fullProfile.username; - if (!id) { - throw new Error(`Oidc user profile does not contain a username`); - } - - return ctx.signInWithCatalogUser({ entityRef: { name: id } }); - }; - }, - }); + export const emailLocalPartMatchingUserEntityName = + commonSignInResolvers.emailLocalPartMatchingUserEntityName; /** - * Looks up the user by matching their email to the `google.com/email` annotation. Still working out this resolver..... + * A oidc resolver that looks up the user using their email address + * as email of the entity. */ - export const emailMatchingUserEntityAnnotation = createSignInResolverFactory({ - create() { - return async (info: SignInInfo, ctx) => { - const email = info.result.iapToken.email; - - if (!email) { - throw new Error('Google IAP sign-in result is missing email'); - } - - return ctx.signInWithCatalogUser({ - annotations: { - 'google.com/email': email, - }, - }); - }; - }, - }); + export const emailMatchingUserEntityProfileEmail = + commonSignInResolvers.emailMatchingUserEntityProfileEmail; } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index a2b4d01b6c..ca92d4c9dd 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -45,6 +45,7 @@ "@backstage/plugin-auth-backend-module-google-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^", "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^", + "@backstage/plugin-auth-backend-module-oidc-provider": "workspace:^", "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^", "@backstage/plugin-auth-node": "workspace:^", "@backstage/plugin-catalog-node": "workspace:^", diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts deleted file mode 100644 index 7161ef1d6b..0000000000 --- a/plugins/auth-backend/src/providers/oidc/provider.test.ts +++ /dev/null @@ -1,189 +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 { Config, ConfigReader } from '@backstage/config'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; -import express from 'express'; -import { Session } from 'express-session'; -import { UnsecuredJWT } from 'jose'; -import { rest } from 'msw'; -import { setupServer } from 'msw/node'; -import { ClientMetadata, IssuerMetadata } from 'openid-client'; -import { OAuthAdapter } from '../../lib/oauth'; -import { oidc, OidcAuthProvider, Options } from './provider'; -import { AuthResolverContext } from '../types'; - -const issuerMetadata = { - issuer: 'https://oidc.test', - authorization_endpoint: 'https://oidc.test/as/authorization.oauth2', - token_endpoint: 'https://oidc.test/as/token.oauth2', - revocation_endpoint: 'https://oidc.test/as/revoke_token.oauth2', - userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', - introspection_endpoint: 'https://oidc.test/as/introspect.oauth2', - jwks_uri: 'https://oidc.test/pf/JWKS', - scopes_supported: ['openid'], - claims_supported: ['email'], - response_types_supported: ['code'], - id_token_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], - token_endpoint_auth_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], - request_object_signing_alg_values_supported: ['RS256', 'RS512', 'HS256'], -}; - -const clientMetadata: Options = { - authHandler: async input => ({ - profile: { - displayName: input.userinfo.email, - }, - }), - resolverContext: {} as AuthResolverContext, - callbackUrl: 'https://oidc.test/callback', - clientId: 'testclientid', - clientSecret: 'testclientsecret', - metadataUrl: 'https://oidc.test/.well-known/openid-configuration', - tokenEndpointAuthMethod: 'none', - tokenSignedResponseAlg: 'none', -}; - -describe('OidcAuthProvider', () => { - const worker = setupServer(); - setupRequestMockHandlers(worker); - - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('hit the metadata url', async () => { - const handler = jest.fn((_req, res, ctx) => { - return res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(issuerMetadata), - ); - }); - worker.use( - rest.get('https://oidc.test/.well-known/openid-configuration', handler), - ); - const provider = new OidcAuthProvider(clientMetadata); - const { strategy } = (await (provider as any).implementation) as any as { - strategy: { - _client: ClientMetadata; - _issuer: IssuerMetadata; - }; - }; - // Assert that the expected request to the metadaurl was made. - expect(handler).toHaveBeenCalledTimes(1); - const { _client, _issuer } = strategy; - expect(_client.client_id).toBe(clientMetadata.clientId); - expect(_issuer.token_endpoint).toBe(issuerMetadata.token_endpoint); - }); - - it('OidcAuthProvider#handler successfully invokes the oidc endpoints', async () => { - const sub = 'alice'; - const iss = 'https://oidc.test'; - const iat = Date.now(); - const aud = clientMetadata.clientId; - const exp = Date.now() + 10000; - const jwt = await new UnsecuredJWT({ iss, sub, aud, iat, exp }) - .setIssuer(iss) - .setAudience(aud) - .setSubject(sub) - .setIssuedAt(iat) - .setExpirationTime(exp) - .encode(); - const requestSequence: Array = []; - - // The array of expected requests executed by the provider handler - const requests: Array<{ - method: 'get' | 'post'; - url: string; - payload: object; - }> = [ - { - method: 'get', - url: 'https://oidc.test/.well-known/openid-configuration', - payload: issuerMetadata, - }, - { - method: 'post', - url: 'https://oidc.test/as/token.oauth2', - payload: { - id_token: jwt, - access_token: 'test', - authorization_signed_response_alg: 'HS256', - }, - }, - { - method: 'get', - url: 'https://oidc.test/idp/userinfo.openid', - payload: { - sub: 'alice', - email: 'alice@oidc.test', - }, - }, - ]; - worker.use( - ...requests.map(r => { - return rest[r.method](r.url, (_req, res, ctx) => { - requestSequence.push(r.url); - return res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(r.payload), - ); - }); - }), - ); - const provider = new OidcAuthProvider(clientMetadata); - const req = { - method: 'GET', - url: 'https://oidc.test/?code=test2', - session: { 'oidc:oidc.test': 'test' } as any as Session, - } as express.Request; - await provider.handler(req); - expect(requestSequence).toEqual([0, 1, 2].map(i => requests[i].url)); - }); - - it('oidc.create', async () => { - const handler = jest.fn((_req, res, ctx) => { - return res( - ctx.status(200), - ctx.set('Content-Type', 'application/json'), - ctx.json(issuerMetadata), - ); - }); - worker.use( - rest.get('https://oidc.test/.well-known/openid-configuration', handler), - ); - const config: Config = new ConfigReader({ - testEnv: { - ...clientMetadata, - metadataUrl: 'https://oidc.test/.well-known/openid-configuration', - }, - } as any); - const provider = oidc.create()({ - globalConfig: { - appUrl: 'https://oidc.test', - baseUrl: 'https://oidc.test', - }, - config, - } as any) as OAuthAdapter; - expect(provider.start).toBeDefined(); - // Cast provider as any here to be able to inspect private members - await (provider as any).handlers.get('testEnv').handlers.implementation; - // Assert that the expected request to the metadaurl was made. - expect(handler).toHaveBeenCalledTimes(1); - }); -}); diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index b6608aebbd..ce8809c55a 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -14,278 +14,282 @@ * limitations under the License. */ -import express from 'express'; -import { - Client, - ClientAuthMethod, - Issuer, - Strategy as OidcStrategy, - TokenSet, - UserinfoResponse, -} from 'openid-client'; -import { - encodeState, - OAuthAdapter, - OAuthEnvironmentHandler, - OAuthHandlers, - OAuthProviderOptions, - OAuthRefreshRequest, - OAuthResponse, - OAuthStartRequest, -} from '../../lib/oauth'; -import { - executeFrameHandlerStrategy, - executeRedirectStrategy, - PassportDoneCallback, -} from '../../lib/passport'; -import { - AuthHandler, - AuthResolverContext, - OAuthStartResponse, - SignInResolver, -} from '../types'; +import { AuthHandler, SignInResolver } from '../types'; +import { OAuthResult } from '../../lib/oauth'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; +import { createOAuthProviderFactory } from '@backstage/plugin-auth-node'; import { - commonByEmailLocalPartResolver, - commonByEmailResolver, -} from '../resolvers'; -import { BACKSTAGE_SESSION_EXPIRATION } from '../../lib/session'; + adaptLegacyOAuthHandler, + adaptLegacyOAuthSignInResolver, +} from '../../lib/legacy'; -type PrivateInfo = { - refreshToken?: string; -}; +import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; -type OidcImpl = { - strategy: OidcStrategy; - client: Client; -}; +// type PrivateInfo = { +// refreshToken?: string; +// }; -/** - * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) - * @public - */ -export type OidcAuthResult = { - tokenset: TokenSet; - userinfo: UserinfoResponse; -}; +// type OidcImpl = { +// strategy: OidcStrategy; +// client: Client; +// }; -export type Options = OAuthProviderOptions & { - metadataUrl: string; - scope?: string; - prompt?: string; - tokenEndpointAuthMethod?: ClientAuthMethod; - tokenSignedResponseAlg?: string; - signInResolver?: SignInResolver; - authHandler: AuthHandler; - resolverContext: AuthResolverContext; -}; +// /** +// * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) +// * @public +// */ +// export type OidcAuthResult = { +// tokenset: TokenSet; +// userinfo: UserinfoResponse; +// }; -export class OidcAuthProvider implements OAuthHandlers { - private readonly implementation: Promise; - private readonly scope?: string; - private readonly prompt?: string; +// export type Options = OAuthProviderOptions & { +// metadataUrl: string; +// scope?: string; +// prompt?: string; +// tokenEndpointAuthMethod?: ClientAuthMethod; +// tokenSignedResponseAlg?: string; +// signInResolver?: SignInResolver; +// authHandler: AuthHandler; +// resolverContext: AuthResolverContext; +// }; - private readonly signInResolver?: SignInResolver; - private readonly authHandler: AuthHandler; - private readonly resolverContext: AuthResolverContext; +// export class OidcAuthProvider implements OAuthHandlers { +// private readonly implementation: Promise; +// private readonly scope?: string; +// private readonly prompt?: string; - constructor(options: Options) { - this.implementation = this.setupStrategy(options); - this.scope = options.scope; - this.prompt = options.prompt; - this.signInResolver = options.signInResolver; - this.authHandler = options.authHandler; - this.resolverContext = options.resolverContext; - } +// private readonly signInResolver?: SignInResolver; +// private readonly authHandler: AuthHandler; +// private readonly resolverContext: AuthResolverContext; - async start(req: OAuthStartRequest): Promise { - const { strategy } = await this.implementation; - const options: Record = { - scope: req.scope || this.scope || 'openid profile email', - state: encodeState(req.state), - }; - const prompt = this.prompt || 'none'; - if (prompt !== 'auto') { - options.prompt = prompt; - } - return await executeRedirectStrategy(req, strategy, options); - } +// constructor(options: Options) { +// this.implementation = this.setupStrategy(options); +// this.scope = options.scope; +// this.prompt = options.prompt; +// this.signInResolver = options.signInResolver; +// this.authHandler = options.authHandler; +// this.resolverContext = options.resolverContext; +// } - async handler(req: express.Request) { - const { strategy } = await this.implementation; - const { result, privateInfo } = await executeFrameHandlerStrategy< - OidcAuthResult, - PrivateInfo - >(req, strategy); +// async start(req: OAuthStartRequest): Promise { +// const { strategy } = await this.implementation; +// const options: Record = { +// scope: req.scope || this.scope || 'openid profile email', +// state: encodeState(req.state), +// }; +// const prompt = this.prompt || 'none'; +// if (prompt !== 'auto') { +// options.prompt = prompt; +// } +// return await executeRedirectStrategy(req, strategy, options); +// } - return { - response: await this.handleResult(result), - refreshToken: privateInfo.refreshToken, - }; - } +// async handler(req: express.Request) { +// const { strategy } = await this.implementation; +// const { result, privateInfo } = await executeFrameHandlerStrategy< +// OidcAuthResult, +// PrivateInfo +// >(req, strategy); - async refresh(req: OAuthRefreshRequest) { - const { client } = await this.implementation; - const tokenset = await client.refresh(req.refreshToken); - if (!tokenset.access_token) { - throw new Error('Refresh failed'); - } - if (!tokenset.scope) { - tokenset.scope = req.scope; - } - const userinfo = await client.userinfo(tokenset.access_token); + // async refresh(req: OAuthRefreshRequest) { + // const { client } = await this.implementation; + // const tokenset = await client.refresh(req.refreshToken); + // if (!tokenset.access_token) { + // throw new Error('Refresh failed'); + // } + // if (!tokenset.scope) { + // tokenset.scope = req.scope; + // } + // const userinfo = await client.userinfo(tokenset.access_token); +// return { +// response: await this.handleResult(result), +// refreshToken: privateInfo.refreshToken, +// }; +// } - return { - response: await this.handleResult({ tokenset, userinfo }), - refreshToken: tokenset.refresh_token, - }; - } +// async refresh(req: OAuthRefreshRequest) { +// const { client } = await this.implementation; +// const tokenset = await client.refresh(req.refreshToken); +// if (!tokenset.access_token) { +// throw new Error('Refresh failed'); +// } +// const userinfo = await client.userinfo(tokenset.access_token); - private async setupStrategy(options: Options): Promise { - const issuer = await Issuer.discover(options.metadataUrl); - const client = new issuer.Client({ - access_type: 'offline', // this option must be passed to provider to receive a refresh token - client_id: options.clientId, - client_secret: options.clientSecret, - redirect_uris: [options.callbackUrl], - response_types: ['code'], - token_endpoint_auth_method: - options.tokenEndpointAuthMethod || 'client_secret_basic', - id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256', - scope: options.scope || '', - }); +// return { +// response: await this.handleResult({ tokenset, userinfo }), +// refreshToken: tokenset.refresh_token, +// }; +// } - const strategy = new OidcStrategy( - { - client, - passReqToCallback: false, - }, - ( - tokenset: TokenSet, - userinfo: UserinfoResponse, - done: PassportDoneCallback, - ) => { - if (typeof done !== 'function') { - throw new Error( - 'OIDC IdP must provide a userinfo_endpoint in the metadata response', - ); - } - done( - undefined, - { tokenset, userinfo }, - { - refreshToken: tokenset.refresh_token, - }, - ); - }, - ); - strategy.error = console.error; - return { strategy, client }; - } +// private async setupStrategy(options: Options): Promise { +// const issuer = await Issuer.discover(options.metadataUrl); +// const client = new issuer.Client({ +// access_type: 'offline', // this option must be passed to provider to receive a refresh token +// client_id: options.clientId, +// client_secret: options.clientSecret, +// redirect_uris: [options.callbackUrl], +// response_types: ['code'], +// token_endpoint_auth_method: +// options.tokenEndpointAuthMethod || 'client_secret_basic', +// id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256', +// scope: options.scope || '', +// }); - // Use this function to grab the user profile info from the token - // Then populate the profile with it - private async handleResult(result: OidcAuthResult): Promise { - const { profile } = await this.authHandler(result, this.resolverContext); +// const strategy = new OidcStrategy( +// { +// client, +// passReqToCallback: false, +// }, +// ( +// tokenset: TokenSet, +// userinfo: UserinfoResponse, +// done: PassportDoneCallback, +// ) => { +// if (typeof done !== 'function') { +// throw new Error( +// 'OIDC IdP must provide a userinfo_endpoint in the metadata response', +// ); +// } +// done( +// undefined, +// { tokenset, userinfo }, +// { +// refreshToken: tokenset.refresh_token, +// }, +// ); +// }, +// ); +// strategy.error = console.error; +// return { strategy, client }; +// } - const expiresInSeconds = - result.tokenset.expires_in === undefined - ? BACKSTAGE_SESSION_EXPIRATION - : Math.min(result.tokenset.expires_in, BACKSTAGE_SESSION_EXPIRATION); +// Use this function to grab the user profile info from the token +// Then populate the profile with it +// private async handleResult(result: OidcAuthResult): Promise { +// const { profile } = await this.authHandler(result, this.resolverContext); - let backstageIdentity = undefined; - if (this.signInResolver) { - backstageIdentity = await this.signInResolver( - { - result, - profile, - }, - this.resolverContext, - ); - } +// const expiresInSeconds = +// result.tokenset.expires_in === undefined +// ? BACKSTAGE_SESSION_EXPIRATION +// : Math.min(result.tokenset.expires_in, BACKSTAGE_SESSION_EXPIRATION); - return { - backstageIdentity, - providerInfo: { - idToken: result.tokenset.id_token, - accessToken: result.tokenset.access_token!, - scope: result.tokenset.scope!, - expiresInSeconds, - }, - profile, - }; - } -} +// let backstageIdentity = undefined; +// if (this.signInResolver) { +// backstageIdentity = await this.signInResolver( +// { +// result, +// profile, +// }, +// this.resolverContext, +// ); +// } + +// return { +// backstageIdentity, +// providerInfo: { +// idToken: result.tokenset.id_token, +// accessToken: result.tokenset.access_token!, +// scope: result.tokenset.scope!, +// expiresInSeconds, +// }, +// profile, +// }; +// } +// } /** * Auth provider integration for generic OpenID Connect auth * * @public */ +// export const oidc = createAuthProviderIntegration({ +// create(options?: { +// authHandler?: AuthHandler; + +// signIn?: { +// resolver: SignInResolver; +// }; +// }) { +// return ({ providerId, globalConfig, config, resolverContext }) => +// OAuthEnvironmentHandler.mapConfig(config, envConfig => { +// const clientId = envConfig.getString('clientId'); +// const clientSecret = envConfig.getString('clientSecret'); +// const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); +// const callbackUrl = +// customCallbackUrl || +// `${globalConfig.baseUrl}/${providerId}/handler/frame`; +// const metadataUrl = envConfig.getString('metadataUrl'); +// const tokenEndpointAuthMethod = envConfig.getOptionalString( +// 'tokenEndpointAuthMethod', +// ) as ClientAuthMethod; +// const tokenSignedResponseAlg = envConfig.getOptionalString( +// 'tokenSignedResponseAlg', +// ); +// const scope = envConfig.getOptionalString('scope'); +// const prompt = envConfig.getOptionalString('prompt'); + +// const authHandler: AuthHandler = options?.authHandler +// ? options.authHandler +// : async ({ userinfo }) => ({ +// profile: { +// displayName: userinfo.name, +// email: userinfo.email, +// picture: userinfo.picture, +// }, +// }); + +// const provider = new OidcAuthProvider({ +// clientId, +// clientSecret, +// callbackUrl, +// tokenEndpointAuthMethod, +// tokenSignedResponseAlg, +// metadataUrl, +// scope, +// prompt, +// signInResolver: options?.signIn?.resolver, +// authHandler, +// resolverContext, +// }); + +// return OAuthAdapter.fromConfig(globalConfig, provider, { +// providerId, +// callbackUrl, +// }); +// }); +// }, +// resolvers: { +// /** +// * Looks up the user by matching their email local part to the entity name. +// */ +// emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, +// /** +// * Looks up the user by matching their email to the entity email. +// */ +// emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, +// }, +// }); + export const oidc = createAuthProviderIntegration({ create(options?: { - authHandler?: AuthHandler; + /** + * 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. + */ signIn?: { - resolver: SignInResolver; + resolver: SignInResolver; }; }) { - return ({ providerId, globalConfig, config, resolverContext }) => - OAuthEnvironmentHandler.mapConfig(config, envConfig => { - const clientId = envConfig.getString('clientId'); - const clientSecret = envConfig.getString('clientSecret'); - const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); - const callbackUrl = - customCallbackUrl || - `${globalConfig.baseUrl}/${providerId}/handler/frame`; - const metadataUrl = envConfig.getString('metadataUrl'); - const tokenEndpointAuthMethod = envConfig.getOptionalString( - 'tokenEndpointAuthMethod', - ) as ClientAuthMethod; - const tokenSignedResponseAlg = envConfig.getOptionalString( - 'tokenSignedResponseAlg', - ); - const scope = envConfig.getOptionalString('scope'); - const prompt = envConfig.getOptionalString('prompt'); - - const authHandler: AuthHandler = options?.authHandler - ? options.authHandler - : async ({ userinfo }) => ({ - profile: { - displayName: userinfo.name, - email: userinfo.email, - picture: userinfo.picture, - }, - }); - - const provider = new OidcAuthProvider({ - clientId, - clientSecret, - callbackUrl, - tokenEndpointAuthMethod, - tokenSignedResponseAlg, - metadataUrl, - scope, - prompt, - signInResolver: options?.signIn?.resolver, - authHandler, - resolverContext, - }); - - return OAuthAdapter.fromConfig(globalConfig, provider, { - providerId, - callbackUrl, - }); - }); - }, - resolvers: { - /** - * Looks up the user by matching their email local part to the entity name. - */ - emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, - /** - * Looks up the user by matching their email to the entity email. - */ - emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, + return createOAuthProviderFactory({ + authenticator: oidcAuthenticator, + profileTransform: adaptLegacyOAuthHandler(options?.authHandler), + signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + }); }, }); diff --git a/yarn.lock b/yarn.lock index 6e6721b99e..16979362ca 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4672,7 +4672,7 @@ __metadata: languageName: unknown linkType: soft -"@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider": +"@backstage/plugin-auth-backend-module-oidc-provider@workspace:^, @backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider": version: 0.0.0-use.local resolution: "@backstage/plugin-auth-backend-module-oidc-provider@workspace:plugins/auth-backend-module-oidc-provider" dependencies: @@ -4777,6 +4777,7 @@ __metadata: "@backstage/plugin-auth-backend-module-google-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-provider": "workspace:^" "@backstage/plugin-auth-backend-module-oauth2-proxy-provider": "workspace:^" + "@backstage/plugin-auth-backend-module-oidc-provider": "workspace:^" "@backstage/plugin-auth-backend-module-okta-provider": "workspace:^" "@backstage/plugin-auth-node": "workspace:^" "@backstage/plugin-catalog-node": "workspace:^" @@ -31276,13 +31277,20 @@ __metadata: languageName: node linkType: hard -"jose@npm:^4.14.6, jose@npm:^4.15.4, jose@npm:^4.6.0": +"jose@npm:^4.14.6, jose@npm:^4.15.1": version: 4.15.4 resolution: "jose@npm:4.15.4" checksum: dccad91cb3357f36423774a0b89ad830dd84b31090de65cd139b85488439f16a00f8c59c0773825e8a1adb0dd9d13ad725ad66e6ea33880ecb3959bb99e1ea5b languageName: node linkType: hard +"jose@npm:^4.6.0": + version: 4.15.2 + resolution: "jose@npm:4.15.2" + checksum: 8f0cab1eef31243abe14a935b2b330cd95f10f9b69808fd642088ae5000e50e566664934537d2c6413ab2f6b54acd8265a5033da05157aa1260c5f1d7e57fab0 + languageName: node + linkType: hard + "joycon@npm:^3.0.1": version: 3.1.0 resolution: "joycon@npm:3.1.0" @@ -35655,19 +35663,7 @@ __metadata: languageName: node linkType: hard -"openid-client@npm:^5.2.1, openid-client@npm:^5.3.0, openid-client@npm:^5.4.3": - version: 5.6.4 - resolution: "openid-client@npm:5.6.4" - dependencies: - jose: ^4.15.4 - lru-cache: ^6.0.0 - object-hash: ^2.2.0 - oidc-token-hash: ^5.0.3 - checksum: 69843f078dacbbc6bad6d65ca6689414ac73f095dfe2f8e606822e6cfc9d9cd7d0dfaf2649352eda604653806f0ea65326ad2d6266da897e4740ec93d26d21f6 - languageName: node - linkType: hard - -"openid-client@npm:^5.5.0": +"openid-client@npm:^5.2.1, openid-client@npm:^5.3.0, openid-client@npm:^5.5.0": version: 5.5.0 resolution: "openid-client@npm:5.5.0" dependencies: @@ -35679,6 +35675,18 @@ __metadata: languageName: node linkType: hard +"openid-client@npm:^5.4.3": + version: 5.6.1 + resolution: "openid-client@npm:5.6.1" + dependencies: + jose: ^4.15.1 + lru-cache: ^6.0.0 + object-hash: ^2.2.0 + oidc-token-hash: ^5.0.3 + checksum: 9d939cec57540e6dd3f67e9a248ec5ecec3b439b7ab5bd2e9fb4481bd03e8d030deedd87a447348194be7f3e93e84085841b0414033caf86479870f526cdbc2f + languageName: node + linkType: hard + "oppa@npm:^0.4.0": version: 0.4.0 resolution: "oppa@npm:0.4.0" From 1964cb7d8805f4b78a75a920c411b68b4cbf9a39 Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Thu, 28 Sep 2023 10:59:18 -0400 Subject: [PATCH 067/152] Working oidc authenticator with passing unit and module tests Signed-off-by: Ruben Vallejo --- .../config.d.ts | 1 + .../dev/index.ts | 6 +- .../src/authenticator.test.ts | 53 +++- .../src/authenticator.ts | 32 ++- .../src/index.ts | 2 +- .../src/module.test.ts | 8 +- .../auth-backend/src/providers/oidc/index.ts | 1 - .../src/providers/oidc/provider.ts | 242 ------------------ 8 files changed, 84 insertions(+), 261 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/config.d.ts b/plugins/auth-backend-module-oidc-provider/config.d.ts index 803b9add73..16131c0761 100644 --- a/plugins/auth-backend-module-oidc-provider/config.d.ts +++ b/plugins/auth-backend-module-oidc-provider/config.d.ts @@ -27,6 +27,7 @@ export interface Config { clientSecret: string; metadataUrl: string; callbackUrl?: string; + tokenEndpointAuthMethod?: string; tokenSignedResponseAlg?: string; scope?: string; prompt?: string; diff --git a/plugins/auth-backend-module-oidc-provider/dev/index.ts b/plugins/auth-backend-module-oidc-provider/dev/index.ts index 4d027a19c2..d3c18c1d48 100644 --- a/plugins/auth-backend-module-oidc-provider/dev/index.ts +++ b/plugins/auth-backend-module-oidc-provider/dev/index.ts @@ -15,12 +15,10 @@ */ import { createBackend } from '@backstage/backend-defaults'; -import { authPlugin } from '@backstage/plugin-auth-backend'; -import { authModuleOidcProvider } from '../src'; const backend = createBackend(); -backend.add(authPlugin); -backend.add(authModuleOidcProvider); +backend.add(import('@backstage/plugin-auth-backend')); +backend.add(import('../src')); backend.start(); diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index 6f22517361..4d21db8442 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -103,7 +103,7 @@ describe('oidcAuthenticator', () => { id_token: idToken, refresh_token: 'refreshToken', scope: 'testScope', - token_type: '', + expires_in: 3600, }) : ctx.status(401), ); @@ -119,6 +119,7 @@ describe('oidcAuthenticator', () => { given_name: 'Alice', family_name: 'Adams', email: 'alice@test.com', + picture: 'http://testPictureUrl/photo.jpg', }), ), ), @@ -127,7 +128,7 @@ describe('oidcAuthenticator', () => { implementation = oidcAuthenticator.initialize({ callbackUrl: 'https://backstage.test/callback', config: new ConfigReader({ - metadataUrl: 'https://oidc.test', + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', clientId: 'clientId', clientSecret: 'clientSecret', }), @@ -219,7 +220,6 @@ describe('oidcAuthenticator', () => { expect(fakeSession['oidc:oidc.test'].code_verifier).toBeDefined(); }); - // without the offline_access scope refresh should not work should we add in the test that is a required scope to test for? curently dont see that by defualt in the provider implementation. it('requests default scopes if none are provided in config', async () => { const startResponse = await oidcAuthenticator.start( startRequest, @@ -310,6 +310,53 @@ describe('oidcAuthenticator', () => { expect(responseScope).toEqual('testScope'); }); + it('returns a default session.tokentype field', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const tokenType = handlerResponse.session.tokenType; + + expect(tokenType).toEqual('bearer'); + }); + + it('returns defined fullProfile with picture and email', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + const displayName = handlerResponse.fullProfile.displayName; + const email = handlerResponse.fullProfile.emails![0].value; + const picture = handlerResponse.fullProfile.photos![0].value; + + expect(displayName).toEqual('Alice Adams'); + expect(email).toEqual('alice@test.com'); + expect(picture).toEqual('http://testPictureUrl/photo.jpg'); + }); + + it('returns defined response with an idToken', async () => { + const handlerResponse = await oidcAuthenticator.authenticate( + handlerRequest, + implementation, + ); + + expect(handlerResponse).toMatchObject({ + fullProfile: { + displayName: 'Alice Adams', + id: 'test', + provider: 'oidc', + }, + session: { + accessToken: 'accessToken', + expiresInSeconds: 3600, + idToken, + refreshToken: 'refreshToken', + scope: 'testScope', + tokenType: 'bearer', + }, + }); + }); + it('fails without authorization code', async () => { handlerRequest.req.url = 'https://test.com'; return expect( diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index 7a7ebafe5c..a95010e353 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -26,7 +26,6 @@ import { PassportOAuthAuthenticatorHelper, PassportOAuthDoneCallback, } from '@backstage/plugin-auth-node'; -// import { BACKSTAGE_SESSION_EXPIRATION } from '@backstage/plugin-auth-backend/src/lib/session'; /** @public */ export const oidcAuthenticator = createOAuthAuthenticator({ @@ -46,9 +45,8 @@ export const oidcAuthenticator = createOAuthAuthenticator({ ); const initializedScope = config.getOptionalString('scope'); const initializedPrompt = config.getOptionalString('prompt'); - const issuer = await Issuer.discover( - `${metadataUrl}/.well-known/openid-configuration`, - ); + + const issuer = await Issuer.discover(metadataUrl); const client = new issuer.Client({ access_type: 'offline', // this option must be passed to provider to receive a refresh token client_id: clientId, @@ -77,9 +75,18 @@ export const oidcAuthenticator = createOAuthAuthenticator({ ); } - const expiresInSeconds = !tokenset.expires_in - ? 3600 - : Math.min(tokenset.expires_in!, 3600); + const emails = userinfo.email ? [{ value: userinfo.email }] : undefined; + const photos = userinfo.picture + ? [{ value: userinfo.picture }] + : undefined; + const name = + userinfo.family_name && userinfo.given_name + ? { + familyName: userinfo.family_name, + givenName: userinfo.given_name, + middleName: userinfo.middle_name, + } + : undefined; done( undefined, @@ -88,11 +95,17 @@ export const oidcAuthenticator = createOAuthAuthenticator({ provider: 'oidc', id: userinfo.sub, displayName: userinfo.name!, + username: userinfo.preferred_username, + name, + emails, + photos, }, accessToken: tokenset.access_token!, params: { + id_token: tokenset.id_token, scope: tokenset.scope!, - expires_in: expiresInSeconds, + expires_in: tokenset.expires_in!, + token_type: tokenset.token_type, }, }, { @@ -139,6 +152,9 @@ export const oidcAuthenticator = createOAuthAuthenticator({ if (!tokenset.access_token) { throw new Error('Refresh failed'); } + if (!tokenset.scope) { + tokenset.scope = input.scope; + } const userinfo = await client.userinfo(tokenset.access_token); return new Promise((resolve, reject) => { diff --git a/plugins/auth-backend-module-oidc-provider/src/index.ts b/plugins/auth-backend-module-oidc-provider/src/index.ts index 4b5ddc123a..b14d350d4d 100644 --- a/plugins/auth-backend-module-oidc-provider/src/index.ts +++ b/plugins/auth-backend-module-oidc-provider/src/index.ts @@ -21,5 +21,5 @@ */ export { oidcAuthenticator } from './authenticator'; -export { authModuleOidcProvider } from './module'; +export { authModuleOidcProvider as default } from './module'; export { oidcSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index 5e01ae343f..09ad38b84c 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -17,7 +17,6 @@ import request from 'supertest'; import { AuthProviderRouteHandlers, - AuthResolverContext, createOAuthRouteHandlers, decodeOAuthState, } from '@backstage/plugin-auth-node'; @@ -121,6 +120,7 @@ describe('authModuleOidcProvider', () => { refresh_token: 'refreshToken', scope: 'testScope', token_type: '', + expires_in: 3600, }) : ctx.status(401), ); @@ -136,6 +136,7 @@ describe('authModuleOidcProvider', () => { given_name: 'Alice', family_name: 'Adams', email: 'alice@test.com', + picture: 'http://testPictureUrl/photo.jpg', }), ), ), @@ -172,7 +173,7 @@ describe('authModuleOidcProvider', () => { isOriginAllowed: _ => true, providerId: 'oidc', config: new ConfigReader({ - metadataUrl: 'https://oidc.test', + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', clientId: 'clientId', clientSecret: 'clientSecret', }), @@ -264,9 +265,12 @@ describe('authModuleOidcProvider', () => { type: 'authorization_response', response: { profile: { + email: 'alice@test.com', + picture: 'http://testPictureUrl/photo.jpg', displayName: 'Alice Adams', }, providerInfo: { + idToken, accessToken: 'accessToken', scope: 'testScope', expiresInSeconds: 3600, diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index 6b2282411c..c4343b1547 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -15,4 +15,3 @@ */ export { oidc } from './provider'; -export type { OidcAuthResult } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index ce8809c55a..18170e104e 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -22,255 +22,13 @@ import { adaptLegacyOAuthHandler, adaptLegacyOAuthSignInResolver, } from '../../lib/legacy'; - import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; -// type PrivateInfo = { -// refreshToken?: string; -// }; - -// type OidcImpl = { -// strategy: OidcStrategy; -// client: Client; -// }; - -// /** -// * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) -// * @public -// */ -// export type OidcAuthResult = { -// tokenset: TokenSet; -// userinfo: UserinfoResponse; -// }; - -// export type Options = OAuthProviderOptions & { -// metadataUrl: string; -// scope?: string; -// prompt?: string; -// tokenEndpointAuthMethod?: ClientAuthMethod; -// tokenSignedResponseAlg?: string; -// signInResolver?: SignInResolver; -// authHandler: AuthHandler; -// resolverContext: AuthResolverContext; -// }; - -// export class OidcAuthProvider implements OAuthHandlers { -// private readonly implementation: Promise; -// private readonly scope?: string; -// private readonly prompt?: string; - -// private readonly signInResolver?: SignInResolver; -// private readonly authHandler: AuthHandler; -// private readonly resolverContext: AuthResolverContext; - -// constructor(options: Options) { -// this.implementation = this.setupStrategy(options); -// this.scope = options.scope; -// this.prompt = options.prompt; -// this.signInResolver = options.signInResolver; -// this.authHandler = options.authHandler; -// this.resolverContext = options.resolverContext; -// } - -// async start(req: OAuthStartRequest): Promise { -// const { strategy } = await this.implementation; -// const options: Record = { -// scope: req.scope || this.scope || 'openid profile email', -// state: encodeState(req.state), -// }; -// const prompt = this.prompt || 'none'; -// if (prompt !== 'auto') { -// options.prompt = prompt; -// } -// return await executeRedirectStrategy(req, strategy, options); -// } - -// async handler(req: express.Request) { -// const { strategy } = await this.implementation; -// const { result, privateInfo } = await executeFrameHandlerStrategy< -// OidcAuthResult, -// PrivateInfo -// >(req, strategy); - - // async refresh(req: OAuthRefreshRequest) { - // const { client } = await this.implementation; - // const tokenset = await client.refresh(req.refreshToken); - // if (!tokenset.access_token) { - // throw new Error('Refresh failed'); - // } - // if (!tokenset.scope) { - // tokenset.scope = req.scope; - // } - // const userinfo = await client.userinfo(tokenset.access_token); -// return { -// response: await this.handleResult(result), -// refreshToken: privateInfo.refreshToken, -// }; -// } - -// async refresh(req: OAuthRefreshRequest) { -// const { client } = await this.implementation; -// const tokenset = await client.refresh(req.refreshToken); -// if (!tokenset.access_token) { -// throw new Error('Refresh failed'); -// } -// const userinfo = await client.userinfo(tokenset.access_token); - -// return { -// response: await this.handleResult({ tokenset, userinfo }), -// refreshToken: tokenset.refresh_token, -// }; -// } - -// private async setupStrategy(options: Options): Promise { -// const issuer = await Issuer.discover(options.metadataUrl); -// const client = new issuer.Client({ -// access_type: 'offline', // this option must be passed to provider to receive a refresh token -// client_id: options.clientId, -// client_secret: options.clientSecret, -// redirect_uris: [options.callbackUrl], -// response_types: ['code'], -// token_endpoint_auth_method: -// options.tokenEndpointAuthMethod || 'client_secret_basic', -// id_token_signed_response_alg: options.tokenSignedResponseAlg || 'RS256', -// scope: options.scope || '', -// }); - -// const strategy = new OidcStrategy( -// { -// client, -// passReqToCallback: false, -// }, -// ( -// tokenset: TokenSet, -// userinfo: UserinfoResponse, -// done: PassportDoneCallback, -// ) => { -// if (typeof done !== 'function') { -// throw new Error( -// 'OIDC IdP must provide a userinfo_endpoint in the metadata response', -// ); -// } -// done( -// undefined, -// { tokenset, userinfo }, -// { -// refreshToken: tokenset.refresh_token, -// }, -// ); -// }, -// ); -// strategy.error = console.error; -// return { strategy, client }; -// } - -// Use this function to grab the user profile info from the token -// Then populate the profile with it -// private async handleResult(result: OidcAuthResult): Promise { -// const { profile } = await this.authHandler(result, this.resolverContext); - -// const expiresInSeconds = -// result.tokenset.expires_in === undefined -// ? BACKSTAGE_SESSION_EXPIRATION -// : Math.min(result.tokenset.expires_in, BACKSTAGE_SESSION_EXPIRATION); - -// let backstageIdentity = undefined; -// if (this.signInResolver) { -// backstageIdentity = await this.signInResolver( -// { -// result, -// profile, -// }, -// this.resolverContext, -// ); -// } - -// return { -// backstageIdentity, -// providerInfo: { -// idToken: result.tokenset.id_token, -// accessToken: result.tokenset.access_token!, -// scope: result.tokenset.scope!, -// expiresInSeconds, -// }, -// profile, -// }; -// } -// } - /** * Auth provider integration for generic OpenID Connect auth * * @public */ -// export const oidc = createAuthProviderIntegration({ -// create(options?: { -// authHandler?: AuthHandler; - -// signIn?: { -// resolver: SignInResolver; -// }; -// }) { -// return ({ providerId, globalConfig, config, resolverContext }) => -// OAuthEnvironmentHandler.mapConfig(config, envConfig => { -// const clientId = envConfig.getString('clientId'); -// const clientSecret = envConfig.getString('clientSecret'); -// const customCallbackUrl = envConfig.getOptionalString('callbackUrl'); -// const callbackUrl = -// customCallbackUrl || -// `${globalConfig.baseUrl}/${providerId}/handler/frame`; -// const metadataUrl = envConfig.getString('metadataUrl'); -// const tokenEndpointAuthMethod = envConfig.getOptionalString( -// 'tokenEndpointAuthMethod', -// ) as ClientAuthMethod; -// const tokenSignedResponseAlg = envConfig.getOptionalString( -// 'tokenSignedResponseAlg', -// ); -// const scope = envConfig.getOptionalString('scope'); -// const prompt = envConfig.getOptionalString('prompt'); - -// const authHandler: AuthHandler = options?.authHandler -// ? options.authHandler -// : async ({ userinfo }) => ({ -// profile: { -// displayName: userinfo.name, -// email: userinfo.email, -// picture: userinfo.picture, -// }, -// }); - -// const provider = new OidcAuthProvider({ -// clientId, -// clientSecret, -// callbackUrl, -// tokenEndpointAuthMethod, -// tokenSignedResponseAlg, -// metadataUrl, -// scope, -// prompt, -// signInResolver: options?.signIn?.resolver, -// authHandler, -// resolverContext, -// }); - -// return OAuthAdapter.fromConfig(globalConfig, provider, { -// providerId, -// callbackUrl, -// }); -// }); -// }, -// resolvers: { -// /** -// * Looks up the user by matching their email local part to the entity name. -// */ -// emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, -// /** -// * Looks up the user by matching their email to the entity email. -// */ -// emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, -// }, -// }); - export const oidc = createAuthProviderIntegration({ create(options?: { /** From 5d2fcba0646b95ad4e81fdd5e46e0f6c3448dd5d Mon Sep 17 00:00:00 2001 From: Ruben Vallejo Date: Fri, 29 Sep 2023 12:30:50 -0400 Subject: [PATCH 068/152] PR chores, changeset,apireport Signed-off-by: Ruben Vallejo --- .changeset/lemon-cameras-remember.md | 5 +++ .changeset/old-students-smoke.md | 5 +++ .../api-report.md | 42 +++++++++++++++++++ .../package.json | 2 +- .../src/authenticator.test.ts | 4 +- .../src/authenticator.ts | 4 +- .../src/module.test.ts | 23 +--------- plugins/auth-backend/api-report.md | 6 +-- plugins/auth-backend/config.d.ts | 16 ------- .../auth-backend/src/providers/oidc/index.ts | 1 + .../src/providers/oidc/provider.ts | 25 +++++++++++ yarn.lock | 36 +++------------- 12 files changed, 92 insertions(+), 77 deletions(-) create mode 100644 .changeset/lemon-cameras-remember.md create mode 100644 .changeset/old-students-smoke.md create mode 100644 plugins/auth-backend-module-oidc-provider/api-report.md diff --git a/.changeset/lemon-cameras-remember.md b/.changeset/lemon-cameras-remember.md new file mode 100644 index 0000000000..29cd88602f --- /dev/null +++ b/.changeset/lemon-cameras-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend': patch +--- + +Migrated oidc auth provider to new `@backstage/plugin-auth-backend-module-oidc-provider` module package. diff --git a/.changeset/old-students-smoke.md b/.changeset/old-students-smoke.md new file mode 100644 index 0000000000..8e072c5203 --- /dev/null +++ b/.changeset/old-students-smoke.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-auth-backend-module-oidc-provider': minor +--- + +Created new `@backstage/plugin-auth-backend-module-oidc-provider` module package to house oidc auth provider migration. diff --git a/plugins/auth-backend-module-oidc-provider/api-report.md b/plugins/auth-backend-module-oidc-provider/api-report.md new file mode 100644 index 0000000000..cf3335b13b --- /dev/null +++ b/plugins/auth-backend-module-oidc-provider/api-report.md @@ -0,0 +1,42 @@ +## API Report File for "@backstage/plugin-auth-backend-module-oidc-provider" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { BackendFeature } from '@backstage/backend-plugin-api'; +import { BaseClient } from 'openid-client'; +import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; +import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; +import { PassportOAuthResult } from '@backstage/plugin-auth-node'; +import { PassportProfile } from '@backstage/plugin-auth-node'; +import { SignInResolverFactory } from '@backstage/plugin-auth-node'; +import { Strategy } from 'openid-client'; + +// @public (undocumented) +const authModuleOidcProvider: () => BackendFeature; +export default authModuleOidcProvider; + +// @public (undocumented) +export const oidcAuthenticator: OAuthAuthenticator< + Promise<{ + helper: PassportOAuthAuthenticatorHelper; + client: BaseClient; + initializedScope: string | undefined; + initializedPrompt: string | undefined; + strategy: Strategy; + }>, + PassportProfile +>; + +// @public +export namespace oidcSignInResolvers { + const emailLocalPartMatchingUserEntityName: SignInResolverFactory< + unknown, + unknown + >; + const emailMatchingUserEntityProfileEmail: SignInResolverFactory< + unknown, + unknown + >; +} +``` diff --git a/plugins/auth-backend-module-oidc-provider/package.json b/plugins/auth-backend-module-oidc-provider/package.json index 09bd1033a0..6962c1f340 100644 --- a/plugins/auth-backend-module-oidc-provider/package.json +++ b/plugins/auth-backend-module-oidc-provider/package.json @@ -1,7 +1,7 @@ { "name": "@backstage/plugin-auth-backend-module-oidc-provider", "description": "The oidc-provider backend module for the auth plugin.", - "version": "0.1.0-next.1", + "version": "0.0.0", "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index 4d21db8442..b44f89e7c7 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -348,13 +348,15 @@ describe('oidcAuthenticator', () => { }, session: { accessToken: 'accessToken', - expiresInSeconds: 3600, idToken, refreshToken: 'refreshToken', scope: 'testScope', tokenType: 'bearer', }, }); + expect( + Math.abs(handlerResponse.session.expiresInSeconds! - 3600), + ).toBeLessThan(5); }); it('fails without authorization code', async () => { diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index a95010e353..97b0af8d7f 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -36,7 +36,6 @@ export const oidcAuthenticator = createOAuthAuthenticator({ const clientSecret = config.getString('clientSecret'); const metadataUrl = config.getString('metadataUrl'); const customCallbackUrl = config.getOptionalString('callbackUrl'); - const callbackUrl2 = customCallbackUrl || callbackUrl; const tokenEndpointAuthMethod = config.getOptionalString( 'tokenEndpointAuthMethod', ) as ClientAuthMethod; @@ -51,7 +50,7 @@ export const oidcAuthenticator = createOAuthAuthenticator({ access_type: 'offline', // this option must be passed to provider to receive a refresh token client_id: clientId, client_secret: clientSecret, - redirect_uris: [callbackUrl2], + redirect_uris: [customCallbackUrl || callbackUrl], response_types: ['code'], token_endpoint_auth_method: tokenEndpointAuthMethod || 'client_secret_basic', @@ -84,7 +83,6 @@ export const oidcAuthenticator = createOAuthAuthenticator({ ? { familyName: userinfo.family_name, givenName: userinfo.given_name, - middleName: userinfo.middle_name, } : undefined; diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index 09ad38b84c..d2487da450 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -245,39 +245,18 @@ describe('authModuleOidcProvider', () => { it('#authenticate exchanges authorization code for a access_token', async () => { const agent = request.agent(''); - - // make /start request with audience parameter const startResponse = await agent.get( `${appUrl}/api/auth/oidc/start?env=development`, ); - // follow redirect to authorization endpoint const authorizationResponse = await agent.get( startResponse.header.location, ); - // follow redirect to token_endpoint const handlerResponse = await agent.get( authorizationResponse.header.location, ); expect(handlerResponse.text).toContain( - encodeURIComponent( - JSON.stringify({ - type: 'authorization_response', - response: { - profile: { - email: 'alice@test.com', - picture: 'http://testPictureUrl/photo.jpg', - displayName: 'Alice Adams', - }, - providerInfo: { - idToken, - accessToken: 'accessToken', - scope: 'testScope', - expiresInSeconds: 3600, - }, - }, - }), - ), + encodeURIComponent(`"accessToken":"accessToken"`), ); }); }); diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index d80648a10f..d097ae2881 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -340,7 +340,7 @@ export type OAuthStartResponse = { // @public @deprecated (undocumented) export type OAuthState = OAuthState_2; -// @public +// @public @deprecated export type OidcAuthResult = { tokenset: TokenSet; userinfo: UserinfoResponse; @@ -564,10 +564,10 @@ export const providers: Readonly<{ create: ( options?: | { - authHandler?: AuthHandler | undefined; + authHandler?: AuthHandler | undefined; signIn?: | { - resolver: SignInResolver; + resolver: SignInResolver; } | undefined; } diff --git a/plugins/auth-backend/config.d.ts b/plugins/auth-backend/config.d.ts index 34139593d3..2b80324fcd 100644 --- a/plugins/auth-backend/config.d.ts +++ b/plugins/auth-backend/config.d.ts @@ -149,22 +149,6 @@ export interface Config { }; }; /** @visibility frontend */ - oidc?: { - [authEnv: string]: { - clientId: string; - /** - * @visibility secret - */ - clientSecret: string; - callbackUrl?: string; - metadataUrl: string; - tokenEndpointAuthMethod?: string; - tokenSignedResponseAlg?: string; - scope?: string; - prompt?: string; - }; - }; - /** @visibility frontend */ auth0?: { [authEnv: string]: { clientId: string; diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index c4343b1547..6b2282411c 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -15,3 +15,4 @@ */ export { oidc } from './provider'; +export type { OidcAuthResult } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index 18170e104e..f6804265cd 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -23,6 +23,21 @@ import { adaptLegacyOAuthSignInResolver, } from '../../lib/legacy'; import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; +import { TokenSet, UserinfoResponse } from 'openid-client'; +import { + commonByEmailLocalPartResolver, + commonByEmailResolver, +} from '../resolvers'; + +/** + * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) + * @public + * @deprecated No longer used + */ +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; /** * Auth provider integration for generic OpenID Connect auth @@ -50,4 +65,14 @@ export const oidc = createAuthProviderIntegration({ signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), }); }, + resolvers: { + /** + * Looks up the user by matching their email local part to the entity name. + */ + emailLocalPartMatchingUserEntityName: () => commonByEmailLocalPartResolver, + /** + * Looks up the user by matching their email to the entity email. + */ + emailMatchingUserEntityProfileEmail: () => commonByEmailResolver, + }, }); diff --git a/yarn.lock b/yarn.lock index 16979362ca..1e41fd83c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31270,24 +31270,10 @@ __metadata: languageName: node linkType: hard -"jose@npm:^4.14.4": - version: 4.14.6 - resolution: "jose@npm:4.14.6" - checksum: eae81a234e7bf1446b1bd80722b3462b014e3835b155c3a7799c1c5043163a53a0dc28d347004151b031e6b7b863403aabf8814d9cc217ce21f8c2f3ebd4b335 - languageName: node - linkType: hard - -"jose@npm:^4.14.6, jose@npm:^4.15.1": - version: 4.15.4 - resolution: "jose@npm:4.15.4" - checksum: dccad91cb3357f36423774a0b89ad830dd84b31090de65cd139b85488439f16a00f8c59c0773825e8a1adb0dd9d13ad725ad66e6ea33880ecb3959bb99e1ea5b - languageName: node - linkType: hard - -"jose@npm:^4.6.0": - version: 4.15.2 - resolution: "jose@npm:4.15.2" - checksum: 8f0cab1eef31243abe14a935b2b330cd95f10f9b69808fd642088ae5000e50e566664934537d2c6413ab2f6b54acd8265a5033da05157aa1260c5f1d7e57fab0 +"jose@npm:^4.14.6, jose@npm:^4.15.1, jose@npm:^4.6.0": + version: 4.15.3 + resolution: "jose@npm:4.15.3" + checksum: b76eeccc1d40d0eaf26dfaadc0f88fc15802c9105ab66a24ee223bd84369f7cb217f4a2cb852f5080ff6996170b3a73db2b2d26878b8905d99c36ca432628134 languageName: node linkType: hard @@ -35663,19 +35649,7 @@ __metadata: languageName: node linkType: hard -"openid-client@npm:^5.2.1, openid-client@npm:^5.3.0, openid-client@npm:^5.5.0": - version: 5.5.0 - resolution: "openid-client@npm:5.5.0" - dependencies: - jose: ^4.14.4 - lru-cache: ^6.0.0 - object-hash: ^2.2.0 - oidc-token-hash: ^5.0.3 - checksum: d2617b5bb0d9a0da338aeb7489bcbe3a79df9681189c7b61c2a3284289eee7110dfee2b04b49a9fdd4f064b7e2057ddb0becfedd9c19388e7788ae15b24c8e4c - languageName: node - linkType: hard - -"openid-client@npm:^5.4.3": +"openid-client@npm:^5.2.1, openid-client@npm:^5.3.0, openid-client@npm:^5.4.3, openid-client@npm:^5.5.0": version: 5.6.1 resolution: "openid-client@npm:5.6.1" dependencies: From 002010c8f60e9cbbea4c3297c86c9c668cd5e9f5 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 5 Jan 2024 18:56:32 -0500 Subject: [PATCH 069/152] authHandler/signInResolver backwards compatibility Signed-off-by: Jamie Klassen --- .../api-report.md | 24 ++- .../src/authenticator.test.ts | 52 +++--- .../src/authenticator.ts | 172 +++++++++--------- .../src/index.ts | 1 + plugins/auth-backend/api-report.md | 13 +- plugins/auth-backend/package.json | 1 - plugins/auth-backend/src/providers/index.ts | 1 - .../auth-backend/src/providers/oidc/index.ts | 1 - .../src/providers/oidc/provider.test.ts | 169 +++++++++++++++++ .../src/providers/oidc/provider.ts | 57 +++--- yarn.lock | 1 - 11 files changed, 337 insertions(+), 155 deletions(-) create mode 100644 plugins/auth-backend/src/providers/oidc/provider.test.ts diff --git a/plugins/auth-backend-module-oidc-provider/api-report.md b/plugins/auth-backend-module-oidc-provider/api-report.md index cf3335b13b..d96d9952a0 100644 --- a/plugins/auth-backend-module-oidc-provider/api-report.md +++ b/plugins/auth-backend-module-oidc-provider/api-report.md @@ -7,10 +7,10 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { BaseClient } from 'openid-client'; import { OAuthAuthenticator } from '@backstage/plugin-auth-node'; import { PassportOAuthAuthenticatorHelper } from '@backstage/plugin-auth-node'; -import { PassportOAuthResult } from '@backstage/plugin-auth-node'; -import { PassportProfile } from '@backstage/plugin-auth-node'; import { SignInResolverFactory } from '@backstage/plugin-auth-node'; import { Strategy } from 'openid-client'; +import { TokenSet } from 'openid-client'; +import { UserinfoResponse } from 'openid-client'; // @public (undocumented) const authModuleOidcProvider: () => BackendFeature; @@ -18,16 +18,24 @@ export default authModuleOidcProvider; // @public (undocumented) export const oidcAuthenticator: OAuthAuthenticator< - Promise<{ - helper: PassportOAuthAuthenticatorHelper; - client: BaseClient; + { initializedScope: string | undefined; initializedPrompt: string | undefined; - strategy: Strategy; - }>, - PassportProfile + promise: Promise<{ + helper: PassportOAuthAuthenticatorHelper; + client: BaseClient; + strategy: Strategy; + }>; + }, + OidcAuthResult >; +// @public +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + // @public export namespace oidcSignInResolvers { const emailLocalPartMatchingUserEntityName: SignInResolverFactory< diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts index b44f89e7c7..717083562c 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.test.ts @@ -281,81 +281,75 @@ describe('oidcAuthenticator', () => { }); it('exchanges authorization code for access token', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const accessToken = handlerResponse.session.accessToken; + const accessToken = authenticatorResult.session.accessToken; expect(accessToken).toEqual('accessToken'); }); it('exchanges authorization code for refresh token', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const refreshToken = handlerResponse.session.refreshToken; + const refreshToken = authenticatorResult.session.refreshToken; expect(refreshToken).toEqual('refreshToken'); }); it('returns granted scope', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const responseScope = handlerResponse.session.scope; + const responseScope = authenticatorResult.session.scope; expect(responseScope).toEqual('testScope'); }); it('returns a default session.tokentype field', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const tokenType = handlerResponse.session.tokenType; + const tokenType = authenticatorResult.session.tokenType; expect(tokenType).toEqual('bearer'); }); - it('returns defined fullProfile with picture and email', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + it('returns picture and email', async () => { + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - const displayName = handlerResponse.fullProfile.displayName; - const email = handlerResponse.fullProfile.emails![0].value; - const picture = handlerResponse.fullProfile.photos![0].value; - expect(displayName).toEqual('Alice Adams'); - expect(email).toEqual('alice@test.com'); - expect(picture).toEqual('http://testPictureUrl/photo.jpg'); + expect(authenticatorResult).toMatchObject({ + fullProfile: { + userinfo: { + email: 'alice@test.com', + picture: 'http://testPictureUrl/photo.jpg', + name: 'Alice Adams', + }, + }, + }); }); - it('returns defined response with an idToken', async () => { - const handlerResponse = await oidcAuthenticator.authenticate( + it('returns idToken', async () => { + const authenticatorResult = await oidcAuthenticator.authenticate( handlerRequest, implementation, ); - expect(handlerResponse).toMatchObject({ - fullProfile: { - displayName: 'Alice Adams', - id: 'test', - provider: 'oidc', - }, + expect(authenticatorResult).toMatchObject({ session: { - accessToken: 'accessToken', idToken, - refreshToken: 'refreshToken', - scope: 'testScope', - tokenType: 'bearer', }, }); expect( - Math.abs(handlerResponse.session.expiresInSeconds! - 3600), + Math.abs(authenticatorResult.session.expiresInSeconds! - 3600), ).toBeLessThan(5); }); diff --git a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts index 97b0af8d7f..ba6bc9d142 100644 --- a/plugins/auth-backend-module-oidc-provider/src/authenticator.ts +++ b/plugins/auth-backend-module-oidc-provider/src/authenticator.ts @@ -23,15 +23,35 @@ import { } from 'openid-client'; import { createOAuthAuthenticator, + OAuthAuthenticatorResult, + PassportDoneCallback, + PassportHelpers, PassportOAuthAuthenticatorHelper, - PassportOAuthDoneCallback, + PassportOAuthPrivateInfo, } from '@backstage/plugin-auth-node'; +/** + * authentication result for the OIDC which includes the token set and user + * profile response + * @public + */ +export type OidcAuthResult = { + tokenset: TokenSet; + userinfo: UserinfoResponse; +}; + /** @public */ export const oidcAuthenticator = createOAuthAuthenticator({ - defaultProfileTransform: - PassportOAuthAuthenticatorHelper.defaultProfileTransform, - async initialize({ callbackUrl, config }) { + defaultProfileTransform: async ( + input: OAuthAuthenticatorResult, + ) => ({ + profile: { + email: input.fullProfile.userinfo.email, + picture: input.fullProfile.userinfo.picture, + displayName: input.fullProfile.userinfo.name, + }, + }), + initialize({ callbackUrl, config }) { const clientId = config.getString('clientId'); const clientSecret = config.getString('clientSecret'); const metadataUrl = config.getString('metadataUrl'); @@ -45,81 +65,53 @@ export const oidcAuthenticator = createOAuthAuthenticator({ const initializedScope = config.getOptionalString('scope'); const initializedPrompt = config.getOptionalString('prompt'); - const issuer = await Issuer.discover(metadataUrl); - const client = new issuer.Client({ - access_type: 'offline', // this option must be passed to provider to receive a refresh token - client_id: clientId, - client_secret: clientSecret, - redirect_uris: [customCallbackUrl || callbackUrl], - response_types: ['code'], - token_endpoint_auth_method: - tokenEndpointAuthMethod || 'client_secret_basic', - id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256', - scope: initializedScope || '', + const promise = Issuer.discover(metadataUrl).then(issuer => { + const client = new issuer.Client({ + access_type: 'offline', // this option must be passed to provider to receive a refresh token + client_id: clientId, + client_secret: clientSecret, + redirect_uris: [customCallbackUrl || callbackUrl], + response_types: ['code'], + token_endpoint_auth_method: + tokenEndpointAuthMethod || 'client_secret_basic', + id_token_signed_response_alg: tokenSignedResponseAlg || 'RS256', + scope: initializedScope || '', + }); + + const strategy = new OidcStrategy( + { + client, + passReqToCallback: false, + }, + ( + tokenset: TokenSet, + userinfo: UserinfoResponse, + done: PassportDoneCallback, + ) => { + if (typeof done !== 'function') { + throw new Error( + 'OIDC IdP must provide a userinfo_endpoint in the metadata response', + ); + } + + done( + undefined, + { tokenset, userinfo }, + { refreshToken: tokenset.refresh_token }, + ); + }, + ); + + const helper = PassportOAuthAuthenticatorHelper.from(strategy); + return { helper, client, strategy }; }); - const strategy = new OidcStrategy( - { - client, - passReqToCallback: false, - }, - ( - tokenset: TokenSet, - userinfo: UserinfoResponse, - done: PassportOAuthDoneCallback, - ) => { - if (typeof done !== 'function') { - throw new Error( - 'OIDC IdP must provide a userinfo_endpoint in the metadata response', - ); - } - - const emails = userinfo.email ? [{ value: userinfo.email }] : undefined; - const photos = userinfo.picture - ? [{ value: userinfo.picture }] - : undefined; - const name = - userinfo.family_name && userinfo.given_name - ? { - familyName: userinfo.family_name, - givenName: userinfo.given_name, - } - : undefined; - - done( - undefined, - { - fullProfile: { - provider: 'oidc', - id: userinfo.sub, - displayName: userinfo.name!, - username: userinfo.preferred_username, - name, - emails, - photos, - }, - accessToken: tokenset.access_token!, - params: { - id_token: tokenset.id_token, - scope: tokenset.scope!, - expires_in: tokenset.expires_in!, - token_type: tokenset.token_type, - }, - }, - { - refreshToken: tokenset.refresh_token, - }, - ); - }, - ); - - const helper = PassportOAuthAuthenticatorHelper.from(strategy); - - return { helper, client, initializedScope, initializedPrompt, strategy }; + return { initializedScope, initializedPrompt, promise }; }, async start(input, ctx) { - const { initializedScope, initializedPrompt, helper, strategy } = await ctx; + const { initializedScope, initializedPrompt, promise } = ctx; + const { helper, strategy } = await promise; const options: Record = { scope: input.scope || initializedScope || 'openid profile email', state: input.state, @@ -140,12 +132,32 @@ export const oidcAuthenticator = createOAuthAuthenticator({ }); }, - async authenticate(input, ctx) { - return (await ctx).helper.authenticate(input); + async authenticate( + input, + ctx, + ): Promise> { + const { strategy } = await ctx.promise; + const { result, privateInfo } = + await PassportHelpers.executeFrameHandlerStrategy< + OidcAuthResult, + PassportOAuthPrivateInfo + >(input.req, strategy); + + return { + fullProfile: result, + session: { + accessToken: result.tokenset.access_token!, + tokenType: result.tokenset.token_type ?? 'bearer', + scope: result.tokenset.scope!, + expiresInSeconds: result.tokenset.expires_in, + idToken: result.tokenset.id_token, + refreshToken: privateInfo.refreshToken, + }, + }; }, async refresh(input, ctx) { - const { client } = await ctx; + const { client } = await ctx.promise; const tokenset = await client.refresh(input.refreshToken); if (!tokenset.access_token) { throw new Error('Refresh failed'); @@ -160,11 +172,7 @@ export const oidcAuthenticator = createOAuthAuthenticator({ reject(new Error('Refresh Failed')); } resolve({ - fullProfile: { - provider: 'oidc', - id: userinfo.sub, - displayName: userinfo.name!, - }, + fullProfile: { userinfo, tokenset }, session: { accessToken: tokenset.access_token!, tokenType: tokenset.token_type ?? 'bearer', diff --git a/plugins/auth-backend-module-oidc-provider/src/index.ts b/plugins/auth-backend-module-oidc-provider/src/index.ts index b14d350d4d..4e951382bb 100644 --- a/plugins/auth-backend-module-oidc-provider/src/index.ts +++ b/plugins/auth-backend-module-oidc-provider/src/index.ts @@ -21,5 +21,6 @@ */ export { oidcAuthenticator } from './authenticator'; +export type { OidcAuthResult } from './authenticator'; export { authModuleOidcProvider as default } from './module'; export { oidcSignInResolvers } from './resolvers'; diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index d097ae2881..16f257d17d 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -25,6 +25,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { OAuth2ProxyResult as OAuth2ProxyResult_2 } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; import { OAuthState as OAuthState_2 } from '@backstage/plugin-auth-node'; +import { OidcAuthResult } from '@backstage/plugin-auth-backend-module-oidc-provider'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { prepareBackstageIdentityResponse as prepareBackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; @@ -34,9 +35,7 @@ import { SignInInfo as SignInInfo_2 } from '@backstage/plugin-auth-node'; import { SignInResolver as SignInResolver_2 } from '@backstage/plugin-auth-node'; import { TokenManager } from '@backstage/backend-common'; import { TokenParams as TokenParams_2 } from '@backstage/plugin-auth-node'; -import { TokenSet } from 'openid-client'; import { UserEntity } from '@backstage/catalog-model'; -import { UserinfoResponse } from 'openid-client'; import { WebMessageResponse as WebMessageResponse_2 } from '@backstage/plugin-auth-node'; // @public @deprecated @@ -340,12 +339,6 @@ export type OAuthStartResponse = { // @public @deprecated (undocumented) export type OAuthState = OAuthState_2; -// @public @deprecated -export type OidcAuthResult = { - tokenset: TokenSet; - userinfo: UserinfoResponse; -}; - // @public @deprecated (undocumented) export const postMessageResponse: ( res: express.Response, @@ -564,10 +557,10 @@ export const providers: Readonly<{ create: ( options?: | { - authHandler?: AuthHandler | undefined; + authHandler?: AuthHandler | undefined; signIn?: | { - resolver: SignInResolver; + resolver: SignInResolver; } | undefined; } diff --git a/plugins/auth-backend/package.json b/plugins/auth-backend/package.json index ca92d4c9dd..918e359610 100644 --- a/plugins/auth-backend/package.json +++ b/plugins/auth-backend/package.json @@ -75,7 +75,6 @@ "passport-auth0": "^1.4.3", "passport-bitbucket-oauth2": "^0.1.2", "passport-github2": "^0.1.12", - "passport-gitlab2": "^5.0.0", "passport-google-oauth20": "^2.0.0", "passport-microsoft": "^1.0.0", "passport-oauth2": "^1.6.1", diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index 8173a7fbc3..c8140f864e 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -29,7 +29,6 @@ export type { } from './cloudflare-access'; export type { GithubOAuthResult } from './github'; export type { OAuth2ProxyResult } from './oauth2-proxy'; -export type { OidcAuthResult } from './oidc'; export type { SamlAuthResult } from './saml'; export type { GcpIapResult, GcpIapTokenInfo } from './gcp-iap'; diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index 6b2282411c..c4343b1547 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -15,4 +15,3 @@ */ export { oidc } from './provider'; -export type { OidcAuthResult } from './provider'; diff --git a/plugins/auth-backend/src/providers/oidc/provider.test.ts b/plugins/auth-backend/src/providers/oidc/provider.test.ts new file mode 100644 index 0000000000..f2172a796d --- /dev/null +++ b/plugins/auth-backend/src/providers/oidc/provider.test.ts @@ -0,0 +1,169 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { getVoidLogger } from '@backstage/backend-common'; +import { LoggerService } from '@backstage/backend-plugin-api'; +import { Config, ConfigReader } from '@backstage/config'; +import { + AuthProviderConfig, + AuthResolverContext, + CookieConfigurer, +} from '@backstage/plugin-auth-node'; +import express from 'express'; +import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; +import { rest } from 'msw'; +import { setupServer } from 'msw/node'; +import { oidc } from './provider'; + +describe('oidc.create', () => { + const userinfo = { + sub: 'test', + iss: 'https://oidc.test', + aud: 'clientId', + nonce: 'foo', + }; + const server = setupServer(); + setupRequestMockHandlers(server); + + let publicKey: JWK; + let tokenset: object; + let providerFactoryOptions: { + providerId: string; + globalConfig: AuthProviderConfig; + config: Config; + logger: LoggerService; + resolverContext: AuthResolverContext; + baseUrl: string; + appUrl: string; + isOriginAllowed: (origin: string) => boolean; + cookieConfigurer?: CookieConfigurer; + }; + + beforeAll(async () => { + const keyPair = await generateKeyPair('RS256'); + const privateKey = await exportJWK(keyPair.privateKey); + publicKey = await exportJWK(keyPair.publicKey); + publicKey.alg = privateKey.alg = 'RS256'; + + tokenset = { + id_token: await new SignJWT({ + iat: Date.now(), + exp: Date.now() + 10000, + ...userinfo, + }) + .setProtectedHeader({ alg: privateKey.alg, kid: privateKey.kid }) + .sign(keyPair.privateKey), + access_token: 'accessToken', + }; + }); + + beforeEach(() => { + server.use( + rest.get( + 'https://oidc.test/.well-known/openid-configuration', + (_req, res, ctx) => + res( + ctx.json({ + issuer: 'https://oidc.test', + token_endpoint: 'https://oidc.test/oauth2/token', + userinfo_endpoint: 'https://oidc.test/idp/userinfo.openid', + jwks_uri: 'https://oidc.test/jwks.json', + }), + ), + ), + rest.post('https://oidc.test/oauth2/token', (_req, res, ctx) => + res(ctx.json(tokenset)), + ), + rest.get('https://oidc.test/jwks.json', async (_req, res, ctx) => + res(ctx.json({ keys: [{ ...publicKey }] })), + ), + rest.get( + 'https://oidc.test/idp/userinfo.openid', + async (_req, res, ctx) => res(ctx.json(userinfo)), + ), + ); + providerFactoryOptions = { + providerId: 'myoidc', + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + globalConfig: { + baseUrl: 'http://backstage.test/api/auth', + appUrl: 'http://backstage.test', + isOriginAllowed: _ => true, + }, + config: new ConfigReader({ + development: { + metadataUrl: 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + }), + logger: getVoidLogger(), + resolverContext: { + issueToken: jest.fn(), + findCatalogUser: jest.fn(), + signInWithCatalogUser: jest.fn(), + }, + }; + }); + + it('invokes authHandler with tokenset and userinfo response', async () => { + const authHandler = jest.fn(); + const provider = oidc.create({ authHandler })(providerFactoryOptions); + const state = Buffer.from('nonce=foo&env=development').toString('hex'); + + await provider.frameHandler( + { + method: 'GET', + url: `http://backstage.test/api/auth/myoidc/handler/frame?code=blahblah&state=${state}`, + query: { state }, + cookies: { 'myoidc-nonce': 'foo' }, + session: { 'oidc:oidc.test': { state, nonce: 'foo' } }, + } as unknown as express.Request, + { setHeader: jest.fn(), end: jest.fn() } as unknown as express.Response, + ); + + expect(authHandler).toHaveBeenCalledWith( + { tokenset, userinfo }, + providerFactoryOptions.resolverContext, + ); + }); + + it('invokes sign-in resolver with tokenset and userinfo response', async () => { + const resolver = jest.fn(); + const provider = oidc.create({ signIn: { resolver } })( + providerFactoryOptions, + ); + const state = Buffer.from('nonce=foo&env=development').toString('hex'); + + await provider.frameHandler( + { + method: 'GET', + url: `http://backstage.test/api/auth/myoidc/handler/frame?code=blahblah&state=${state}`, + query: { state }, + cookies: { 'myoidc-nonce': 'foo' }, + session: { 'oidc:oidc.test': { state, nonce: 'foo' } }, + } as unknown as express.Request, + { setHeader: jest.fn(), end: jest.fn() } as unknown as express.Response, + ); + + expect(resolver).toHaveBeenCalledWith( + expect.objectContaining({ result: { tokenset, userinfo } }), + providerFactoryOptions.resolverContext, + ); + }); +}); diff --git a/plugins/auth-backend/src/providers/oidc/provider.ts b/plugins/auth-backend/src/providers/oidc/provider.ts index f6804265cd..9bc78c48d2 100644 --- a/plugins/auth-backend/src/providers/oidc/provider.ts +++ b/plugins/auth-backend/src/providers/oidc/provider.ts @@ -15,30 +15,23 @@ */ import { AuthHandler, SignInResolver } from '../types'; -import { OAuthResult } from '../../lib/oauth'; import { createAuthProviderIntegration } from '../createAuthProviderIntegration'; -import { createOAuthProviderFactory } from '@backstage/plugin-auth-node'; import { - adaptLegacyOAuthHandler, - adaptLegacyOAuthSignInResolver, -} from '../../lib/legacy'; -import { oidcAuthenticator } from '@backstage/plugin-auth-backend-module-oidc-provider'; -import { TokenSet, UserinfoResponse } from 'openid-client'; + createOAuthProviderFactory, + AuthResolverContext, + BackstageSignInResult, + OAuthAuthenticatorResult, + SignInInfo, +} from '@backstage/plugin-auth-node'; +import { + oidcAuthenticator, + OidcAuthResult, +} from '@backstage/plugin-auth-backend-module-oidc-provider'; import { commonByEmailLocalPartResolver, commonByEmailResolver, } from '../resolvers'; -/** - * authentication result for the OIDC which includes the token set and user information (a profile response sent by OIDC server) - * @public - * @deprecated No longer used - */ -export type OidcAuthResult = { - tokenset: TokenSet; - userinfo: UserinfoResponse; -}; - /** * Auth provider integration for generic OpenID Connect auth * @@ -50,19 +43,39 @@ export const oidc = createAuthProviderIntegration({ * The profile transformation function used to verify and convert the auth response * into the profile that will be presented to the user. */ - authHandler?: AuthHandler; + authHandler?: AuthHandler; /** - * Configure sign-in for this provider, without it the provider can not be used to sign users in. + * Configure sign-in for this provider; convert user profile respones into + * Backstage identities. */ signIn?: { - resolver: SignInResolver; + resolver: SignInResolver; }; }) { + const authHandler = options?.authHandler; + const signInResolver = options?.signIn?.resolver; return createOAuthProviderFactory({ authenticator: oidcAuthenticator, - profileTransform: adaptLegacyOAuthHandler(options?.authHandler), - signInResolver: adaptLegacyOAuthSignInResolver(options?.signIn?.resolver), + profileTransform: + authHandler && + (( + result: OAuthAuthenticatorResult, + context: AuthResolverContext, + ) => authHandler(result.fullProfile, context)), + signInResolver: + signInResolver && + (( + info: SignInInfo>, + context: AuthResolverContext, + ): Promise => + signInResolver( + { + result: info.result.fullProfile, + profile: info.profile, + }, + context, + )), }); }, resolvers: { diff --git a/yarn.lock b/yarn.lock index 1e41fd83c7..eb6ade926f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4819,7 +4819,6 @@ __metadata: passport-auth0: ^1.4.3 passport-bitbucket-oauth2: ^0.1.2 passport-github2: ^0.1.12 - passport-gitlab2: ^5.0.0 passport-google-oauth20: ^2.0.0 passport-microsoft: ^1.0.0 passport-oauth2: ^1.6.1 From 7e85338c1f31a846ad4b3f5e5d4607cb7ae78c0c Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Mon, 8 Jan 2024 14:52:19 -0500 Subject: [PATCH 070/152] return to using startTestBackend following the example of #21445 Signed-off-by: Jamie Klassen --- .../src/module.test.ts | 105 ++++++------------ 1 file changed, 33 insertions(+), 72 deletions(-) diff --git a/plugins/auth-backend-module-oidc-provider/src/module.test.ts b/plugins/auth-backend-module-oidc-provider/src/module.test.ts index d2487da450..216c101049 100644 --- a/plugins/auth-backend-module-oidc-provider/src/module.test.ts +++ b/plugins/auth-backend-module-oidc-provider/src/module.test.ts @@ -15,30 +15,21 @@ */ import request from 'supertest'; -import { - AuthProviderRouteHandlers, - createOAuthRouteHandlers, - decodeOAuthState, -} from '@backstage/plugin-auth-node'; +import { decodeOAuthState } from '@backstage/plugin-auth-node'; import { setupServer } from 'msw/node'; import { rest } from 'msw'; -import { setupRequestMockHandlers } from '@backstage/backend-test-utils'; +import { + mockServices, + setupRequestMockHandlers, + startTestBackend, +} from '@backstage/backend-test-utils'; import { Server } from 'http'; -import { AddressInfo } from 'net'; -import express from 'express'; import { JWK, SignJWT, exportJWK, generateKeyPair } from 'jose'; -import cookieParser from 'cookie-parser'; -import passport from 'passport'; -import session from 'express-session'; -import { oidcAuthenticator } from './authenticator'; -import { ConfigReader } from '@backstage/config'; -import Router from 'express-promise-router'; +import { authModuleOidcProvider } from './module'; describe('authModuleOidcProvider', () => { - let app: express.Express; let backstageServer: Server; let appUrl: string; - let providerRouteHandler: AuthProviderRouteHandlers; let idToken: string; let publicKey: JWK; @@ -142,65 +133,35 @@ describe('authModuleOidcProvider', () => { ), ); - const secret = 'secret'; - app = express() - .use(cookieParser(secret)) - .use( - session({ - secret, - saveUninitialized: false, - resave: false, - cookie: { secure: false }, - }), - ) - .use(passport.initialize()) - .use(passport.session()); - await new Promise(resolve => { - backstageServer = app.listen(0, '0.0.0.0', () => { - appUrl = `http://127.0.0.1:${ - (backstageServer.address() as AddressInfo).port - }`; - resolve(null); - }); - }); - - mswServer.use(rest.all(`${appUrl}/*`, req => req.passthrough())); - - providerRouteHandler = createOAuthRouteHandlers({ - authenticator: oidcAuthenticator, - appUrl, - baseUrl: `${appUrl}/api/auth`, - isOriginAllowed: _ => true, - providerId: 'oidc', - config: new ConfigReader({ - metadataUrl: 'https://oidc.test/.well-known/openid-configuration', - clientId: 'clientId', - clientSecret: 'clientSecret', - }), - resolverContext: { - issueToken: async _ => ({ token: '' }), - findCatalogUser: async _ => ({ - entity: { - apiVersion: '', - kind: '', - metadata: { name: '' }, + const backend = await startTestBackend({ + features: [ + authModuleOidcProvider, + import('@backstage/plugin-auth-backend'), + mockServices.rootConfig.factory({ + data: { + app: { baseUrl: 'http://localhost' }, + auth: { + session: { secret: 'test' }, + providers: { + oidc: { + development: { + metadataUrl: + 'https://oidc.test/.well-known/openid-configuration', + clientId: 'clientId', + clientSecret: 'clientSecret', + }, + }, + }, + }, }, }), - signInWithCatalogUser: async _ => ({ token: '' }), - }, + ], }); - const router = Router(); - router - .use( - '/api/auth/oidc/start', - providerRouteHandler.start.bind(providerRouteHandler), - ) - .use( - '/api/auth/oidc/handler/frame', - providerRouteHandler.frameHandler.bind(providerRouteHandler), - ); - app.use(router); + backstageServer = backend.server; + const port = backend.server.port(); + appUrl = `http://localhost:${port}`; + mswServer.use(rest.all(`http://*:${port}/*`, req => req.passthrough())); }); afterEach(() => { @@ -216,7 +177,7 @@ describe('authModuleOidcProvider', () => { expect(startResponse.status).toEqual(302); const nonceCookie = agent.jar.getCookie('oidc-nonce', { - domain: '127.0.0.1', + domain: 'localhost', path: '/api/auth/oidc/handler', script: false, secure: false, From c2108f75819c250e166ae13b0c704f28330254f6 Mon Sep 17 00:00:00 2001 From: Aizat Faiz Date: Thu, 18 Jan 2024 10:49:09 +0800 Subject: [PATCH 071/152] revert changes to AboutCard.tsx Signed-off-by: Aizat Faiz --- plugins/catalog/src/components/AboutCard/AboutCard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/catalog/src/components/AboutCard/AboutCard.tsx b/plugins/catalog/src/components/AboutCard/AboutCard.tsx index 2bbbc20760..763f6fc094 100644 --- a/plugins/catalog/src/components/AboutCard/AboutCard.tsx +++ b/plugins/catalog/src/components/AboutCard/AboutCard.tsx @@ -166,7 +166,7 @@ export function AboutCard(props: AboutCardProps) { const subHeaderLinks = [viewInSource, viewInTechDocs]; if (isTemplateEntityV1beta3(entity)) { - const Icon = app.getSystemIcon('kind:template') ?? CreateComponentIcon; + const Icon = app.getSystemIcon('scaffolder') ?? CreateComponentIcon; const launchTemplate: IconLinkVerticalProps = { label: 'Launch Template', From f899eecf0791b4942826cc646fb83497374c1484 Mon Sep 17 00:00:00 2001 From: David Weber Date: Sat, 16 Dec 2023 23:24:11 +0100 Subject: [PATCH 072/152] fix: change kind:resource icon Signed-off-by: David Weber --- .changeset/dry-lizards-taste.md | 6 ++++++ packages/app-defaults/src/defaults/icons.tsx | 4 ++-- plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx | 4 ++-- 3 files changed, 10 insertions(+), 4 deletions(-) create mode 100644 .changeset/dry-lizards-taste.md diff --git a/.changeset/dry-lizards-taste.md b/.changeset/dry-lizards-taste.md new file mode 100644 index 0000000000..90e4c9f659 --- /dev/null +++ b/.changeset/dry-lizards-taste.md @@ -0,0 +1,6 @@ +--- +'@backstage/app-defaults': patch +'@backstage/plugin-catalog': patch +--- + +Change default icon for `kind:resource` to the storage icon. diff --git a/packages/app-defaults/src/defaults/icons.tsx b/packages/app-defaults/src/defaults/icons.tsx index fbdfb14e5b..1ed5bf03c6 100644 --- a/packages/app-defaults/src/defaults/icons.tsx +++ b/packages/app-defaults/src/defaults/icons.tsx @@ -34,7 +34,7 @@ import MuiMenuBookIcon from '@material-ui/icons/MenuBook'; import MuiPeopleIcon from '@material-ui/icons/People'; import MuiPersonIcon from '@material-ui/icons/Person'; import MuiWarningIcon from '@material-ui/icons/Warning'; -import MuiWorkIcon from '@material-ui/icons/Work'; +import MuiStorageIcon from '@material-ui/icons/Storage'; import MuiFeaturedPlayListIcon from '@material-ui/icons/FeaturedPlayList'; export const icons = { @@ -58,7 +58,7 @@ export const icons = { 'kind:location': MuiLocationOnIcon as IconComponent, 'kind:system': MuiCategoryIcon as IconComponent, 'kind:user': MuiPersonIcon as IconComponent, - 'kind:resource': MuiWorkIcon as IconComponent, + 'kind:resource': MuiStorageIcon as IconComponent, 'kind:template': MuiFeaturedPlayListIcon as IconComponent, user: MuiPersonIcon as IconComponent, warning: MuiWarningIcon as IconComponent, diff --git a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx index 84f04c6f83..3dda6ef2de 100644 --- a/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx +++ b/plugins/catalog/src/apis/EntityPresentationApi/defaults.tsx @@ -26,7 +26,7 @@ import LocationOnIcon from '@material-ui/icons/LocationOn'; import MemoryIcon from '@material-ui/icons/Memory'; import PeopleIcon from '@material-ui/icons/People'; import PersonIcon from '@material-ui/icons/Person'; -import WorkIcon from '@material-ui/icons/Work'; +import StorageIcon from '@material-ui/icons/Storage'; import { DefaultEntityPresentationApiRenderer } from './DefaultEntityPresentationApi'; export const DEFAULT_CACHE_TTL: HumanDuration = { seconds: 10 }; @@ -39,7 +39,7 @@ export const DEFAULT_ICONS: Record = { api: ExtensionIcon, component: MemoryIcon, system: BusinessIcon, - resource: WorkIcon, + resource: StorageIcon, domain: ApartmentIcon, location: LocationOnIcon, user: PersonIcon, From b5abb970f6ebd5fbb4a80452782ade870eec0616 Mon Sep 17 00:00:00 2001 From: David Weber Date: Sun, 14 Jan 2024 14:09:33 +0100 Subject: [PATCH 073/152] chore: add end of life plugin Signed-off-by: David Weber --- microsite/data/plugins/end-of-life.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 microsite/data/plugins/end-of-life.yaml diff --git a/microsite/data/plugins/end-of-life.yaml b/microsite/data/plugins/end-of-life.yaml new file mode 100644 index 0000000000..d54a6bc5f9 --- /dev/null +++ b/microsite/data/plugins/end-of-life.yaml @@ -0,0 +1,10 @@ +--- +title: End of life +author: dweber019 +authorUrl: https://github.com/dweber019 +category: Quality +description: Display end of life data for entities from endoflife.data +documentation: https://github.com/dweber019/backstage-plugin-endoflife +iconUrl: https://raw.githubusercontent.com/dweber019/backstage-plugin-endoflife/main/plugins/endoflife/docs/pluginIcon.png +npmPackageName: '@dweber019/backstage-plugin-endoflife' +addedDate: '2024-01-18' From 88e3fb20480d876f38384cd18cb8acc0f5b94e26 Mon Sep 17 00:00:00 2001 From: Navya Govind Date: Thu, 18 Jan 2024 11:47:25 +0100 Subject: [PATCH 074/152] dynamic overflow Signed-off-by: Navya Govind --- .../src/components/EntityOwnerPicker/EntityOwnerPicker.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx index 8876266db2..5b5bd39310 100644 --- a/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx +++ b/plugins/catalog-react/src/components/EntityOwnerPicker/EntityOwnerPicker.tsx @@ -66,6 +66,9 @@ const FixedWidthFormControlLabel = withStyles( label: { width: '100%', }, + root: { + width: '90%', + }, }), { name: 'FixedWidthFormControlLabel' }, )(FormControlLabel); From 092a4bbf7783201f1747fea202dbdc1b8a1d1e4c Mon Sep 17 00:00:00 2001 From: AmbrishRamachandiran Date: Thu, 18 Jan 2024 18:36:56 +0530 Subject: [PATCH 075/152] Updated readme Signed-off-by: AmbrishRamachandiran --- contrib/terraform/techdocs-s3-storage/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/terraform/techdocs-s3-storage/readme.md b/contrib/terraform/techdocs-s3-storage/readme.md index 8031c05028..b4968f7c09 100644 --- a/contrib/terraform/techdocs-s3-storage/readme.md +++ b/contrib/terraform/techdocs-s3-storage/readme.md @@ -1 +1 @@ -This Terraform file should build an S3 bucket and configure IAM with a user and an inline policy that grants the user access to the bucket. After you've established the bucket, user, and policy, go to the user's AWS dashboard and generate an access key. This access key should be used to set the environment variables in step 3a [here](https://backstage.io/docs/features/techdocs/using-cloud-storage#configuring-aws-s3-bucket-with-techdocs). +This Terraform file should create an S3 bucket and set up IAM with a user and an inline policy that authorises access to the bucket. After you've set up the bucket, user, and policy, go to the user's AWS dashboard and create an access key. This access key should be used to configure the environment variables in step 3a [here](https://backstage.io/docs/features/techdocs/using-cloud-storage#configuring-aws-s3-bucket-with-techdocs). From c60fd9c71e70f0c4e2c90edc8f9837b43f84e9aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Jan 2024 16:46:31 +0100 Subject: [PATCH 076/152] fix the adopter form clipping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss index 77e1a87a53..24d07be2e7 100644 --- a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss +++ b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss @@ -4,6 +4,8 @@ display: flex; position: fixed; transform: translateY(-50%); + margin-top: 44px; + max-height: calc(100% - 44px); transition: all 250ms ease-in-out; @@ -22,9 +24,7 @@ } .hubSpotNewAdopterFormContent { - height: 600px; max-width: 500px; - overflow-y: hidden; border-radius: 8px 0 0 8px; background-color: var(--ifm-color-white); From aea7e811528e1338848dd5e15470585ccdf99412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Thu, 18 Jan 2024 16:52:59 +0100 Subject: [PATCH 077/152] address comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss index 24d07be2e7..9533890660 100644 --- a/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss +++ b/microsite/src/pages/home/hubSpotNewAdoptersForm.module.scss @@ -1,11 +1,13 @@ +$page-header-height: 44px; + .hubSpotNewAdopterFormContainer { top: 50%; right: 0; display: flex; position: fixed; transform: translateY(-50%); - margin-top: 44px; - max-height: calc(100% - 44px); + margin-top: $page-header-height; + max-height: calc(100% - $page-header-height); transition: all 250ms ease-in-out; From 48738d9bc358d9f71f14976c58967e72cb5bced3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 18:44:45 +0000 Subject: [PATCH 078/152] chore(deps): update ruby docker tag to v3.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- plugins/scaffolder-backend-module-rails/Rails.dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend-module-rails/Rails.dockerfile b/plugins/scaffolder-backend-module-rails/Rails.dockerfile index 921faa601d..8ea3fc370a 100644 --- a/plugins/scaffolder-backend-module-rails/Rails.dockerfile +++ b/plugins/scaffolder-backend-module-rails/Rails.dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.2 +FROM ruby:3.3 RUN apt-get update -qq && \ apt-get install -y nodejs postgresql-client git && \ From 2992a4c3103e1f669bf8f8fc7c63f8e1d8be87bd Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 19:14:24 +0000 Subject: [PATCH 079/152] fix(deps): update dependency @gitbeaker/rest to v39.30.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 53 ++++++++++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5b449d6d32..f6e3acd0b9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11017,14 +11017,14 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/core@npm:^39.25.0": - version: 39.25.0 - resolution: "@gitbeaker/core@npm:39.25.0" +"@gitbeaker/core@npm:^39.30.0": + version: 39.30.0 + resolution: "@gitbeaker/core@npm:39.30.0" dependencies: - "@gitbeaker/requester-utils": ^39.25.0 + "@gitbeaker/requester-utils": ^39.30.0 qs: ^6.11.2 xcase: ^2.0.1 - checksum: 56ebe3c6ba1078c47a6cae0abcd5ae4fd928dc73c9eb7dc462894d497d6fa5373c0d09a7a7a0583097cffa641eeb0b28b1764d33a085a7b95f0115399240bc7e + checksum: 4724659bab57cde13b63bba7ffad52651ebb726c444bb1d6c1c69c70efb89ed3d319abf12c117f9a8ab6741cc26e6a19c23e237bebd07d13b2f8c970171dc069 languageName: node linkType: hard @@ -11052,25 +11052,25 @@ __metadata: languageName: node linkType: hard -"@gitbeaker/requester-utils@npm:^39.25.0": - version: 39.25.0 - resolution: "@gitbeaker/requester-utils@npm:39.25.0" +"@gitbeaker/requester-utils@npm:^39.30.0": + version: 39.30.0 + resolution: "@gitbeaker/requester-utils@npm:39.30.0" dependencies: - async-sema: ^3.1.1 - micromatch: ^4.0.5 + picomatch-browser: ^2.2.6 qs: ^6.11.2 + rate-limiter-flexible: ^4.0.0 xcase: ^2.0.1 - checksum: 1ca90f5ac705d5ef9df682b3051106326bc660427b9b2a88762992690981ce0b6bed4b22bc08af0ba97043766f9898605ba58ed6c1a08b6711af34032f013ed2 + checksum: 05a60b027736edd165341c88ddbe095c78e77616f60033a9c9b419b657cd7d9998a9a801470a88d25d96be44b3a5233e3f1a4042aee075872f2882d10122db69 languageName: node linkType: hard "@gitbeaker/rest@npm:^39.25.0": - version: 39.25.0 - resolution: "@gitbeaker/rest@npm:39.25.0" + version: 39.30.0 + resolution: "@gitbeaker/rest@npm:39.30.0" dependencies: - "@gitbeaker/core": ^39.25.0 - "@gitbeaker/requester-utils": ^39.25.0 - checksum: 5732a34fe3f2fe0a048963baf90b237fde7a1be61fa327e2c7aa69266649394b5b48cd99a6c6abaf1cf8b32b84a293800af33eef49c011b252d6e061f7fe1af9 + "@gitbeaker/core": ^39.30.0 + "@gitbeaker/requester-utils": ^39.30.0 + checksum: 2b04dbb28c1a5f47c067c376ff0b4664278d72dcdef7696646a6d9331a400d9dfe75382e041a0a683fdc5ac1805abdb2919455fea786fd97f0eb65a5473d3fb5 languageName: node linkType: hard @@ -20699,13 +20699,6 @@ __metadata: languageName: node linkType: hard -"async-sema@npm:^3.1.1": - version: 3.1.1 - resolution: "async-sema@npm:3.1.1" - checksum: 07b8c51f6cab107417ecdd8126b7a9fe5a75151b7f69fdd420dcc8ee08f9e37c473a217247e894b56e999b088b32e902dbe41637e4e9b594d3f8dfcdddfadc5e - languageName: node - linkType: hard - "async@npm:^2.6.2, async@npm:^2.6.4": version: 2.6.4 resolution: "async@npm:2.6.4" @@ -36656,6 +36649,13 @@ __metadata: languageName: node linkType: hard +"picomatch-browser@npm:^2.2.6": + version: 2.2.6 + resolution: "picomatch-browser@npm:2.2.6" + checksum: 946c50a8654da43167a0f9a90dd0b50f3d4e830ee71c0926ee9052d7e6f40d8907deaafba19cea71b97207b4aaa52c245335e4cd5bbaf78fe2ba567cd0c341fa + languageName: node + linkType: hard + "picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.2.2, picomatch@npm:^2.2.3, picomatch@npm:^2.3.0, picomatch@npm:^2.3.1": version: 2.3.1 resolution: "picomatch@npm:2.3.1" @@ -37992,6 +37992,13 @@ __metadata: languageName: node linkType: hard +"rate-limiter-flexible@npm:^4.0.0": + version: 4.0.0 + resolution: "rate-limiter-flexible@npm:4.0.0" + checksum: defaa10e35d77399c4bbdeaa0e3f0028bcc5f6d678118d54496d5c01a4c9d5d56e16885e2ea339e7dd1ea056a3594bba24a18b5da236965f1d186fd52bdbe9fe + languageName: node + linkType: hard + "raw-body@npm:2.5.1": version: 2.5.1 resolution: "raw-body@npm:2.5.1" From bcaff38c9dcf4fa55511fe48757cbf438ee15f48 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Thu, 18 Jan 2024 22:13:32 +0100 Subject: [PATCH 080/152] .github/workflows: switch to 3 Jest workers Signed-off-by: Patrik Oldsberg --- .github/workflows/ci.yml | 2 +- .github/workflows/deploy_packages.yml | 2 +- .github/workflows/verify_windows.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c46380fef..fd6e2edbc3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -216,7 +216,7 @@ jobs: run: yarn backstage-cli repo lint --since origin/master - name: test changed packages - run: yarn backstage-cli repo test --maxWorkers=4 --workerIdleMemoryLimit=1300M --since origin/master + run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --since origin/master env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 BACKSTAGE_TEST_DATABASE_POSTGRES16_CONNECTION_STRING: postgresql://postgres:postgres@localhost:${{ job.services.postgres16.ports[5432] }} diff --git a/.github/workflows/deploy_packages.yml b/.github/workflows/deploy_packages.yml index cd7ca23a86..6cd6caecf9 100644 --- a/.github/workflows/deploy_packages.yml +++ b/.github/workflows/deploy_packages.yml @@ -98,7 +98,7 @@ jobs: - name: test (and upload coverage) run: | - yarn backstage-cli repo test --maxWorkers=4 --workerIdleMemoryLimit=1300M --coverage + yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M --coverage bash <(curl -s https://codecov.io/bash) # Upload code coverage for some specific flags. Also see .codecov.yml bash <(curl -s https://codecov.io/bash) -f packages/core-app-api/coverage/* -F core-app-api diff --git a/.github/workflows/verify_windows.yml b/.github/workflows/verify_windows.yml index 0acf1cc1ef..143b22c8b6 100644 --- a/.github/workflows/verify_windows.yml +++ b/.github/workflows/verify_windows.yml @@ -55,7 +55,7 @@ jobs: run: yarn lint:type-deps - name: test - run: yarn backstage-cli repo test --maxWorkers=4 --workerIdleMemoryLimit=1300M + run: yarn backstage-cli repo test --maxWorkers=3 --workerIdleMemoryLimit=1300M env: BACKSTAGE_TEST_DISABLE_DOCKER: 1 From 324b9702091806b4df60648afe8ed63ead90b529 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 16:21:39 -0500 Subject: [PATCH 081/152] feat(openapi-tooling): use info.title as the pluginId the current approach to pass pluginId to the client is opaque and using package name as a title isn't super descriptive. Signed-off-by: Aramis --- .../catalog-client/src/generated/apis/DefaultApi.client.ts | 2 +- packages/catalog-client/src/generated/apis/index.ts | 2 +- packages/catalog-client/src/generated/index.ts | 2 +- .../generated/models/AnalyzeLocationEntityField.model.ts | 2 +- .../models/AnalyzeLocationExistingEntity.model.ts | 2 +- .../models/AnalyzeLocationGenerateEntity.model.ts | 2 +- .../src/generated/models/AnalyzeLocationRequest.model.ts | 2 +- .../src/generated/models/AnalyzeLocationResponse.model.ts | 2 +- .../generated/models/CreateLocation201Response.model.ts | 2 +- .../src/generated/models/CreateLocationRequest.model.ts | 2 +- .../src/generated/models/EntitiesBatchResponse.model.ts | 2 +- .../src/generated/models/EntitiesQueryResponse.model.ts | 2 +- .../models/EntitiesQueryResponsePageInfo.model.ts | 2 +- .../catalog-client/src/generated/models/Entity.model.ts | 2 +- .../src/generated/models/EntityAncestryResponse.model.ts | 2 +- .../models/EntityAncestryResponseItemsInner.model.ts | 2 +- .../src/generated/models/EntityFacet.model.ts | 2 +- .../src/generated/models/EntityFacetsResponse.model.ts | 2 +- .../src/generated/models/EntityLink.model.ts | 2 +- .../src/generated/models/EntityMeta.model.ts | 2 +- .../src/generated/models/EntityRelation.model.ts | 2 +- .../src/generated/models/ErrorError.model.ts | 2 +- .../src/generated/models/ErrorRequest.model.ts | 2 +- .../src/generated/models/ErrorResponse.model.ts | 2 +- .../src/generated/models/GetEntitiesByRefsRequest.model.ts | 2 +- .../generated/models/GetLocations200ResponseInner.model.ts | 2 +- .../catalog-client/src/generated/models/Location.model.ts | 2 +- .../src/generated/models/LocationInput.model.ts | 2 +- .../src/generated/models/LocationSpec.model.ts | 2 +- .../src/generated/models/ModelError.model.ts | 2 +- .../src/generated/models/NullableEntity.model.ts | 2 +- .../src/generated/models/RecursivePartialEntity.model.ts | 2 +- .../generated/models/RecursivePartialEntityMeta.model.ts | 2 +- .../models/RecursivePartialEntityMetaAllOf.model.ts | 2 +- .../models/RecursivePartialEntityRelation.model.ts | 2 +- .../src/generated/models/RefreshEntityRequest.model.ts | 2 +- .../generated/models/ValidateEntity400Response.model.ts | 2 +- .../models/ValidateEntity400ResponseErrorsInner.model.ts | 2 +- .../src/generated/models/ValidateEntityRequest.model.ts | 2 +- packages/catalog-client/src/generated/models/index.ts | 2 +- packages/catalog-client/src/generated/pluginId.ts | 2 +- .../templates/typescript-backstage/pluginId.mustache | 6 +----- plugins/catalog-backend/src/schema/openapi.generated.ts | 7 ++----- plugins/catalog-backend/src/schema/openapi.yaml | 3 +-- plugins/search-backend/src/schema/openapi.yaml | 2 +- plugins/todo-backend/src/schema/openapi.yaml | 2 +- 46 files changed, 47 insertions(+), 55 deletions(-) diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 65b8699370..8905e3295a 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/apis/index.ts b/packages/catalog-client/src/generated/apis/index.ts index ad2f72c5b2..51dcca33fe 100644 --- a/packages/catalog-client/src/generated/apis/index.ts +++ b/packages/catalog-client/src/generated/apis/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/index.ts b/packages/catalog-client/src/generated/index.ts index 0cf3e9cc49..bb399e97a0 100644 --- a/packages/catalog-client/src/generated/index.ts +++ b/packages/catalog-client/src/generated/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts index a1c1e1f669..ff905977f8 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts index 5ede0fb60b..06a64b0b25 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts index 886aac05d9..aae5eeccf9 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts index e8e9542f5a..0e0d51d001 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts index bf60aa8018..398771b13c 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts index d090d80bf2..d5e6a4baa1 100644 --- a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts index 6ef950072e..267b6e0e47 100644 --- a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts index d09b2cc086..deebdd46d7 100644 --- a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts index 10d4747caf..1231a62ea9 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts index 8a7d5b39bb..06135e2187 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/Entity.model.ts b/packages/catalog-client/src/generated/models/Entity.model.ts index 59ef9bc741..c7bc507cd4 100644 --- a/packages/catalog-client/src/generated/models/Entity.model.ts +++ b/packages/catalog-client/src/generated/models/Entity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts index 08076fcf36..8db41427b5 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts index 7713c21295..2ca69a66e1 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityFacet.model.ts b/packages/catalog-client/src/generated/models/EntityFacet.model.ts index 3872091d77..2652d1ae8a 100644 --- a/packages/catalog-client/src/generated/models/EntityFacet.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacet.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts index 0ff37e4f2f..af7a80bd13 100644 --- a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityLink.model.ts b/packages/catalog-client/src/generated/models/EntityLink.model.ts index f52f9f2df0..7b0b6898fb 100644 --- a/packages/catalog-client/src/generated/models/EntityLink.model.ts +++ b/packages/catalog-client/src/generated/models/EntityLink.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityMeta.model.ts b/packages/catalog-client/src/generated/models/EntityMeta.model.ts index cb0fd28fdc..063edd2d60 100644 --- a/packages/catalog-client/src/generated/models/EntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/EntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityRelation.model.ts b/packages/catalog-client/src/generated/models/EntityRelation.model.ts index 1107f7563c..a5112bfe67 100644 --- a/packages/catalog-client/src/generated/models/EntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/EntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorError.model.ts b/packages/catalog-client/src/generated/models/ErrorError.model.ts index 9dbfa22813..372770fa86 100644 --- a/packages/catalog-client/src/generated/models/ErrorError.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts index e6306b87f5..bc2817f79f 100644 --- a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts index 1f55c34b4a..a266f9d3cc 100644 --- a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts index bdda7c2fa6..acdb340c7e 100644 --- a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts +++ b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts index 134306cce7..4fd3623229 100644 --- a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts +++ b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/Location.model.ts b/packages/catalog-client/src/generated/models/Location.model.ts index 27e47a5a25..f5b608a2eb 100644 --- a/packages/catalog-client/src/generated/models/Location.model.ts +++ b/packages/catalog-client/src/generated/models/Location.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/LocationInput.model.ts b/packages/catalog-client/src/generated/models/LocationInput.model.ts index 8de7e3e6c9..6bdbe090f2 100644 --- a/packages/catalog-client/src/generated/models/LocationInput.model.ts +++ b/packages/catalog-client/src/generated/models/LocationInput.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/LocationSpec.model.ts b/packages/catalog-client/src/generated/models/LocationSpec.model.ts index 80db2cadf2..21d4342cec 100644 --- a/packages/catalog-client/src/generated/models/LocationSpec.model.ts +++ b/packages/catalog-client/src/generated/models/LocationSpec.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ModelError.model.ts b/packages/catalog-client/src/generated/models/ModelError.model.ts index 989e755c9a..3e3b90ebdd 100644 --- a/packages/catalog-client/src/generated/models/ModelError.model.ts +++ b/packages/catalog-client/src/generated/models/ModelError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/NullableEntity.model.ts b/packages/catalog-client/src/generated/models/NullableEntity.model.ts index 36aff86ba8..802fc00faa 100644 --- a/packages/catalog-client/src/generated/models/NullableEntity.model.ts +++ b/packages/catalog-client/src/generated/models/NullableEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts index 8de7824550..3bfcc348b9 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts index b21cc1bb21..c56277e2b0 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts index 5111cd7f9f..f53190f323 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts index 50635b69f4..8fcbfd4138 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts index 573523385b..08358b6849 100644 --- a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts index 0031b2edc2..18e4a47c98 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts index 6223df27af..5753d9dd18 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts index d5ce1be5c9..31f68d7ad6 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/index.ts b/packages/catalog-client/src/generated/models/index.ts index 5a93dbb33a..d6c81becb5 100644 --- a/packages/catalog-client/src/generated/models/index.ts +++ b/packages/catalog-client/src/generated/models/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/pluginId.ts b/packages/catalog-client/src/generated/pluginId.ts index c8c8ce1ca3..b45379f0b0 100644 --- a/packages/catalog-client/src/generated/pluginId.ts +++ b/packages/catalog-client/src/generated/pluginId.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/repo-tools/templates/typescript-backstage/pluginId.mustache b/packages/repo-tools/templates/typescript-backstage/pluginId.mustache index 8bc8461cc9..a71829bb84 100644 --- a/packages/repo-tools/templates/typescript-backstage/pluginId.mustache +++ b/packages/repo-tools/templates/typescript-backstage/pluginId.mustache @@ -1,6 +1,2 @@ -{{#servers}} -{{#-last}} -export const pluginId = "{{url}}"; -{{/-last}} -{{/servers}} \ No newline at end of file +export const pluginId = "{{appName}}"; \ No newline at end of file diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index fe64c75373..20dfa2c978 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils'; export const spec = { openapi: '3.0.3', info: { - title: '@backstage/plugin-catalog-backend', + title: 'catalog', version: '1', description: 'The Backstage backend plugin that provides the Backstage catalog', @@ -36,9 +36,6 @@ export const spec = { { url: '/', }, - { - url: 'catalog', - }, ], components: { examples: {}, diff --git a/plugins/catalog-backend/src/schema/openapi.yaml b/plugins/catalog-backend/src/schema/openapi.yaml index d3ef718286..51283d5fbc 100644 --- a/plugins/catalog-backend/src/schema/openapi.yaml +++ b/plugins/catalog-backend/src/schema/openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: '@backstage/plugin-catalog-backend' + title: catalog version: '1' description: The Backstage backend plugin that provides the Backstage catalog license: @@ -9,7 +9,6 @@ info: contact: {} servers: - url: / - - url: catalog components: examples: {} headers: {} diff --git a/plugins/search-backend/src/schema/openapi.yaml b/plugins/search-backend/src/schema/openapi.yaml index 79bb84dfe0..47f7e0a1e5 100644 --- a/plugins/search-backend/src/schema/openapi.yaml +++ b/plugins/search-backend/src/schema/openapi.yaml @@ -1,6 +1,6 @@ openapi: 3.0.3 info: - title: '@backstage/plugin-search-backend' + title: search version: '1' description: The Backstage backend plugin that provides search functionality. license: diff --git a/plugins/todo-backend/src/schema/openapi.yaml b/plugins/todo-backend/src/schema/openapi.yaml index 8023144f48..7408927b99 100644 --- a/plugins/todo-backend/src/schema/openapi.yaml +++ b/plugins/todo-backend/src/schema/openapi.yaml @@ -1,7 +1,7 @@ openapi: 3.0.3 info: - title: '@backstage/plugin-todo-backend' + title: todo version: '1' description: The Backstage backend plugin that provides source code todo comment browsing. license: From b1329c569d63cda445b0d79fd751d48fb0807cf9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:27:43 +0000 Subject: [PATCH 082/152] fix(deps): update aws-sdk-js-v3 monorepo to v3.495.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 1304 ++++++++++++++++++++++++++--------------------------- 1 file changed, 652 insertions(+), 652 deletions(-) diff --git a/yarn.lock b/yarn.lock index 39042b78e8..8bf8b426dd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -363,515 +363,515 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/client-cognito-identity@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/client-cognito-identity@npm:3.490.0" +"@aws-sdk/client-cognito-identity@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/client-cognito-identity@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-signing": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-signing": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: 4b131c722d611242c8d5eb961115fb60f40bd228ecb2f6ce4851d30bae7b3b39b68cfd6ca363f901e8c16571539d37c7a617dae6681aa0bcfeec189a6b5afcea + checksum: 078c17e6f6fed15ee320a4d94e9925ff7ccb537416552492b3e66179998a8259c1d68c2f1523243c497c7bc64d613f33acc92b9f9c2c9238eee41be26aebcaf9 languageName: node linkType: hard "@aws-sdk/client-eks@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/client-eks@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/client-eks@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-signing": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 - "@smithy/util-waiter": ^2.0.16 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-signing": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 + "@smithy/util-waiter": ^2.1.0 tslib: ^2.5.0 uuid: ^8.3.2 - checksum: 367e3c987f644b0b4a6c5fba1ef1e6caa52594205ac9efe095960e00dcf4b0191f143080cfbdbb3807b91140cda14973f734e536104b355908d9c7ed3ba9fa64 + checksum: f4d9ce87c23c1d117e4046c97ac974131e54b878eb0aed9457739ca0d202f8d36c9b2ba3718120de531fdb200ccab123a4d1f848ef8c3858b1ca5d81dcd9c789 languageName: node linkType: hard "@aws-sdk/client-organizations@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/client-organizations@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/client-organizations@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-signing": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-signing": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: 073bc5ecc31e7c0cb0fd195e868f17946e7f60e12cdab3402fd1752374939915affb8f15fb55697f1a626736d7b05e35e65d466a5dddfd6e091eb0e16803f8a8 + checksum: 3c7deae27a0549ff97551d577df701f21823ca6f3fc965b35add491642faf3ddb060183752687264727e10cc80addb1a3c263af81f2a35d15fe0c3edb02a5da2 languageName: node linkType: hard "@aws-sdk/client-s3@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/client-s3@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/client-s3@npm:3.495.0" dependencies: "@aws-crypto/sha1-browser": 3.0.0 "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-bucket-endpoint": 3.489.0 - "@aws-sdk/middleware-expect-continue": 3.489.0 - "@aws-sdk/middleware-flexible-checksums": 3.489.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-location-constraint": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-sdk-s3": 3.489.0 - "@aws-sdk/middleware-signing": 3.489.0 - "@aws-sdk/middleware-ssec": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/signature-v4-multi-region": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@aws-sdk/xml-builder": 3.485.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/eventstream-serde-browser": ^2.0.16 - "@smithy/eventstream-serde-config-resolver": ^2.0.16 - "@smithy/eventstream-serde-node": ^2.0.16 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-blob-browser": ^2.0.17 - "@smithy/hash-node": ^2.0.18 - "@smithy/hash-stream-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/md5-js": ^2.0.18 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-stream": ^2.0.24 - "@smithy/util-utf8": ^2.0.2 - "@smithy/util-waiter": ^2.0.16 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-bucket-endpoint": 3.495.0 + "@aws-sdk/middleware-expect-continue": 3.495.0 + "@aws-sdk/middleware-flexible-checksums": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-location-constraint": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-sdk-s3": 3.495.0 + "@aws-sdk/middleware-signing": 3.495.0 + "@aws-sdk/middleware-ssec": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/signature-v4-multi-region": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@aws-sdk/xml-builder": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/eventstream-serde-browser": ^2.1.0 + "@smithy/eventstream-serde-config-resolver": ^2.1.0 + "@smithy/eventstream-serde-node": ^2.1.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-blob-browser": ^2.1.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/hash-stream-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/md5-js": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-stream": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 + "@smithy/util-waiter": ^2.1.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 28fc1c384bdba8c374ac8f150350feb5b6e8afaef76aaba2705d8d06d60dec9a209869888a3e91e75c88454084940916b1bc6070cc422d50712af7dd20a66ccf + checksum: 2add0f58189bfc394a6b3963ef4295b9997f3ea544d8a92ba2d16b9051f4fd07fb9f184e9ad6ca6ce665cbec496abd5e0e69a91a97213117dc20aafd3bed2840 languageName: node linkType: hard "@aws-sdk/client-sqs@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/client-sqs@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/client-sqs@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-sdk-sqs": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/md5-js": ^2.0.18 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-middleware": ^2.0.9 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-sdk-sqs": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/md5-js": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-middleware": ^2.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: 394f351ca8feff09b631eab6e285068379384a592832ec8ae502a15f60a475f2aa43e29d74b0dd5c2732610a9f7f122148ee442550975ecded53368b8d01a6b1 + checksum: 4dcbf031c1902149d8fa82f7c555eb416d904c1e197ad2deae927856748719c03b025bb122a131df34435269039a6b0538635826e0bcab204b86ef24fe4595b8 languageName: node linkType: hard -"@aws-sdk/client-sso@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/client-sso@npm:3.490.0" +"@aws-sdk/client-sso@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/client-sso@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: f09172f7af1de8371dc4bd03ef18f2a260bd9868db9460912d392d0f2bcf4101e8f78eed440db6bd99437a3b8d1c1a107fb3bc904f20f4a99eb0a262c0644b14 + checksum: a4672a8a2c454be634cb7e97a3cc075df55ad7ab8eb494bda3e4d16831f110212069668ab4fe6085f4b09920d1a04f3bd2cca0caf3a395d874cbcf5d71c6ffe7 languageName: node linkType: hard -"@aws-sdk/client-sts@npm:3.490.0, @aws-sdk/client-sts@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/client-sts@npm:3.490.0" +"@aws-sdk/client-sts@npm:3.495.0, @aws-sdk/client-sts@npm:^3.350.0": + version: 3.495.0 + resolution: "@aws-sdk/client-sts@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/core": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/core": ^1.2.2 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-middleware": ^2.0.9 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/core": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/core": ^1.3.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-middleware": ^2.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 fast-xml-parser: 4.2.5 tslib: ^2.5.0 - checksum: 0fffd52bfae00c2e1beacf2cc58924131097efb7022a72f2d8c00e93e9c00ceb584d085b92160a2798d761726c7ca8492da5a7e8665d103a658ad96f9633e04e + checksum: 33c5ef3fc87a2fb8cb5dc6bc855c5756712110d1c0be713543ffd657fcae4062e10ce9a1501bb9bc7a9f821a542f6e52c3bd9d88add89af67a116f3f5ab486fd languageName: node linkType: hard -"@aws-sdk/core@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/core@npm:3.490.0" +"@aws-sdk/core@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/core@npm:3.495.0" dependencies: - "@smithy/core": ^1.2.2 - "@smithy/protocol-http": ^3.0.12 - "@smithy/signature-v4": ^2.0.0 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 + "@smithy/core": ^1.3.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/signature-v4": ^2.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 8d711ba4373b4ee074f5a225642cb91cd0052daf1e2880da1f5bd1b38197ea5c7888a4181710f715393f83618aacc4465262f9a80de8f4faf2644d5832e455b5 + checksum: 2038b283ab62a0a10880a7193cef8b3673298ff9b7ba3005e3a47df43c588d3a3a9dfdbe607b99183932675a74ec17a88a90f9a5c5f5896292a9e6cbc888cf13 languageName: node linkType: hard -"@aws-sdk/credential-provider-cognito-identity@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.490.0" +"@aws-sdk/credential-provider-cognito-identity@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-cognito-identity@npm:3.495.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.490.0 - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/client-cognito-identity": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: c1ee2aa0c1d6c9bd61f17550c9381b05e790f23afc5dd757a242e2a9f8ee0c03c49f8b29452258b86c5f68b9e87fba3eb5b299ceb6414f1db6919de32aacba17 + checksum: 8bd8841d5d2e5316c271f6e5585216f906d60a42b221d5afdd52d49b6318faa6776cdc956704a22b675c5dd41fd164a2cf512f069a99738660eeea24c3316234 languageName: node linkType: hard -"@aws-sdk/credential-provider-env@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/credential-provider-env@npm:3.489.0" +"@aws-sdk/credential-provider-env@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: ef1c3fb9e12cb3b62be41d87ce168b704d462b6fb27d08eab58b003940e3b24b043f122efab42cdc41d0bd632074114f39b474c480705dc61ed6e879690fa93f + checksum: 5454a770638e2122877679897acb422871e0381c7e8a2a848c1837f16115bea7d3abaf56e2282b9986885de186538868f90c62292348251bbbd84c872c4a5eb0 languageName: node linkType: hard -"@aws-sdk/credential-provider-http@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/credential-provider-http@npm:3.489.0" +"@aws-sdk/credential-provider-http@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/property-provider": ^2.0.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/util-stream": ^2.0.24 + "@aws-sdk/types": 3.495.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/util-stream": ^2.1.0 tslib: ^2.5.0 - checksum: 601580c4299b1b0d7a696a94643b78c12491261f975275c5ae544ca5eee1f08fb4d21af1935257f2972b1f8ed8c0246275963e9c97cc53484f6d501ad6dbd4c6 + checksum: 367563058b01bc681523de85f4753bb53c53b0975c9258d6b4583222a28543ca2b2e3b5f75c1f1f6fb25a25baeb053d8d46c86b6997c4243bfc062a03093bae8 languageName: node linkType: hard -"@aws-sdk/credential-provider-ini@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/credential-provider-ini@npm:3.490.0" +"@aws-sdk/credential-provider-ini@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.495.0" dependencies: - "@aws-sdk/credential-provider-env": 3.489.0 - "@aws-sdk/credential-provider-process": 3.489.0 - "@aws-sdk/credential-provider-sso": 3.490.0 - "@aws-sdk/credential-provider-web-identity": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@smithy/credential-provider-imds": ^2.0.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.8.0 + "@aws-sdk/credential-provider-env": 3.495.0 + "@aws-sdk/credential-provider-process": 3.495.0 + "@aws-sdk/credential-provider-sso": 3.495.0 + "@aws-sdk/credential-provider-web-identity": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/credential-provider-imds": ^2.2.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/shared-ini-file-loader": ^2.3.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 5dd24af06b3a2977c1ebd47621619b18b8d9f9bed04e8ac5daae9faedabe66d6c6792a62da54513d668f0b11315cab4fd41a85637e35646cec46588742e36b1f + checksum: fcd401dcc8a71a601888a9abd5e915ae8bdf19501ed0f497478d8844a7e11769314488b2b3a1995c33a954ab499ab118911decdfaf8151e47c4768d6e090a429 languageName: node linkType: hard -"@aws-sdk/credential-provider-node@npm:3.490.0, @aws-sdk/credential-provider-node@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/credential-provider-node@npm:3.490.0" +"@aws-sdk/credential-provider-node@npm:3.495.0, @aws-sdk/credential-provider-node@npm:^3.350.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.495.0" dependencies: - "@aws-sdk/credential-provider-env": 3.489.0 - "@aws-sdk/credential-provider-ini": 3.490.0 - "@aws-sdk/credential-provider-process": 3.489.0 - "@aws-sdk/credential-provider-sso": 3.490.0 - "@aws-sdk/credential-provider-web-identity": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@smithy/credential-provider-imds": ^2.0.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.8.0 + "@aws-sdk/credential-provider-env": 3.495.0 + "@aws-sdk/credential-provider-ini": 3.495.0 + "@aws-sdk/credential-provider-process": 3.495.0 + "@aws-sdk/credential-provider-sso": 3.495.0 + "@aws-sdk/credential-provider-web-identity": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/credential-provider-imds": ^2.2.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/shared-ini-file-loader": ^2.3.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 52820573a6aff0ea8c7ca0699fb374ba9a94a9e9eb777c2b1225d3565fdde7a65c70fcbe9ec887cf555c2df73cdc341641791f1358a102e052869d0f4978d4d0 + checksum: c661de3423fb106d1f3347e24287d0fd9208e02a2fe4159300ec7c1df90196f09df25aab06428f64d3cfcae2a28482737d8cd9ad6d8adca0aa0cfa810276b0ac languageName: node linkType: hard -"@aws-sdk/credential-provider-process@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/credential-provider-process@npm:3.489.0" +"@aws-sdk/credential-provider-process@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/shared-ini-file-loader": ^2.3.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 19eb75f0222176f33ad5fbeb5a02b312a37a48966b73ff5c1af9974d439dc4c6faeffe745c2a23b0abb67ab876f9e10099b4a22c2ee9d15be5290556331a7a9a + checksum: e19812756fb578cf5e7845db1fc2df77fadb427efaefe7ce23effa20f5f7e1661cea05179ab425e3cbef08966b1821a4ccfbd00343f68f65b0564c0b06510b9a languageName: node linkType: hard -"@aws-sdk/credential-provider-sso@npm:3.490.0": - version: 3.490.0 - resolution: "@aws-sdk/credential-provider-sso@npm:3.490.0" +"@aws-sdk/credential-provider-sso@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.495.0" dependencies: - "@aws-sdk/client-sso": 3.490.0 - "@aws-sdk/token-providers": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/types": ^2.8.0 + "@aws-sdk/client-sso": 3.495.0 + "@aws-sdk/token-providers": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/shared-ini-file-loader": ^2.3.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: ea3c9d180f13f676b7717a8e0aabdca33b36f2dbf27128dfa6f8172040aa895437ef03e08802b9d6b104a783b8606b8af923193ff54f79e92118aa54babaa311 + checksum: 2f96aba1921612026e8dcd7d7f58988c63efdf270a8688adb0704f2e442778700d7160c890cf926a89eb79a2471ee9084329bd8463998bbb416b0f5ebfe8f7e3 languageName: node linkType: hard -"@aws-sdk/credential-provider-web-identity@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/credential-provider-web-identity@npm:3.489.0" +"@aws-sdk/credential-provider-web-identity@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 6da681688c9a61b6e100c71983a1ba19ef34bcbd8d1e4e85e3399d86e74cdc8d81f57b6da1983319596c87ef1600471e82c5243fb909f9752eff99a6ef26a8ea + checksum: d22fda65683d0558dd03e710c8ef4762c4c500d8527cf951b3597a716a56ffbd1d9bf22089e4714325f4043a5e9eb9f6b27e2e3f217eef06de3cc3364e5a8a0f languageName: node linkType: hard "@aws-sdk/credential-providers@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/credential-providers@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/credential-providers@npm:3.495.0" dependencies: - "@aws-sdk/client-cognito-identity": 3.490.0 - "@aws-sdk/client-sso": 3.490.0 - "@aws-sdk/client-sts": 3.490.0 - "@aws-sdk/credential-provider-cognito-identity": 3.490.0 - "@aws-sdk/credential-provider-env": 3.489.0 - "@aws-sdk/credential-provider-http": 3.489.0 - "@aws-sdk/credential-provider-ini": 3.490.0 - "@aws-sdk/credential-provider-node": 3.490.0 - "@aws-sdk/credential-provider-process": 3.489.0 - "@aws-sdk/credential-provider-sso": 3.490.0 - "@aws-sdk/credential-provider-web-identity": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@smithy/credential-provider-imds": ^2.0.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/client-cognito-identity": 3.495.0 + "@aws-sdk/client-sso": 3.495.0 + "@aws-sdk/client-sts": 3.495.0 + "@aws-sdk/credential-provider-cognito-identity": 3.495.0 + "@aws-sdk/credential-provider-env": 3.495.0 + "@aws-sdk/credential-provider-http": 3.495.0 + "@aws-sdk/credential-provider-ini": 3.495.0 + "@aws-sdk/credential-provider-node": 3.495.0 + "@aws-sdk/credential-provider-process": 3.495.0 + "@aws-sdk/credential-provider-sso": 3.495.0 + "@aws-sdk/credential-provider-web-identity": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/credential-provider-imds": ^2.2.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 470f752cdf07861b6d5d7aff18e0541400bb211fcda7037347593f67d3882be9f5bd66b334915dc6549b96c554e1334e1dfec3286210eb9e18628a8bc2f19052 + checksum: 1cb201bc9fe2f2f5edd277416962e32e6f99ecc98b5d386bfcac91d6db1719d66b57b1e690d660a6fe1ac198648d5df906397ac5e2d9d5a35d345bcc3230324a languageName: node linkType: hard @@ -897,34 +897,34 @@ __metadata: linkType: hard "@aws-sdk/lib-storage@npm:^3.350.0": - version: 3.490.0 - resolution: "@aws-sdk/lib-storage@npm:3.490.0" + version: 3.495.0 + resolution: "@aws-sdk/lib-storage@npm:3.495.0" dependencies: - "@smithy/abort-controller": ^2.0.1 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/smithy-client": ^2.2.1 + "@smithy/abort-controller": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/smithy-client": ^2.3.0 buffer: 5.6.0 events: 3.3.0 stream-browserify: 3.0.0 tslib: ^2.5.0 peerDependencies: "@aws-sdk/client-s3": ^3.0.0 - checksum: 253dc2e4fd33efcf5ee8d3ad76bb3f2ed1d4573e27fa8be23dc3b9b60bcf22e769684f3ab639c3551a66282e160e4b1693527ac49062c6a42b55716e7aadc6df + checksum: 6d7a1576f30ff2432f0907a8357604de5a6d338c706e34c8e85ef6fbb4e2a878c84ce41978c79844757454b0e90914c83b4c983a428a7ea0ba67b6ce7d15c9a8 languageName: node linkType: hard -"@aws-sdk/middleware-bucket-endpoint@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.489.0" +"@aws-sdk/middleware-bucket-endpoint@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-arn-parser": 3.465.0 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 - "@smithy/util-config-provider": ^2.1.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-arn-parser": 3.495.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 + "@smithy/util-config-provider": ^2.2.0 tslib: ^2.5.0 - checksum: 209278da47bf7e3b0e3cd6bc0f80d56bd6fc06400c12041474d1fdef7d227b8120bd7ac0b471fd15e03433e8c9e5e8c7a20d4f5f057dc23edfecaf4b61ad1378 + checksum: d75288d8d290b0749a9f17bf893dfb2d334a08707dbda2d24d187f53096c58f190e102a789310045b03792fe6d145a23af7dbac07994b27473f386328cc2bb5c languageName: node linkType: hard @@ -941,107 +941,107 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-expect-continue@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-expect-continue@npm:3.489.0" +"@aws-sdk/middleware-expect-continue@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 2e4bc714e5c410d2362ec274e2f36ffbb35dc826030ac64ec2d34bf9a89b1defa3c8cd1e0d92c4f41ddb64035039ddfac927069152ea70437061f2f078d43cc1 + checksum: d9e49bd36994cc5a0114d51fb050efecf5f9ae46bd5718b5acb98d486a864d33ced0d1a0afc96f22192a23f4a9acf8c784db3d97243b25452c55fd4e3491aec8 languageName: node linkType: hard -"@aws-sdk/middleware-flexible-checksums@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.489.0" +"@aws-sdk/middleware-flexible-checksums@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.495.0" dependencies: "@aws-crypto/crc32": 3.0.0 "@aws-crypto/crc32c": 3.0.0 - "@aws-sdk/types": 3.489.0 - "@smithy/is-array-buffer": ^2.0.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/types": 3.495.0 + "@smithy/is-array-buffer": ^2.1.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: 522f234ba9fdf5fe1476c72b7480158723a52e15db0560a4d79fa00dd43935633104648a4be2d0d741afa90f5b4f5ced3e99ffdece9b1aa75f4c08389dbe5f10 + checksum: 5ec9e6543222349ee4cc1560ebd0ec3fc076e62bb6a6f2fcfb55cb2b7b6d7f944dd3801869bc3a0367975bf7efbc587c1acb19fc9de07faf389aa966e8eeb15f languageName: node linkType: hard -"@aws-sdk/middleware-host-header@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-host-header@npm:3.489.0" +"@aws-sdk/middleware-host-header@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 15072d8409066de23ae815ce84c9ab9ce509e368654a3438b398e0f1bec5e215a52a59388ede1fe4c7a40cb639759c50aab3a3562ffe17352a24e6f92f2ddd1e + checksum: 4b4f64827f2f75df045229987625560c1ee35a30e48044f3b60292dbb0599760459097a79e805e2982e1a1150def39758aaf5637e8696b8d21101c03988499ff languageName: node linkType: hard -"@aws-sdk/middleware-location-constraint@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-location-constraint@npm:3.489.0" +"@aws-sdk/middleware-location-constraint@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: d9499c897ef1f89afafdc2b4ebaa32cb31396cd7412ddaab545427820c33ef90e6f3e9f16b82607cc7a0d402994034b0c51255c06e2bd15f30d618d10f8bcbc6 + checksum: 8315ea52cff810e8c19176e24528bee4a29af7f2631b1508e04a8936396dd020535e507300df9e5d8b98d4b8c1c5fd78b9e868f0500b7a66e0743394fd2313a5 languageName: node linkType: hard -"@aws-sdk/middleware-logger@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-logger@npm:3.489.0" +"@aws-sdk/middleware-logger@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-logger@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 10fa2538663512158f7a5889da693b0351f1f464457e45bc02199540adf6985ddfda81fe36dbdddabe1fe12709118fa106a365988e691fab834c9d93bf34329e + checksum: d36ee788bdcfc19bd73525c7b0dc476e63b26f4a798a43626fdc76776c0b46f905867bd86c8792aaa7736cbfb18b1fe36c22ef86af4cd8dacdc10e34feee0caf languageName: node linkType: hard -"@aws-sdk/middleware-recursion-detection@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-recursion-detection@npm:3.489.0" +"@aws-sdk/middleware-recursion-detection@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: bf1592f0577e8a5be5857100c288c0de0895b98b00d1947403d9ff99936d366cd3e8ae4838560ee68ff0d5fb74f26c2f139f23fee8b72ce8f753a742b467bb02 + checksum: eaa7921de46d6d2389b56eb36417a32bd1af41c4ed24f6e2956fa4fd1bb14141253368d96882402df4f6498eaee081c67c170ce74f042d74b0ec431cd1219df7 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-s3@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-sdk-s3@npm:3.489.0" +"@aws-sdk/middleware-sdk-s3@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-arn-parser": 3.465.0 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/protocol-http": ^3.0.12 - "@smithy/signature-v4": ^2.0.0 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/util-config-provider": ^2.1.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-arn-parser": 3.495.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/signature-v4": ^2.1.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/util-config-provider": ^2.2.0 tslib: ^2.5.0 - checksum: 4a1541d7e21172a270cf3965171abcfd1dcb1fc98eac982fb2d3f8dbc10cfecec5ea13bfb27ada66c1a40a6446d027399818edabf1180b340e496eb6826fa8d7 + checksum: b3c6e8fda75ca696a1ba8b2327c1cf3ec941d19af5927614949f4ba317934db2733c22fa011f614651a093a3374d7be1fa5c3f834517a48a6884060ff6cfd625 languageName: node linkType: hard -"@aws-sdk/middleware-sdk-sqs@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.489.0" +"@aws-sdk/middleware-sdk-sqs@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-sdk-sqs@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 - "@smithy/util-hex-encoding": ^2.0.0 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 + "@smithy/util-hex-encoding": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: a87d0f4c4dd0afd67b37cd5b4de4daa2505c7a51faa2c6aeef6eac0d6a74e68aa3457ec30bc6c174418899899de252ccda1ce53afe8380db2183c8620a4fa84f + checksum: 78c2b4a59fe5fe6797b5a31c486b7169aba57c4c070db1e691c1049d03a6952a406cd88d0d612bbb252a8d06855437022f505934f137e8244db93d33572545ea languageName: node linkType: hard @@ -1055,42 +1055,42 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/middleware-signing@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-signing@npm:3.489.0" +"@aws-sdk/middleware-signing@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-signing@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/property-provider": ^2.0.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/signature-v4": ^2.0.0 - "@smithy/types": ^2.8.0 - "@smithy/util-middleware": ^2.0.9 + "@aws-sdk/types": 3.495.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/signature-v4": ^2.1.0 + "@smithy/types": ^2.9.0 + "@smithy/util-middleware": ^2.1.0 tslib: ^2.5.0 - checksum: bf50942d8ca7baf2d27b0a6d8fb06219971da9863ee0fa7a2ab56c85d344b0374724f608cec371b75a2ea2a0cd4da6d7d431f8bf1f3d9fe5f1797a4d15272962 + checksum: ca717e5c2bc03157f4ee6f3c05d8c815ecf7bab83f5f86c8751a3106d4f5459630258d945d8840650bfb367b881ff3be1fc0c9037162f717380c2a76dec938db languageName: node linkType: hard -"@aws-sdk/middleware-ssec@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-ssec@npm:3.489.0" +"@aws-sdk/middleware-ssec@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: c3666f606bcdcbe2c36a21e0460fd9514ea6f7a3faf09fb392457ca7fc9f41da81351a50f4432ce748a24daad5715736eddf7e59694f0fbbcc530b395c2bf12c + checksum: 5f92ae6ac0b7a869203650c343eb0f42375b8784b20abed88f2075db4413935469c988c5daee6b520aea63a2208ca412a9a78c6fbb0b14fc7719c9dbc9ffdc65 languageName: node linkType: hard -"@aws-sdk/middleware-user-agent@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/middleware-user-agent@npm:3.489.0" +"@aws-sdk/middleware-user-agent@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: b5254863f2203b598199ad65ca87a5a3b92a3ecb51cabbb910819b0f03f61b26b553364e047b9b3329463f8dd4324a650d045deaf548cb5e7de2c08ed2f5dfd6 + checksum: a36dcd5252b001786a8d970143855e80242fa1d88252693a8c2a244114fcfae02eb4ae958a37efa92aeff5cc792506f9199961c346fd943949141a3e078c3123 languageName: node linkType: hard @@ -1138,31 +1138,31 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/region-config-resolver@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/region-config-resolver@npm:3.489.0" +"@aws-sdk/region-config-resolver@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/types": ^2.8.0 - "@smithy/util-config-provider": ^2.1.0 - "@smithy/util-middleware": ^2.0.9 + "@aws-sdk/types": 3.495.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/types": ^2.9.0 + "@smithy/util-config-provider": ^2.2.0 + "@smithy/util-middleware": ^2.1.0 tslib: ^2.5.0 - checksum: 2352d0b3409e6d5225fd3f6f5da81164fcb93bb528579eacefea75ef8760f8626434e377eb3c7785046ce996732209f300de70958905124acbd1eb45a192e24b + checksum: f0f723dbd46761246dd771d975cb60c60afcbb10a94138183b7b00cad633ce92fdd7f2ee9ae60fcfc369d4aadd653d56562f5a14652dc8974816fbaf2c924182 languageName: node linkType: hard -"@aws-sdk/signature-v4-multi-region@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/signature-v4-multi-region@npm:3.489.0" +"@aws-sdk/signature-v4-multi-region@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.495.0" dependencies: - "@aws-sdk/middleware-sdk-s3": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/signature-v4": ^2.0.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/middleware-sdk-s3": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/signature-v4": ^2.1.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 613ab4f64682844dd9a6c6675f7db218185d3415a40b255e1346a0217576bad431156324739d148542421c67036462476b95ac801ea8c7f24f5abac96514ee23 + checksum: 93559456f5d4cae2fde06f49c41275d411bc9c4625cb73b6fc88f165c960d7a9a5e1432ae95ad6243430e6c0d07b6710e94aaf0ca39c7514e7c9f0240a466507 languageName: node linkType: hard @@ -1182,48 +1182,48 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/token-providers@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/token-providers@npm:3.489.0" +"@aws-sdk/token-providers@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/token-providers@npm:3.495.0" dependencies: "@aws-crypto/sha256-browser": 3.0.0 "@aws-crypto/sha256-js": 3.0.0 - "@aws-sdk/middleware-host-header": 3.489.0 - "@aws-sdk/middleware-logger": 3.489.0 - "@aws-sdk/middleware-recursion-detection": 3.489.0 - "@aws-sdk/middleware-user-agent": 3.489.0 - "@aws-sdk/region-config-resolver": 3.489.0 - "@aws-sdk/types": 3.489.0 - "@aws-sdk/util-endpoints": 3.489.0 - "@aws-sdk/util-user-agent-browser": 3.489.0 - "@aws-sdk/util-user-agent-node": 3.489.0 - "@smithy/config-resolver": ^2.0.23 - "@smithy/fetch-http-handler": ^2.3.2 - "@smithy/hash-node": ^2.0.18 - "@smithy/invalid-dependency": ^2.0.16 - "@smithy/middleware-content-length": ^2.0.18 - "@smithy/middleware-endpoint": ^2.3.0 - "@smithy/middleware-retry": ^2.0.26 - "@smithy/middleware-serde": ^2.0.16 - "@smithy/middleware-stack": ^2.0.10 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/node-http-handler": ^2.2.2 - "@smithy/property-provider": ^2.0.0 - "@smithy/protocol-http": ^3.0.12 - "@smithy/shared-ini-file-loader": ^2.0.6 - "@smithy/smithy-client": ^2.2.1 - "@smithy/types": ^2.8.0 - "@smithy/url-parser": ^2.0.16 - "@smithy/util-base64": ^2.0.1 - "@smithy/util-body-length-browser": ^2.0.1 - "@smithy/util-body-length-node": ^2.1.0 - "@smithy/util-defaults-mode-browser": ^2.0.24 - "@smithy/util-defaults-mode-node": ^2.0.32 - "@smithy/util-endpoints": ^1.0.8 - "@smithy/util-retry": ^2.0.9 - "@smithy/util-utf8": ^2.0.2 + "@aws-sdk/middleware-host-header": 3.495.0 + "@aws-sdk/middleware-logger": 3.495.0 + "@aws-sdk/middleware-recursion-detection": 3.495.0 + "@aws-sdk/middleware-user-agent": 3.495.0 + "@aws-sdk/region-config-resolver": 3.495.0 + "@aws-sdk/types": 3.495.0 + "@aws-sdk/util-endpoints": 3.495.0 + "@aws-sdk/util-user-agent-browser": 3.495.0 + "@aws-sdk/util-user-agent-node": 3.495.0 + "@smithy/config-resolver": ^2.1.0 + "@smithy/fetch-http-handler": ^2.4.0 + "@smithy/hash-node": ^2.1.0 + "@smithy/invalid-dependency": ^2.1.0 + "@smithy/middleware-content-length": ^2.1.0 + "@smithy/middleware-endpoint": ^2.4.0 + "@smithy/middleware-retry": ^2.1.0 + "@smithy/middleware-serde": ^2.1.0 + "@smithy/middleware-stack": ^2.1.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/node-http-handler": ^2.3.0 + "@smithy/property-provider": ^2.1.0 + "@smithy/protocol-http": ^3.1.0 + "@smithy/shared-ini-file-loader": ^2.3.0 + "@smithy/smithy-client": ^2.3.0 + "@smithy/types": ^2.9.0 + "@smithy/url-parser": ^2.1.0 + "@smithy/util-base64": ^2.1.0 + "@smithy/util-body-length-browser": ^2.1.0 + "@smithy/util-body-length-node": ^2.2.0 + "@smithy/util-defaults-mode-browser": ^2.1.0 + "@smithy/util-defaults-mode-node": ^2.1.0 + "@smithy/util-endpoints": ^1.1.0 + "@smithy/util-retry": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: cf2e14a09ead031e9ac3426fd0e5bc71656f1ce9ba470c860af26d8935dce65b28365973388175f982dd2d8fb6c470520dee426045b26174663a5adc6ac5928c + checksum: 76b8dacaf9d0d9c608866d10bb8d0d42070e88872688f9dae3cd2cce2cd573bb6937ffd4e4ef3ca94763ac4f549faaf6f7d1e1bea714d7ec37b5a7ca533f5599 languageName: node linkType: hard @@ -1237,13 +1237,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/types@npm:3.489.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": - version: 3.489.0 - resolution: "@aws-sdk/types@npm:3.489.0" +"@aws-sdk/types@npm:3.495.0, @aws-sdk/types@npm:^3.222.0, @aws-sdk/types@npm:^3.347.0": + version: 3.495.0 + resolution: "@aws-sdk/types@npm:3.495.0" dependencies: - "@smithy/types": ^2.8.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: e4692f04daee278e4fc7faeadb8fee077aee5b1210a6c237c203b4688b714d1efc96c9f4574b71e264f71d8f4aad06b5728bcba13d12242c92f93dec81e3d3da + checksum: 786d7d860cd8c22a631d48e5a2a0570b941f43927d3bee1da36d4749f71fbdc2e7908f15f73582d0572c74f9f0e6b411a33f94facf27aec93d9f2920b9a662f4 languageName: node linkType: hard @@ -1258,12 +1258,12 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-arn-parser@npm:3.465.0, @aws-sdk/util-arn-parser@npm:^3.310.0": - version: 3.465.0 - resolution: "@aws-sdk/util-arn-parser@npm:3.465.0" +"@aws-sdk/util-arn-parser@npm:3.495.0, @aws-sdk/util-arn-parser@npm:^3.310.0": + version: 3.495.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.495.0" dependencies: tslib: ^2.5.0 - checksum: ce2dd638e9b8ef3260ce1c1ae299a4e44cbaa28a07cda9f1033b763cea8b1d901b7a963338e8a172af17074d06811f09605f3f903318c4bd2bbf102e92d02546 + checksum: 130f1b1c0d2b9917782049693aa4ab6aec98858d6e2b46c14a3a6c4979e65073f731aec96aa0e1c04e927d0b32ad2d4a6a701e2a6d5d416c6ea35e670e4eb987 languageName: node linkType: hard @@ -1277,15 +1277,15 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-endpoints@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/util-endpoints@npm:3.489.0" +"@aws-sdk/util-endpoints@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/util-endpoints@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 - "@smithy/util-endpoints": ^1.0.8 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 + "@smithy/util-endpoints": ^1.1.0 tslib: ^2.5.0 - checksum: 5c225b12ce5c18ecd64079d2e4133374244728ac7c8055efb07ca5b274266cb0e2d6c88ce5ae4385d45d67b2999fcd5bbe5e8dd4299792542683682ac05950e8 + checksum: a479feeea6b136c9a07f885373e859a37f19b4d55cf6f9ecd89017c13349f55d239aa5a083465b1d945ff6bf13809efd27133340bdecaef875e72bd60d534c76 languageName: node linkType: hard @@ -1337,32 +1337,32 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/util-user-agent-browser@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/util-user-agent-browser@npm:3.489.0" +"@aws-sdk/util-user-agent-browser@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/types": ^2.9.0 bowser: ^2.11.0 tslib: ^2.5.0 - checksum: b24009655bd5a7755575d6ed5c955d6fcfa3a70248e1a9c9d4a58cc7ed4bb25936a276917a0ab60b6e11e7ed915d5dcfdd5e9112e373bd23b24d24963324e516 + checksum: 47071d5d514e08ef2818e42b437798238df771bff320256eef4589a24dae3f84ac064897c941919e1b571aaf95345282a7e8e43729f787526b70e80dfba8dead languageName: node linkType: hard -"@aws-sdk/util-user-agent-node@npm:3.489.0": - version: 3.489.0 - resolution: "@aws-sdk/util-user-agent-node@npm:3.489.0" +"@aws-sdk/util-user-agent-node@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.495.0" dependencies: - "@aws-sdk/types": 3.489.0 - "@smithy/node-config-provider": ^2.1.9 - "@smithy/types": ^2.8.0 + "@aws-sdk/types": 3.495.0 + "@smithy/node-config-provider": ^2.2.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 peerDependencies: aws-crt: ">=1.0.0" peerDependenciesMeta: aws-crt: optional: true - checksum: 755845ce1cebdc78d3bb2bab058cf8e966658373daa7c62ae808fc0fc733248ed019042b1eed4ad3274ae08f23506cc000e1b0d41b6f01fd29cccad4275b3f8e + checksum: e4c188c91b78b33d8da77984f3513a61ece3454d02d16605ec985b8481d309d12ad7544d2233d1396aaffd0758b4d0d1c4c143b9edd68d66e2681a4fd32eac34 languageName: node linkType: hard @@ -1385,13 +1385,13 @@ __metadata: languageName: node linkType: hard -"@aws-sdk/xml-builder@npm:3.485.0": - version: 3.485.0 - resolution: "@aws-sdk/xml-builder@npm:3.485.0" +"@aws-sdk/xml-builder@npm:3.495.0": + version: 3.495.0 + resolution: "@aws-sdk/xml-builder@npm:3.495.0" dependencies: - "@smithy/types": ^2.8.0 + "@smithy/types": ^2.9.0 tslib: ^2.5.0 - checksum: 97eacc7fff1161876cb672051905156d6aec8116f76b35d5f2ca87b676b5b270fc6de0b9bed53792272a7712d835077ac64b4135fed7ea81c3e4cbf070f1be58 + checksum: f2c695268c887e1f0e39991867ceca861f3242a0c12689ccfe582bce4f3543cf241a7ea4873a4a1c8b3637a5592a29c8f9266040f375a3eaaa9aebce6d697ecd languageName: node linkType: hard @@ -15347,7 +15347,7 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^2.0.1, @smithy/abort-controller@npm:^2.1.0": +"@smithy/abort-controller@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/abort-controller@npm:2.1.0" dependencies: @@ -15376,7 +15376,7 @@ __metadata: languageName: node linkType: hard -"@smithy/config-resolver@npm:^2.0.23, @smithy/config-resolver@npm:^2.1.0": +"@smithy/config-resolver@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/config-resolver@npm:2.1.0" dependencies: @@ -15389,7 +15389,7 @@ __metadata: languageName: node linkType: hard -"@smithy/core@npm:^1.2.2": +"@smithy/core@npm:^1.3.0": version: 1.3.0 resolution: "@smithy/core@npm:1.3.0" dependencies: @@ -15405,7 +15405,7 @@ __metadata: languageName: node linkType: hard -"@smithy/credential-provider-imds@npm:^2.0.0, @smithy/credential-provider-imds@npm:^2.2.0": +"@smithy/credential-provider-imds@npm:^2.2.0": version: 2.2.0 resolution: "@smithy/credential-provider-imds@npm:2.2.0" dependencies: @@ -15418,7 +15418,7 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-codec@npm:^2.0.1, @smithy/eventstream-codec@npm:^2.1.0": +"@smithy/eventstream-codec@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/eventstream-codec@npm:2.1.0" dependencies: @@ -15430,7 +15430,7 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-browser@npm:^2.0.16": +"@smithy/eventstream-serde-browser@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/eventstream-serde-browser@npm:2.1.0" dependencies: @@ -15441,7 +15441,7 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-config-resolver@npm:^2.0.16": +"@smithy/eventstream-serde-config-resolver@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/eventstream-serde-config-resolver@npm:2.1.0" dependencies: @@ -15451,7 +15451,7 @@ __metadata: languageName: node linkType: hard -"@smithy/eventstream-serde-node@npm:^2.0.16": +"@smithy/eventstream-serde-node@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/eventstream-serde-node@npm:2.1.0" dependencies: @@ -15473,7 +15473,7 @@ __metadata: languageName: node linkType: hard -"@smithy/fetch-http-handler@npm:^2.3.2, @smithy/fetch-http-handler@npm:^2.4.0": +"@smithy/fetch-http-handler@npm:^2.4.0": version: 2.4.0 resolution: "@smithy/fetch-http-handler@npm:2.4.0" dependencies: @@ -15486,7 +15486,7 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-blob-browser@npm:^2.0.17": +"@smithy/hash-blob-browser@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/hash-blob-browser@npm:2.1.0" dependencies: @@ -15498,7 +15498,7 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-node@npm:^2.0.18": +"@smithy/hash-node@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/hash-node@npm:2.1.0" dependencies: @@ -15510,7 +15510,7 @@ __metadata: languageName: node linkType: hard -"@smithy/hash-stream-node@npm:^2.0.18": +"@smithy/hash-stream-node@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/hash-stream-node@npm:2.1.0" dependencies: @@ -15521,7 +15521,7 @@ __metadata: languageName: node linkType: hard -"@smithy/invalid-dependency@npm:^2.0.16": +"@smithy/invalid-dependency@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/invalid-dependency@npm:2.1.0" dependencies: @@ -15531,7 +15531,7 @@ __metadata: languageName: node linkType: hard -"@smithy/is-array-buffer@npm:^2.0.0, @smithy/is-array-buffer@npm:^2.1.0": +"@smithy/is-array-buffer@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/is-array-buffer@npm:2.1.0" dependencies: @@ -15540,7 +15540,7 @@ __metadata: languageName: node linkType: hard -"@smithy/md5-js@npm:^2.0.18": +"@smithy/md5-js@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/md5-js@npm:2.1.0" dependencies: @@ -15551,7 +15551,7 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-content-length@npm:^2.0.18": +"@smithy/middleware-content-length@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/middleware-content-length@npm:2.1.0" dependencies: @@ -15562,7 +15562,7 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-endpoint@npm:^2.3.0, @smithy/middleware-endpoint@npm:^2.4.0": +"@smithy/middleware-endpoint@npm:^2.4.0": version: 2.4.0 resolution: "@smithy/middleware-endpoint@npm:2.4.0" dependencies: @@ -15577,7 +15577,7 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-retry@npm:^2.0.26, @smithy/middleware-retry@npm:^2.1.0": +"@smithy/middleware-retry@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/middleware-retry@npm:2.1.0" dependencies: @@ -15594,7 +15594,7 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-serde@npm:^2.0.16, @smithy/middleware-serde@npm:^2.1.0": +"@smithy/middleware-serde@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/middleware-serde@npm:2.1.0" dependencies: @@ -15604,7 +15604,7 @@ __metadata: languageName: node linkType: hard -"@smithy/middleware-stack@npm:^2.0.10, @smithy/middleware-stack@npm:^2.1.0": +"@smithy/middleware-stack@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/middleware-stack@npm:2.1.0" dependencies: @@ -15614,7 +15614,7 @@ __metadata: languageName: node linkType: hard -"@smithy/node-config-provider@npm:^2.1.9, @smithy/node-config-provider@npm:^2.2.0": +"@smithy/node-config-provider@npm:^2.2.0": version: 2.2.0 resolution: "@smithy/node-config-provider@npm:2.2.0" dependencies: @@ -15626,7 +15626,7 @@ __metadata: languageName: node linkType: hard -"@smithy/node-http-handler@npm:^2.1.7, @smithy/node-http-handler@npm:^2.2.2, @smithy/node-http-handler@npm:^2.3.0": +"@smithy/node-http-handler@npm:^2.1.7, @smithy/node-http-handler@npm:^2.3.0": version: 2.3.0 resolution: "@smithy/node-http-handler@npm:2.3.0" dependencies: @@ -15639,7 +15639,7 @@ __metadata: languageName: node linkType: hard -"@smithy/property-provider@npm:^2.0.0, @smithy/property-provider@npm:^2.1.0": +"@smithy/property-provider@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/property-provider@npm:2.1.0" dependencies: @@ -15649,7 +15649,7 @@ __metadata: languageName: node linkType: hard -"@smithy/protocol-http@npm:^3.0.12, @smithy/protocol-http@npm:^3.1.0": +"@smithy/protocol-http@npm:^3.1.0": version: 3.1.0 resolution: "@smithy/protocol-http@npm:3.1.0" dependencies: @@ -15689,7 +15689,7 @@ __metadata: languageName: node linkType: hard -"@smithy/shared-ini-file-loader@npm:^2.0.6, @smithy/shared-ini-file-loader@npm:^2.3.0": +"@smithy/shared-ini-file-loader@npm:^2.3.0": version: 2.3.0 resolution: "@smithy/shared-ini-file-loader@npm:2.3.0" dependencies: @@ -15699,23 +15699,23 @@ __metadata: languageName: node linkType: hard -"@smithy/signature-v4@npm:^2.0.0": - version: 2.0.1 - resolution: "@smithy/signature-v4@npm:2.0.1" +"@smithy/signature-v4@npm:^2.1.0": + version: 2.1.0 + resolution: "@smithy/signature-v4@npm:2.1.0" dependencies: - "@smithy/eventstream-codec": ^2.0.1 - "@smithy/is-array-buffer": ^2.0.0 - "@smithy/types": ^2.0.2 - "@smithy/util-hex-encoding": ^2.0.0 - "@smithy/util-middleware": ^2.0.0 - "@smithy/util-uri-escape": ^2.0.0 - "@smithy/util-utf8": ^2.0.0 + "@smithy/eventstream-codec": ^2.1.0 + "@smithy/is-array-buffer": ^2.1.0 + "@smithy/types": ^2.9.0 + "@smithy/util-hex-encoding": ^2.1.0 + "@smithy/util-middleware": ^2.1.0 + "@smithy/util-uri-escape": ^2.1.0 + "@smithy/util-utf8": ^2.1.0 tslib: ^2.5.0 - checksum: 2c4bc499251dfbdbea0359fc5196002ac805df7f6f87d7cba36164d8fcd16a749a3c1a52259569134628511dc496724d5e76744c1c39fee3a3f42c5fbfe3749b + checksum: 3e2fa005930f8228b9480087f4eeacbf36200e268c760890182e21ae4035b5e02e9d10bc6d9f9ae0bc257b02cadadb522afdf1f3bddf3c2d61c3e3796f90e8ca languageName: node linkType: hard -"@smithy/smithy-client@npm:^2.2.1, @smithy/smithy-client@npm:^2.3.0": +"@smithy/smithy-client@npm:^2.3.0": version: 2.3.0 resolution: "@smithy/smithy-client@npm:2.3.0" dependencies: @@ -15738,7 +15738,7 @@ __metadata: languageName: node linkType: hard -"@smithy/types@npm:^2.0.2, @smithy/types@npm:^2.8.0, @smithy/types@npm:^2.9.0": +"@smithy/types@npm:^2.9.0": version: 2.9.0 resolution: "@smithy/types@npm:2.9.0" dependencies: @@ -15747,7 +15747,7 @@ __metadata: languageName: node linkType: hard -"@smithy/url-parser@npm:^2.0.16, @smithy/url-parser@npm:^2.1.0": +"@smithy/url-parser@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/url-parser@npm:2.1.0" dependencies: @@ -15758,7 +15758,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-base64@npm:^2.0.1, @smithy/util-base64@npm:^2.1.0": +"@smithy/util-base64@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-base64@npm:2.1.0" dependencies: @@ -15768,7 +15768,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-body-length-browser@npm:^2.0.1": +"@smithy/util-body-length-browser@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-body-length-browser@npm:2.1.0" dependencies: @@ -15777,12 +15777,12 @@ __metadata: languageName: node linkType: hard -"@smithy/util-body-length-node@npm:^2.1.0": - version: 2.1.0 - resolution: "@smithy/util-body-length-node@npm:2.1.0" +"@smithy/util-body-length-node@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/util-body-length-node@npm:2.2.0" dependencies: tslib: ^2.5.0 - checksum: e4635251898f12e1825f2848e0b7cc9d01ec6635b3f1f71b790734bb702b88e795f6c539d42d95472dad00e50e9ff13fcf396791092b131e5834069cb8f52ed0 + checksum: a2ada7daeb31147675b66f0a95cd24ed10ee800211ddde34b1bbbd0a2bfbfdc82efbc8fe2c9bf4e18a67e7112aaf432f381a32e19be06735c8f4e0c3eec1a4a5 languageName: node linkType: hard @@ -15796,7 +15796,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-config-provider@npm:^2.1.0, @smithy/util-config-provider@npm:^2.2.0": +"@smithy/util-config-provider@npm:^2.2.0": version: 2.2.0 resolution: "@smithy/util-config-provider@npm:2.2.0" dependencies: @@ -15805,7 +15805,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-defaults-mode-browser@npm:^2.0.24": +"@smithy/util-defaults-mode-browser@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-defaults-mode-browser@npm:2.1.0" dependencies: @@ -15818,7 +15818,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-defaults-mode-node@npm:^2.0.32": +"@smithy/util-defaults-mode-node@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-defaults-mode-node@npm:2.1.0" dependencies: @@ -15833,7 +15833,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-endpoints@npm:^1.0.8": +"@smithy/util-endpoints@npm:^1.1.0": version: 1.1.0 resolution: "@smithy/util-endpoints@npm:1.1.0" dependencies: @@ -15844,7 +15844,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-hex-encoding@npm:^2.0.0, @smithy/util-hex-encoding@npm:^2.1.0": +"@smithy/util-hex-encoding@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-hex-encoding@npm:2.1.0" dependencies: @@ -15853,7 +15853,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-middleware@npm:^2.0.0, @smithy/util-middleware@npm:^2.0.9, @smithy/util-middleware@npm:^2.1.0": +"@smithy/util-middleware@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-middleware@npm:2.1.0" dependencies: @@ -15863,7 +15863,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-retry@npm:^2.0.9, @smithy/util-retry@npm:^2.1.0": +"@smithy/util-retry@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-retry@npm:2.1.0" dependencies: @@ -15874,7 +15874,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-stream@npm:^2.0.24, @smithy/util-stream@npm:^2.1.0": +"@smithy/util-stream@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-stream@npm:2.1.0" dependencies: @@ -15890,7 +15890,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-uri-escape@npm:^2.0.0, @smithy/util-uri-escape@npm:^2.1.0": +"@smithy/util-uri-escape@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-uri-escape@npm:2.1.0" dependencies: @@ -15899,7 +15899,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-utf8@npm:^2.0.0, @smithy/util-utf8@npm:^2.0.2, @smithy/util-utf8@npm:^2.1.0": +"@smithy/util-utf8@npm:^2.0.0, @smithy/util-utf8@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-utf8@npm:2.1.0" dependencies: @@ -15909,7 +15909,7 @@ __metadata: languageName: node linkType: hard -"@smithy/util-waiter@npm:^2.0.16": +"@smithy/util-waiter@npm:^2.1.0": version: 2.1.0 resolution: "@smithy/util-waiter@npm:2.1.0" dependencies: From 04907c39c20f8f5ebc2dd306d57c40973580310c Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 16:27:49 -0500 Subject: [PATCH 083/152] update documentation and add changeset Signed-off-by: Aramis --- .changeset/afraid-numbers-invite.md | 15 +++++++++++++++ .changeset/nine-bulldogs-camp.md | 8 ++++++++ docs/openapi/generate-client.md | 11 ++++------- 3 files changed, 27 insertions(+), 7 deletions(-) create mode 100644 .changeset/afraid-numbers-invite.md create mode 100644 .changeset/nine-bulldogs-camp.md diff --git a/.changeset/afraid-numbers-invite.md b/.changeset/afraid-numbers-invite.md new file mode 100644 index 0000000000..ee81341d3a --- /dev/null +++ b/.changeset/afraid-numbers-invite.md @@ -0,0 +1,15 @@ +--- +'@backstage/repo-tools': minor +--- + +Updates the OpenAPI client template to support the new format for identifying plugin ID. You should now use `info.title` like so, + +```diff +info: ++ title: yourPluginId +- title: @internal/plugin-*-backend + +servers: + - / +- - yourPluginId +``` diff --git a/.changeset/nine-bulldogs-camp.md b/.changeset/nine-bulldogs-camp.md new file mode 100644 index 0000000000..f397bb9ab7 --- /dev/null +++ b/.changeset/nine-bulldogs-camp.md @@ -0,0 +1,8 @@ +--- +'@backstage/catalog-client': minor +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-search-backend': minor +'@backstage/plugin-todo-backend': minor +--- + +Updates the OpenAPI specification title to plugin ID instead of package name. diff --git a/docs/openapi/generate-client.md b/docs/openapi/generate-client.md index 4afa7d64d7..f2c7c64574 100644 --- a/docs/openapi/generate-client.md +++ b/docs/openapi/generate-client.md @@ -8,15 +8,12 @@ description: Documentation on how to create a client for a given OpenAPI spec ### Prerequisites -1. Add your plugin ID as the last `servers` item, like this, +1. Set your OpenAPI file's `info.title` to your pluginID like so, ```yaml -servers: - # first value, used for OpenAPI router validation. - - url: / - - # final value, pluginId. - - url: catalog +info: + # your pluginId + title: catalog ``` 2. Find or create a new plugin to house your new generated client. Currently, we do not support generating an entirely new plugin and instead just generate client files. From 0788a2dda8273578a1d6c416ea22dcd391e08fe6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 21:29:24 +0000 Subject: [PATCH 084/152] fix(deps): update dependency @rollup/plugin-json to v6.1.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/yarn.lock b/yarn.lock index 39042b78e8..11bbe31272 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15048,16 +15048,16 @@ __metadata: linkType: hard "@rollup/plugin-json@npm:^6.0.0": - version: 6.0.1 - resolution: "@rollup/plugin-json@npm:6.0.1" + version: 6.1.0 + resolution: "@rollup/plugin-json@npm:6.1.0" dependencies: - "@rollup/pluginutils": ^5.0.1 + "@rollup/pluginutils": ^5.1.0 peerDependencies: rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - checksum: 86995e3ceec0205bd0b5ae3075f4592e0ab3e6e6327a5dcfc825b44113eaae5819d26d5403d29b177ac59299e0b08c641c8d030e0c72805b92805ededc9cac44 + checksum: cc018d20c80242a2b8b44fae61a968049cf31bb8406218187cc7cda35747616594e79452dd65722e7da6dd825b392e90d4599d43cd4461a02fefa2865945164e languageName: node linkType: hard @@ -15106,19 +15106,19 @@ __metadata: languageName: node linkType: hard -"@rollup/pluginutils@npm:^5.0.1": - version: 5.0.4 - resolution: "@rollup/pluginutils@npm:5.0.4" +"@rollup/pluginutils@npm:^5.0.1, @rollup/pluginutils@npm:^5.1.0": + version: 5.1.0 + resolution: "@rollup/pluginutils@npm:5.1.0" dependencies: "@types/estree": ^1.0.0 estree-walker: ^2.0.2 picomatch: ^2.3.1 peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 peerDependenciesMeta: rollup: optional: true - checksum: 893d5805ac4121fc704926963a0ae4e79e9e2bc8d736c3b28499ab69a404cce5119ca3a4e0c3d3a81d62f1beb3966f35285c36935d94b061794f26e94fed4cd1 + checksum: 3cc5a6d91452a6eabbfd1ae79b4dd1f1e809d2eecda6e175deb784e75b0911f47e9ecce73f8dd315d6a8b3f362582c91d3c0f66908b6ced69345b3cbe28f8ce8 languageName: node linkType: hard From e6c2f88abcd6407fb15fb319e314efae03362a5c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:17:10 +0000 Subject: [PATCH 085/152] fix(deps): update dependency @stoplight/types to v14.1.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9c19a225b5..8a2b906b13 100644 --- a/yarn.lock +++ b/yarn.lock @@ -16201,12 +16201,12 @@ __metadata: linkType: hard "@stoplight/types@npm:^14.0.0": - version: 14.0.0 - resolution: "@stoplight/types@npm:14.0.0" + version: 14.1.1 + resolution: "@stoplight/types@npm:14.1.1" dependencies: "@types/json-schema": ^7.0.4 utility-types: ^3.10.0 - checksum: 462fb6c718a2679927421ca1fffb5e31be2d150130e95a2771ffba04f68fc0e0f8ef3d4ae050e63fd4044bc4103e2ea82c98e1f5c7ff192a78914838eb250e5e + checksum: 1da2e683e88afe2f72c3b3af341537bc9bac153d224f65744ca60d44eade93609ce91172064ae27093e1ebfa7bcbf05fb232a1910d83b2aee5b1eed4bb726200 languageName: node linkType: hard From 8e2ea17b1cb20ce91c34d733ec6f34c40a495635 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 18 Jan 2024 22:18:43 +0000 Subject: [PATCH 086/152] fix(deps): update dependency azure-devops-node-api to v12.3.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9c19a225b5..dcdd0ce70b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20926,12 +20926,12 @@ __metadata: linkType: hard "azure-devops-node-api@npm:^12.0.0": - version: 12.1.0 - resolution: "azure-devops-node-api@npm:12.1.0" + version: 12.3.0 + resolution: "azure-devops-node-api@npm:12.3.0" dependencies: tunnel: 0.0.6 typed-rest-client: ^1.8.4 - checksum: aa3c3f3eff89485a44ea31843749c5abf5ddfb0d2f059a3f5623d75d5eddbab5b839a5458c158e7941b9c8a2adfde2ecfea128ed076f730c309c869e8a318a78 + checksum: 4f76f4633f57b92267b9f841591ee2c9548bc51421d3d5b0072c854f6a3f3bc77b3a334863a22f1b903c620492a8be877ee16ff5af05e6d131b281a683a7f695 languageName: node linkType: hard From ebd45bcc5d4ac8c70a01f60f7729d6d6e564d302 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:21:40 -0500 Subject: [PATCH 087/152] revert prettier license year changes Signed-off-by: Aramis --- packages/catalog-client/src/generated/apis/DefaultApi.client.ts | 2 +- packages/catalog-client/src/generated/apis/index.ts | 2 +- packages/catalog-client/src/generated/index.ts | 2 +- .../src/generated/models/AnalyzeLocationEntityField.model.ts | 2 +- .../src/generated/models/AnalyzeLocationExistingEntity.model.ts | 2 +- .../src/generated/models/AnalyzeLocationGenerateEntity.model.ts | 2 +- .../src/generated/models/AnalyzeLocationRequest.model.ts | 2 +- .../src/generated/models/AnalyzeLocationResponse.model.ts | 2 +- .../src/generated/models/CreateLocation201Response.model.ts | 2 +- .../src/generated/models/CreateLocationRequest.model.ts | 2 +- .../src/generated/models/EntitiesBatchResponse.model.ts | 2 +- .../src/generated/models/EntitiesQueryResponse.model.ts | 2 +- .../src/generated/models/EntitiesQueryResponsePageInfo.model.ts | 2 +- packages/catalog-client/src/generated/models/Entity.model.ts | 2 +- .../src/generated/models/EntityAncestryResponse.model.ts | 2 +- .../generated/models/EntityAncestryResponseItemsInner.model.ts | 2 +- .../catalog-client/src/generated/models/EntityFacet.model.ts | 2 +- .../src/generated/models/EntityFacetsResponse.model.ts | 2 +- .../catalog-client/src/generated/models/EntityLink.model.ts | 2 +- .../catalog-client/src/generated/models/EntityMeta.model.ts | 2 +- .../catalog-client/src/generated/models/EntityRelation.model.ts | 2 +- .../catalog-client/src/generated/models/ErrorError.model.ts | 2 +- .../catalog-client/src/generated/models/ErrorRequest.model.ts | 2 +- .../catalog-client/src/generated/models/ErrorResponse.model.ts | 2 +- .../src/generated/models/GetEntitiesByRefsRequest.model.ts | 2 +- .../src/generated/models/GetLocations200ResponseInner.model.ts | 2 +- packages/catalog-client/src/generated/models/Location.model.ts | 2 +- .../catalog-client/src/generated/models/LocationInput.model.ts | 2 +- .../catalog-client/src/generated/models/LocationSpec.model.ts | 2 +- .../catalog-client/src/generated/models/ModelError.model.ts | 2 +- .../catalog-client/src/generated/models/NullableEntity.model.ts | 2 +- .../src/generated/models/RecursivePartialEntity.model.ts | 2 +- .../src/generated/models/RecursivePartialEntityMeta.model.ts | 2 +- .../generated/models/RecursivePartialEntityMetaAllOf.model.ts | 2 +- .../generated/models/RecursivePartialEntityRelation.model.ts | 2 +- .../src/generated/models/RefreshEntityRequest.model.ts | 2 +- .../src/generated/models/ValidateEntity400Response.model.ts | 2 +- .../models/ValidateEntity400ResponseErrorsInner.model.ts | 2 +- .../src/generated/models/ValidateEntityRequest.model.ts | 2 +- packages/catalog-client/src/generated/models/index.ts | 2 +- packages/catalog-client/src/generated/pluginId.ts | 2 +- 41 files changed, 41 insertions(+), 41 deletions(-) diff --git a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts index 8905e3295a..65b8699370 100644 --- a/packages/catalog-client/src/generated/apis/DefaultApi.client.ts +++ b/packages/catalog-client/src/generated/apis/DefaultApi.client.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/apis/index.ts b/packages/catalog-client/src/generated/apis/index.ts index 51dcca33fe..ad2f72c5b2 100644 --- a/packages/catalog-client/src/generated/apis/index.ts +++ b/packages/catalog-client/src/generated/apis/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/index.ts b/packages/catalog-client/src/generated/index.ts index bb399e97a0..0cf3e9cc49 100644 --- a/packages/catalog-client/src/generated/index.ts +++ b/packages/catalog-client/src/generated/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts index ff905977f8..a1c1e1f669 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationEntityField.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts index 06a64b0b25..5ede0fb60b 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationExistingEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts index aae5eeccf9..886aac05d9 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationGenerateEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts index 0e0d51d001..e8e9542f5a 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts index 398771b13c..bf60aa8018 100644 --- a/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts +++ b/packages/catalog-client/src/generated/models/AnalyzeLocationResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts index d5e6a4baa1..d090d80bf2 100644 --- a/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocation201Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts index 267b6e0e47..6ef950072e 100644 --- a/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts +++ b/packages/catalog-client/src/generated/models/CreateLocationRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts index deebdd46d7..d09b2cc086 100644 --- a/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesBatchResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts index 1231a62ea9..10d4747caf 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts index 06135e2187..8a7d5b39bb 100644 --- a/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts +++ b/packages/catalog-client/src/generated/models/EntitiesQueryResponsePageInfo.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/Entity.model.ts b/packages/catalog-client/src/generated/models/Entity.model.ts index c7bc507cd4..59ef9bc741 100644 --- a/packages/catalog-client/src/generated/models/Entity.model.ts +++ b/packages/catalog-client/src/generated/models/Entity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts index 8db41427b5..08076fcf36 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts index 2ca69a66e1..7713c21295 100644 --- a/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts +++ b/packages/catalog-client/src/generated/models/EntityAncestryResponseItemsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityFacet.model.ts b/packages/catalog-client/src/generated/models/EntityFacet.model.ts index 2652d1ae8a..3872091d77 100644 --- a/packages/catalog-client/src/generated/models/EntityFacet.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacet.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts index af7a80bd13..0ff37e4f2f 100644 --- a/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts +++ b/packages/catalog-client/src/generated/models/EntityFacetsResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityLink.model.ts b/packages/catalog-client/src/generated/models/EntityLink.model.ts index 7b0b6898fb..f52f9f2df0 100644 --- a/packages/catalog-client/src/generated/models/EntityLink.model.ts +++ b/packages/catalog-client/src/generated/models/EntityLink.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityMeta.model.ts b/packages/catalog-client/src/generated/models/EntityMeta.model.ts index 063edd2d60..cb0fd28fdc 100644 --- a/packages/catalog-client/src/generated/models/EntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/EntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/EntityRelation.model.ts b/packages/catalog-client/src/generated/models/EntityRelation.model.ts index a5112bfe67..1107f7563c 100644 --- a/packages/catalog-client/src/generated/models/EntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/EntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorError.model.ts b/packages/catalog-client/src/generated/models/ErrorError.model.ts index 372770fa86..9dbfa22813 100644 --- a/packages/catalog-client/src/generated/models/ErrorError.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts index bc2817f79f..e6306b87f5 100644 --- a/packages/catalog-client/src/generated/models/ErrorRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts index a266f9d3cc..1f55c34b4a 100644 --- a/packages/catalog-client/src/generated/models/ErrorResponse.model.ts +++ b/packages/catalog-client/src/generated/models/ErrorResponse.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts index acdb340c7e..bdda7c2fa6 100644 --- a/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts +++ b/packages/catalog-client/src/generated/models/GetEntitiesByRefsRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts index 4fd3623229..134306cce7 100644 --- a/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts +++ b/packages/catalog-client/src/generated/models/GetLocations200ResponseInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/Location.model.ts b/packages/catalog-client/src/generated/models/Location.model.ts index f5b608a2eb..27e47a5a25 100644 --- a/packages/catalog-client/src/generated/models/Location.model.ts +++ b/packages/catalog-client/src/generated/models/Location.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/LocationInput.model.ts b/packages/catalog-client/src/generated/models/LocationInput.model.ts index 6bdbe090f2..8de7e3e6c9 100644 --- a/packages/catalog-client/src/generated/models/LocationInput.model.ts +++ b/packages/catalog-client/src/generated/models/LocationInput.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/LocationSpec.model.ts b/packages/catalog-client/src/generated/models/LocationSpec.model.ts index 21d4342cec..80db2cadf2 100644 --- a/packages/catalog-client/src/generated/models/LocationSpec.model.ts +++ b/packages/catalog-client/src/generated/models/LocationSpec.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ModelError.model.ts b/packages/catalog-client/src/generated/models/ModelError.model.ts index 3e3b90ebdd..989e755c9a 100644 --- a/packages/catalog-client/src/generated/models/ModelError.model.ts +++ b/packages/catalog-client/src/generated/models/ModelError.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/NullableEntity.model.ts b/packages/catalog-client/src/generated/models/NullableEntity.model.ts index 802fc00faa..36aff86ba8 100644 --- a/packages/catalog-client/src/generated/models/NullableEntity.model.ts +++ b/packages/catalog-client/src/generated/models/NullableEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts index 3bfcc348b9..8de7824550 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntity.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts index c56277e2b0..b21cc1bb21 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMeta.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts index f53190f323..5111cd7f9f 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityMetaAllOf.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts index 8fcbfd4138..50635b69f4 100644 --- a/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts +++ b/packages/catalog-client/src/generated/models/RecursivePartialEntityRelation.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts index 08358b6849..573523385b 100644 --- a/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/RefreshEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts index 18e4a47c98..0031b2edc2 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400Response.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts index 5753d9dd18..6223df27af 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntity400ResponseErrorsInner.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts index 31f68d7ad6..d5ce1be5c9 100644 --- a/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts +++ b/packages/catalog-client/src/generated/models/ValidateEntityRequest.model.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/models/index.ts b/packages/catalog-client/src/generated/models/index.ts index d6c81becb5..5a93dbb33a 100644 --- a/packages/catalog-client/src/generated/models/index.ts +++ b/packages/catalog-client/src/generated/models/index.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/packages/catalog-client/src/generated/pluginId.ts b/packages/catalog-client/src/generated/pluginId.ts index b45379f0b0..c8c8ce1ca3 100644 --- a/packages/catalog-client/src/generated/pluginId.ts +++ b/packages/catalog-client/src/generated/pluginId.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 552e762784265d74f9564d0c25c5f287e98afeb1 Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:22:00 -0500 Subject: [PATCH 088/152] fix generated files Signed-off-by: Aramis --- plugins/search-backend/src/schema/openapi.generated.ts | 4 ++-- plugins/todo-backend/src/schema/openapi.generated.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/search-backend/src/schema/openapi.generated.ts b/plugins/search-backend/src/schema/openapi.generated.ts index 2586b7a5bf..cc38a6b293 100644 --- a/plugins/search-backend/src/schema/openapi.generated.ts +++ b/plugins/search-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils'; export const spec = { openapi: '3.0.3', info: { - title: '@backstage/plugin-search-backend', + title: 'search', version: '1', description: 'The Backstage backend plugin that provides search functionality.', diff --git a/plugins/todo-backend/src/schema/openapi.generated.ts b/plugins/todo-backend/src/schema/openapi.generated.ts index effe851790..2308ec0ab5 100644 --- a/plugins/todo-backend/src/schema/openapi.generated.ts +++ b/plugins/todo-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2023 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,7 @@ import { createValidatedOpenApiRouter } from '@backstage/backend-openapi-utils'; export const spec = { openapi: '3.0.3', info: { - title: '@backstage/plugin-todo-backend', + title: 'todo', version: '1', description: 'The Backstage backend plugin that provides source code todo comment browsing.', From 126c2f9e7aa5297166f8b6ee0d60c99cf11df1eb Mon Sep 17 00:00:00 2001 From: Aramis Date: Thu, 18 Jan 2024 18:26:26 -0500 Subject: [PATCH 089/152] add changeset Signed-off-by: Aramis --- .changeset/long-suns-bow.md | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .changeset/long-suns-bow.md diff --git a/.changeset/long-suns-bow.md b/.changeset/long-suns-bow.md new file mode 100644 index 0000000000..837482500f --- /dev/null +++ b/.changeset/long-suns-bow.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-catalog-backend': minor +'@backstage/plugin-search-backend': minor +'@backstage/plugin-todo-backend': minor +--- + +Updates the OpenAPI spec to use plugin as `info.title` instead of package name. From bcd277a311a06fd45bcd87a3cd730fc8998fd866 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 03:43:47 +0000 Subject: [PATCH 090/152] chore(deps): update dependency @openapitools/openapi-generator-cli to v2.8.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 52 ++++++++++------------------------------------------ 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/yarn.lock b/yarn.lock index e224a5129a..d42c31b56b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13966,8 +13966,8 @@ __metadata: linkType: hard "@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": - version: 2.8.0 - resolution: "@openapitools/openapi-generator-cli@npm:2.8.0" + version: 2.8.1 + resolution: "@openapitools/openapi-generator-cli@npm:2.8.1" dependencies: "@nestjs/axios": 3.0.1 "@nestjs/common": 10.3.0 @@ -13981,14 +13981,14 @@ __metadata: console.table: 0.10.0 fs-extra: 10.1.0 glob: 7.2.3 - inquirer: 8.2.5 + inquirer: 8.2.6 lodash: 4.17.21 reflect-metadata: 0.1.13 - rxjs: 7.8.0 + rxjs: 7.8.1 tslib: 2.6.2 bin: openapi-generator-cli: main.js - checksum: 76a43d3087b2a4eb521814369ce99ade622fc146bb2414f52c01a7565f81fa94a6d42f2bf6889a4b25814744f0462613853a609d97c12a608c5717aed2e3898a + checksum: 5e0e5460ec8cf5f96338703820066265d187ce984f24cfcf6f1abe70049b38a8953f0835658f5915ec41304a98bd76cf17ec9fb50fa746701116be6502dca39e languageName: node linkType: hard @@ -29536,30 +29536,7 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:8.2.5": - version: 8.2.5 - resolution: "inquirer@npm:8.2.5" - dependencies: - ansi-escapes: ^4.2.1 - chalk: ^4.1.1 - cli-cursor: ^3.1.0 - cli-width: ^3.0.0 - external-editor: ^3.0.3 - figures: ^3.0.0 - lodash: ^4.17.21 - mute-stream: 0.0.8 - ora: ^5.4.1 - run-async: ^2.4.0 - rxjs: ^7.5.5 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - through: ^2.3.6 - wrap-ansi: ^7.0.0 - checksum: f13ee4c444187786fb393609dedf6b30870115a57b603f2e6424f29a99abc13446fd45ee22461c33c9c40a92a60a8df62d0d6b25d74fc6676fa4cb211de55b55 - languageName: node - linkType: hard - -"inquirer@npm:^8.0.0, inquirer@npm:^8.2.0": +"inquirer@npm:8.2.6, inquirer@npm:^8.0.0, inquirer@npm:^8.2.0": version: 8.2.6 resolution: "inquirer@npm:8.2.6" dependencies: @@ -39930,12 +39907,12 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:7.8.0": - version: 7.8.0 - resolution: "rxjs@npm:7.8.0" +"rxjs@npm:7.8.1, rxjs@npm:^7.2.0, rxjs@npm:^7.5.5, rxjs@npm:^7.8.1": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" dependencies: tslib: ^2.1.0 - checksum: 61b4d4fd323c1043d8d6ceb91f24183b28bcf5def4f01ca111511d5c6b66755bc5578587fe714ef5d67cf4c9f2e26f4490d4e1d8cabf9bd5967687835e9866a2 + checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 languageName: node linkType: hard @@ -39948,15 +39925,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.2.0, rxjs@npm:^7.5.5, rxjs@npm:^7.8.1": - version: 7.8.1 - resolution: "rxjs@npm:7.8.1" - dependencies: - tslib: ^2.1.0 - checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 - languageName: node - linkType: hard - "sade@npm:^1.7.3": version: 1.8.1 resolution: "sade@npm:1.8.1" From ec48be1dab9540e7e501bc587883597ab74560f8 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 03:44:29 +0000 Subject: [PATCH 091/152] fix(deps): update dependency classnames to v2.5.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index e224a5129a..01f4f2ab04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22242,9 +22242,9 @@ __metadata: linkType: hard "classnames@npm:*, classnames@npm:^2.2.5, classnames@npm:^2.2.6, classnames@npm:^2.3.1": - version: 2.3.3 - resolution: "classnames@npm:2.3.3" - checksum: 0cb4db9cc3b8d2363c81f857f85b30351605fe683c89d140939aed4e06895a9570be737494ef3907e7069e2b98ed25ba3960fbf087b33309a18707fc38565e95 + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: da424a8a6f3a96a2e87d01a432ba19315503294ac7e025f9fece656db6b6a0f7b5003bb1fbb51cbb0d9624d964f1b9bb35a51c73af9b2434c7b292c42231c1e5 languageName: node linkType: hard From a4bdc80fd76fce2643875384b55ba163366c5fc0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 04:18:18 +0000 Subject: [PATCH 092/152] fix(deps): update dependency clsx to v2.1.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index 5e976cce69..e9f027d3a7 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -4383,9 +4383,9 @@ __metadata: linkType: hard "clsx@npm:^2.0.0": - version: 2.0.0 - resolution: "clsx@npm:2.0.0" - checksum: a2cfb2351b254611acf92faa0daf15220f4cd648bdf96ce369d729813b85336993871a4bf6978ddea2b81b5a130478339c20d9d0b5c6fc287e5147f0c059276e + version: 2.1.0 + resolution: "clsx@npm:2.1.0" + checksum: 43fefc29b6b49c9476fbce4f8b1cc75c27b67747738e598e6651dd40d63692135dc60b18fa1c5b78a2a9ba8ae6fd2055a068924b94e20b42039bd53b78b98e1d languageName: node linkType: hard From 74803f1568f08623c0f84b4569118160d63dbfec Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 05:32:26 +0000 Subject: [PATCH 093/152] fix(deps): update dependency core-js to v3.35.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0c40ea23ed..dd4ff651af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23234,9 +23234,9 @@ __metadata: linkType: hard "core-js@npm:^3.26.0, core-js@npm:^3.6.5": - version: 3.33.3 - resolution: "core-js@npm:3.33.3" - checksum: 070b9c696f5a9b22bab0208daf9fcdffbc3b4f4c89ed2be512b7d99d4be363accfbb26fa6eec7261055d89e8ea0a5bcfccad5923fe43f1f2eafca9f568cc5596 + version: 3.35.0 + resolution: "core-js@npm:3.35.0" + checksum: 25c224aca3df012b98f08f13ccbd8171ef5852acd33fd5e58e106d27f5f0c97de2fdbc520f0b4364d26253caf2deb3e5d265310f57d2a66ae6cc922850e649f0 languageName: node linkType: hard From c05175f515921051ec38580216c101a44e51bf8f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 05:33:02 +0000 Subject: [PATCH 094/152] fix(deps): update dependency css-loader to v6.9.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0c40ea23ed..82a38f7ea0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23600,20 +23600,20 @@ __metadata: linkType: hard "css-loader@npm:^6.5.1": - version: 6.8.1 - resolution: "css-loader@npm:6.8.1" + version: 6.9.1 + resolution: "css-loader@npm:6.9.1" dependencies: icss-utils: ^5.1.0 - postcss: ^8.4.21 + postcss: ^8.4.33 postcss-modules-extract-imports: ^3.0.0 - postcss-modules-local-by-default: ^4.0.3 - postcss-modules-scope: ^3.0.0 + postcss-modules-local-by-default: ^4.0.4 + postcss-modules-scope: ^3.1.1 postcss-modules-values: ^4.0.0 postcss-value-parser: ^4.2.0 - semver: ^7.3.8 + semver: ^7.5.4 peerDependencies: webpack: ^5.0.0 - checksum: 7c1784247bdbe76dc5c55fb1ac84f1d4177a74c47259942c9cfdb7a8e6baef11967a0bc85ac285f26bd26d5059decb848af8154a03fdb4f4894f41212f45eef3 + checksum: b0c1776f9c46474219eb471eeb8f4c8986a7735dc8356e578a63303cf292a7c8cb868fa74bf94a1a174e948fcb6523c63fca081cac6bbf246be8275b3cc384f0 languageName: node linkType: hard @@ -37050,27 +37050,27 @@ __metadata: languageName: node linkType: hard -"postcss-modules-local-by-default@npm:^4.0.0, postcss-modules-local-by-default@npm:^4.0.3": - version: 4.0.3 - resolution: "postcss-modules-local-by-default@npm:4.0.3" +"postcss-modules-local-by-default@npm:^4.0.0, postcss-modules-local-by-default@npm:^4.0.4": + version: 4.0.4 + resolution: "postcss-modules-local-by-default@npm:4.0.4" dependencies: icss-utils: ^5.0.0 postcss-selector-parser: ^6.0.2 postcss-value-parser: ^4.1.0 peerDependencies: postcss: ^8.1.0 - checksum: 2f8083687f3d6067885f8863dd32dbbb4f779cfcc7e52c17abede9311d84faf6d3ed8760e7c54c6380281732ae1f78e5e56a28baf3c271b33f450a11c9e30485 + checksum: 578b955b0773147890caa88c30b10dfc849c5b1412a47ad51751890dba16fca9528c3ab00a19b186a8c2c150c2d08e2ce64d3d907800afee1f37c6d38252e365 languageName: node linkType: hard -"postcss-modules-scope@npm:^3.0.0": - version: 3.0.0 - resolution: "postcss-modules-scope@npm:3.0.0" +"postcss-modules-scope@npm:^3.0.0, postcss-modules-scope@npm:^3.1.1": + version: 3.1.1 + resolution: "postcss-modules-scope@npm:3.1.1" dependencies: postcss-selector-parser: ^6.0.4 peerDependencies: postcss: ^8.1.0 - checksum: 330b9398dbd44c992c92b0dc612c0626135e2cc840fee41841eb61247a6cfed95af2bd6f67ead9dd9d0bb41f5b0367129d93c6e434fa3e9c58ade391d9a5a138 + checksum: 9e9d23abb0babc7fa243be65704d72a5a9ceb2bded4dbaef96a88210d468b03c8c3158c197f4e22300c851f08c6fdddd6ebe65f44e4c34448b45b8a2e063a16d languageName: node linkType: hard @@ -37283,7 +37283,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.1.0, postcss@npm:^8.4.21, postcss@npm:^8.4.27": +"postcss@npm:^8.1.0, postcss@npm:^8.4.27, postcss@npm:^8.4.33": version: 8.4.33 resolution: "postcss@npm:8.4.33" dependencies: From 60528c59a497d2f2975aa897624f65e06f4ea555 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 06:22:41 +0000 Subject: [PATCH 095/152] fix(deps): update dependency elastic-builder to v2.24.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 651198d5d7..33ee0fc70f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25041,8 +25041,8 @@ __metadata: linkType: hard "elastic-builder@npm:^2.16.0": - version: 2.23.0 - resolution: "elastic-builder@npm:2.23.0" + version: 2.24.0 + resolution: "elastic-builder@npm:2.24.0" dependencies: lodash.has: ^4.5.2 lodash.hasin: ^4.5.2 @@ -25052,7 +25052,7 @@ __metadata: lodash.isobject: ^3.0.2 lodash.isstring: ^4.0.1 lodash.omit: ^4.5.0 - checksum: 6cd3d6fd3ea20adb2f48a21d3bd25fd89dd9f692feab95357356b8a814f5a36579c7a8326e818127fc5ec50774c1818c4f8e5d0aeea00cccd0aeda5563e17123 + checksum: 4bbfa66a179b78dbd90a0a3ee19cf7fc2105deca195c65d3047cf2ef2e6fa4925049038ed1f28d46a3ce96daa47f2cf8d924ea2e2fc0c585569a5a7acd09f2ed languageName: node linkType: hard From 6009879578b91f08b2fdf2dcbd62d3cab0cc772a Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 07:09:54 +0000 Subject: [PATCH 096/152] fix(deps): update dependency graphiql to v3.1.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 33ee0fc70f..92d760c61f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28409,7 +28409,7 @@ __metadata: languageName: node linkType: hard -"graphiql@npm:3.0.10, graphiql@npm:^3.0.6": +"graphiql@npm:3.0.10": version: 3.0.10 resolution: "graphiql@npm:3.0.10" dependencies: @@ -28425,6 +28425,22 @@ __metadata: languageName: node linkType: hard +"graphiql@npm:^3.0.6": + version: 3.1.0 + resolution: "graphiql@npm:3.1.0" + dependencies: + "@graphiql/react": ^0.20.2 + "@graphiql/toolkit": ^0.9.1 + graphql-language-service: ^5.2.0 + markdown-it: ^12.2.0 + peerDependencies: + graphql: ^15.5.0 || ^16.0.0 + react: ^16.8.0 || ^17 || ^18 + react-dom: ^16.8.0 || ^17 || ^18 + checksum: a8969ea299561fe627ba566fb72dd90eda31b195e9983af87c4079bf807f5b356d116ece133e46d5098d6809a88b8f3b64b04a9e95dfe34cd84eb8fd109e2b8a + languageName: node + linkType: hard + "graphlib@npm:^2.1.8": version: 2.1.8 resolution: "graphlib@npm:2.1.8" From 8ab9b2d95d5ac59f8e6ba7502f7ca8eeab8940a9 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 07:10:26 +0000 Subject: [PATCH 097/152] fix(deps): update dependency html-webpack-plugin to v5.6.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 33ee0fc70f..7808d07f7f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28990,8 +28990,8 @@ __metadata: linkType: hard "html-webpack-plugin@npm:^5.3.1": - version: 5.5.4 - resolution: "html-webpack-plugin@npm:5.5.4" + version: 5.6.0 + resolution: "html-webpack-plugin@npm:5.6.0" dependencies: "@types/html-minifier-terser": ^6.0.0 html-minifier-terser: ^6.0.2 @@ -28999,8 +28999,14 @@ __metadata: pretty-error: ^4.0.0 tapable: ^2.0.0 peerDependencies: + "@rspack/core": 0.x || 1.x webpack: ^5.20.0 - checksum: b49befb73d67a3716fd0e6f7776b108d2b0b7050fb8221f05cd114cbae13c03150a13b7cdf5e76170be040ce7936a1cf76f7a4bfd9ebe1552b72d7889a74c374 + peerDependenciesMeta: + "@rspack/core": + optional: true + webpack: + optional: true + checksum: 32a6e41da538e798fd0be476637d7611a5e8a98a3508f031996e9eb27804dcdc282cb01f847cf5d066f21b49cfb8e21627fcf977ffd0c9bea81cf80e5a65070d languageName: node linkType: hard From 98181f627c1a1841e27ae828d8611484e8d686d1 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:10:32 +0000 Subject: [PATCH 098/152] fix(deps): update dependency json-schema-to-ts to v2.12.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 25f67aaed4..818865fba7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31616,13 +31616,13 @@ __metadata: linkType: hard "json-schema-to-ts@npm:^2.6.2": - version: 2.9.2 - resolution: "json-schema-to-ts@npm:2.9.2" + version: 2.12.0 + resolution: "json-schema-to-ts@npm:2.12.0" dependencies: "@babel/runtime": ^7.18.3 "@types/json-schema": ^7.0.9 - ts-algebra: ^1.2.0 - checksum: 64bf7226511a3719075d28e715fbd203576088b6ebcf494d6c5ee2bcf5b24d6081fa0cfce22ce254ad8798b7044603db4e4d19779c6f5765285fb9ddaa0756ef + ts-algebra: ^1.2.2 + checksum: 6dc4bc836591d888beb20e8bf45dfa3b75df4a331f18675bd2e39a2262e105e0dc86012031fff0be408499a07ae8bc30f5a879c24696189b2c30a34edd5fa72f languageName: node linkType: hard @@ -42475,10 +42475,10 @@ __metadata: languageName: node linkType: hard -"ts-algebra@npm:^1.2.0": - version: 1.2.0 - resolution: "ts-algebra@npm:1.2.0" - checksum: 471d3a049dbceadf2355f980e4d0a4cba413b50e66f0c8fb5eece876c238f7d2a270e1c85aa9ebd04349ae70335715f8e6d87338fd8cf755d12e285e884b3d91 +"ts-algebra@npm:^1.2.2": + version: 1.2.2 + resolution: "ts-algebra@npm:1.2.2" + checksum: eea0c08fba10c4a758086079c575a32c4c050f50087e06fcc1ccd53b045bdf7bdb0ab9597a52a241a714f058ff047cedbddcb1539de5e66bb424aa5b33c677a1 languageName: node linkType: hard From dd15d3df0cedafa483dab38761c7ce35ff9769cc Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 08:11:11 +0000 Subject: [PATCH 099/152] fix(deps): update dependency knex to v3.1.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index 25f67aaed4..e2f53b8bfc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -32141,8 +32141,8 @@ __metadata: linkType: hard "knex@npm:3, knex@npm:^3.0.0": - version: 3.0.1 - resolution: "knex@npm:3.0.1" + version: 3.1.0 + resolution: "knex@npm:3.1.0" dependencies: colorette: 2.0.19 commander: ^10.0.0 @@ -32153,7 +32153,7 @@ __metadata: getopts: 2.3.0 interpret: ^2.2.0 lodash: ^4.17.21 - pg-connection-string: 2.6.1 + pg-connection-string: 2.6.2 rechoir: ^0.8.0 resolve-from: ^5.0.0 tarn: ^3.0.2 @@ -32175,7 +32175,7 @@ __metadata: optional: true bin: knex: bin/cli.js - checksum: bcfc3f8da9a7e898a873d2a122856ac9355f5ee1c0ab39534d6cac9ea69388da2fe0fa607b20d65298e191a4377af72f8ff7f4430f8b8c4abc144010b7e9796c + checksum: 3905f8d27960975f7f57f3f488d1ef3ccf47784acc8eb627e8a28cbbe1f296c6879c8ef0cbd9e17e867be80117d305cd948545f3fbd4c74b24c90d2413bbc021 languageName: node linkType: hard @@ -36542,14 +36542,7 @@ __metadata: languageName: node linkType: hard -"pg-connection-string@npm:2.6.1": - version: 2.6.1 - resolution: "pg-connection-string@npm:2.6.1" - checksum: 882344a47e1ecf3a91383e0809bf2ac48facea97fcec0358d6e060e1cbcb8737acde419b4c86f05da4ce4a16634ee50fff1d2bb787d73b52ccbfde697243ad8a - languageName: node - linkType: hard - -"pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.5.0, pg-connection-string@npm:^2.6.2": +"pg-connection-string@npm:2.6.2, pg-connection-string@npm:^2.3.0, pg-connection-string@npm:^2.5.0, pg-connection-string@npm:^2.6.2": version: 2.6.2 resolution: "pg-connection-string@npm:2.6.2" checksum: 22265882c3b6f2320785378d0760b051294a684989163d5a1cde4009e64e84448d7bf67d9a7b9e7f69440c3ee9e2212f9aa10dd17ad6773f6143c6020cebbcb5 From 029295cd940bf23b54267295ed038d915529f59c Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jan 2024 14:13:36 +0100 Subject: [PATCH 100/152] chore: remove the legacy exports from alpha Signed-off-by: blam --- packages/app/src/App.tsx | 76 +-- plugins/scaffolder-react/src/alpha.ts | 1 - .../src/legacy/extensions/index.tsx | 56 -- .../src/legacy/extensions/types.ts | 65 --- plugins/scaffolder-react/src/legacy/index.ts | 16 - plugins/scaffolder/dev/index.tsx | 25 +- plugins/scaffolder/src/components/index.ts | 2 - plugins/scaffolder/src/components/types.ts | 15 - plugins/scaffolder/src/deprecated.ts | 10 + .../FieldOverrides/DescriptionField.tsx | 22 - .../MultistepJsonForm/FieldOverrides/index.ts | 16 - .../MultistepJsonForm.test.tsx | 88 --- .../MultistepJsonForm/MultistepJsonForm.tsx | 225 -------- .../legacy/MultistepJsonForm/ReviewStep.tsx | 128 ----- .../src/legacy/MultistepJsonForm/index.ts | 16 - .../legacy/MultistepJsonForm/schema.test.ts | 535 ------------------ .../src/legacy/MultistepJsonForm/schema.ts | 129 ----- plugins/scaffolder/src/legacy/Router.tsx | 183 ------ .../legacy/ScaffolderPage/ScaffolderPage.tsx | 159 ------ .../ScaffolderPageContextMenu.test.tsx | 83 --- .../ScaffolderPageContextMenu.tsx | 129 ----- .../src/legacy/ScaffolderPage/index.ts | 16 - .../src/legacy/TaskPage/IconLink.test.tsx | 30 - .../src/legacy/TaskPage/TaskErrors.tsx | 39 -- .../src/legacy/TaskPage/TaskPage.tsx | 393 ------------- .../legacy/TaskPage/TaskPageLinks.test.tsx | 67 --- .../scaffolder/src/legacy/TaskPage/index.ts | 17 - .../src/legacy/TemplateCard/TemplateCard.tsx | 316 ----------- .../src/legacy/TemplateCard/index.ts | 17 - .../CustomFieldExplorer.tsx | 205 ------- .../TemplateEditorPage/TemplateEditor.tsx | 92 --- .../TemplateEditorPage/TemplateEditorForm.tsx | 261 --------- .../TemplateEditorPage/TemplateEditorPage.tsx | 105 ---- .../TemplateFormPreviewer.tsx | 221 -------- .../src/legacy/TemplateEditorPage/index.ts | 16 - .../legacy/TemplateList/TemplateList.test.tsx | 83 --- .../src/legacy/TemplateList/TemplateList.tsx | 114 ---- .../src/legacy/TemplateList/index.ts | 17 - .../legacy/TemplatePage/TemplatePage.test.tsx | 366 ------------ .../src/legacy/TemplatePage/TemplatePage.tsx | 187 ------ .../TemplatePage/createValidator.test.ts | 200 ------- .../legacy/TemplatePage/createValidator.ts | 103 ---- .../src/legacy/TemplatePage/index.ts | 17 - plugins/scaffolder/src/legacy/index.ts | 17 - .../DryRunResults/DryRunResultsView.tsx | 4 +- .../DryRunResults}/IconLink.tsx | 3 +- .../DryRunResults}/TaskPageLinks.tsx | 0 .../DryRunResults/TaskStatusStepper.tsx | 199 +++++++ plugins/scaffolder/src/plugin.tsx | 12 - 49 files changed, 236 insertions(+), 4860 deletions(-) delete mode 100644 plugins/scaffolder-react/src/legacy/extensions/index.tsx delete mode 100644 plugins/scaffolder-react/src/legacy/extensions/types.ts delete mode 100644 plugins/scaffolder-react/src/legacy/index.ts delete mode 100644 plugins/scaffolder/src/components/types.ts delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/DescriptionField.tsx delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/index.ts delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.tsx delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/ReviewStep.tsx delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/index.ts delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/schema.test.ts delete mode 100644 plugins/scaffolder/src/legacy/MultistepJsonForm/schema.ts delete mode 100644 plugins/scaffolder/src/legacy/Router.tsx delete mode 100644 plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPage.tsx delete mode 100644 plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.tsx delete mode 100644 plugins/scaffolder/src/legacy/ScaffolderPage/index.ts delete mode 100644 plugins/scaffolder/src/legacy/TaskPage/IconLink.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/TaskPage/TaskErrors.tsx delete mode 100644 plugins/scaffolder/src/legacy/TaskPage/TaskPage.tsx delete mode 100644 plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/TaskPage/index.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplateCard/TemplateCard.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateCard/index.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/CustomFieldExplorer.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditor.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorForm.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorPage.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateFormPreviewer.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateEditorPage/index.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplateList/TemplateList.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateList/TemplateList.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplateList/index.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.test.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.tsx delete mode 100644 plugins/scaffolder/src/legacy/TemplatePage/createValidator.test.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplatePage/createValidator.ts delete mode 100644 plugins/scaffolder/src/legacy/TemplatePage/index.ts delete mode 100644 plugins/scaffolder/src/legacy/index.ts rename plugins/scaffolder/src/{legacy/TaskPage => next/TemplateEditorPage/DryRunResults}/IconLink.tsx (97%) rename plugins/scaffolder/src/{legacy/TaskPage => next/TemplateEditorPage/DryRunResults}/TaskPageLinks.tsx (100%) create mode 100644 plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.tsx diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index c2296d01e7..d2dde95f68 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -31,7 +31,6 @@ import { AppRouter, ConfigReader, defaultConfigLoader, - FeatureFlagged, FlatRoutes, } from '@backstage/core-app-api'; import { @@ -64,7 +63,6 @@ import { GcpProjectsPage } from '@backstage/plugin-gcp-projects'; import { HomepageCompositionRoot, VisitListener } from '@backstage/plugin-home'; import { LighthousePage } from '@backstage/plugin-lighthouse'; import { NewRelicPage } from '@backstage/plugin-newrelic'; -import { LegacyScaffolderPage } from '@backstage/plugin-scaffolder/alpha'; import { ScaffolderPage, scaffolderPlugin } from '@backstage/plugin-scaffolder'; import { ScaffolderFieldExtensions, @@ -96,10 +94,7 @@ import { apis } from './apis'; import { entityPage } from './components/catalog/EntityPage'; import { homePage } from './components/home/HomePage'; import { Root } from './components/Root'; -import { - DelayingComponentFieldExtension, - LowerCaseValuePickerFieldExtension, -} from './components/scaffolder/customScaffolderExtensions'; +import { DelayingComponentFieldExtension } from './components/scaffolder/customScaffolderExtensions'; import { defaultPreviewTemplate } from './components/scaffolder/defaultPreviewTemplate'; import { searchPage } from './components/search/SearchPage'; import { providers } from './identityProviders'; @@ -236,53 +231,28 @@ const routes = ( - - - entity?.metadata?.tags?.includes('recommended') ?? false, - }, - ]} - /> - } - > - - - - - - - - - - - entity?.metadata?.tags?.includes('recommended') ?? false, - }, - ]} - /> - } - > - - - - - - - - + + entity?.metadata?.tags?.includes('recommended') ?? false, + }, + ]} + /> + } + > + + + + + + + } /> ( - options: LegacyFieldExtensionOptions, -): Extension> { - return { - expose() { - const FieldExtensionDataHolder: any = () => null; - - attachComponentData( - FieldExtensionDataHolder, - FIELD_EXTENSION_KEY, - options, - ); - - return FieldExtensionDataHolder; - }, - }; -} - -export type { - LegacyCustomFieldValidator, - LegacyFieldExtensionOptions, - LegacyFieldExtensionComponentProps, -}; diff --git a/plugins/scaffolder-react/src/legacy/extensions/types.ts b/plugins/scaffolder-react/src/legacy/extensions/types.ts deleted file mode 100644 index 54c38ad2cd..0000000000 --- a/plugins/scaffolder-react/src/legacy/extensions/types.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { ApiHolder } from '@backstage/core-plugin-api'; -import { FieldValidation } from '@rjsf/utils'; -import { - CustomFieldExtensionSchema, - ScaffolderRJSFFieldProps, -} from '@backstage/plugin-scaffolder-react'; - -/** - * Field validation type for Custom Field Extensions. - * - * @alpha - */ -export type LegacyCustomFieldValidator = ( - data: TFieldReturnValue, - field: FieldValidation, - context: { apiHolder: ApiHolder }, -) => void | Promise; - -/** - * Type for the Custom Field Extension with the - * name and components and validation function. - * - * @alpha - */ -export type LegacyFieldExtensionOptions< - TFieldReturnValue = unknown, - TInputProps = unknown, -> = { - name: string; - component: ( - props: LegacyFieldExtensionComponentProps, - ) => JSX.Element | null; - validation?: LegacyCustomFieldValidator; - schema?: CustomFieldExtensionSchema; -}; - -/** - * Type for field extensions and being able to type - * incoming props easier. - * - * @alpha - */ -export interface LegacyFieldExtensionComponentProps< - TFieldReturnValue, - TUiOptions = unknown, -> extends ScaffolderRJSFFieldProps { - uiSchema: ScaffolderRJSFFieldProps['uiSchema'] & { - 'ui:options'?: TUiOptions; - }; -} diff --git a/plugins/scaffolder-react/src/legacy/index.ts b/plugins/scaffolder-react/src/legacy/index.ts deleted file mode 100644 index c539a8ba60..0000000000 --- a/plugins/scaffolder-react/src/legacy/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export * from './extensions'; diff --git a/plugins/scaffolder/dev/index.tsx b/plugins/scaffolder/dev/index.tsx index b683345ff5..e4425b04bf 100644 --- a/plugins/scaffolder/dev/index.tsx +++ b/plugins/scaffolder/dev/index.tsx @@ -24,7 +24,7 @@ import { } from '@backstage/plugin-catalog-react'; import React from 'react'; import { scaffolderApiRef, ScaffolderClient } from '../src'; -import { ScaffolderPage, LegacyScaffolderPage } from '../src/plugin'; +import { ScaffolderPage } from '../src/plugin'; import { discoveryApiRef, fetchApiRef, @@ -68,11 +68,6 @@ createDevApp() title: 'Create', element: , }) - .addPage({ - path: '/legacy-create', - title: 'Create (next)', - element: , - }) .addPage({ path: '/create-groups', title: 'Groups', @@ -91,22 +86,4 @@ createDevApp() /> ), }) - .addPage({ - path: '/legacy-create-groups', - title: 'Groups (next)', - element: ( - e.metadata.tags?.includes('techdocs') || false, - title: 'Techdocs', - }, - { - filter: e => e.metadata.tags?.includes('react') || false, - title: 'React', - }, - ]} - /> - ), - }) .render(); diff --git a/plugins/scaffolder/src/components/index.ts b/plugins/scaffolder/src/components/index.ts index fd8c11e2da..552a75c99b 100644 --- a/plugins/scaffolder/src/components/index.ts +++ b/plugins/scaffolder/src/components/index.ts @@ -22,5 +22,3 @@ export type { RouterProps } from './Router'; export { OngoingTask as TaskPage } from './OngoingTask'; export type { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; - -export type { TaskPageProps } from '../legacy/TaskPage'; diff --git a/plugins/scaffolder/src/components/types.ts b/plugins/scaffolder/src/components/types.ts deleted file mode 100644 index b1e99a84e6..0000000000 --- a/plugins/scaffolder/src/components/types.ts +++ /dev/null @@ -1,15 +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. - */ diff --git a/plugins/scaffolder/src/deprecated.ts b/plugins/scaffolder/src/deprecated.ts index f08a83c594..ffe923058b 100644 --- a/plugins/scaffolder/src/deprecated.ts +++ b/plugins/scaffolder/src/deprecated.ts @@ -212,3 +212,13 @@ export type LayoutTemplate = LayoutTemplateTemp; * @deprecated use import from {@link @backstage/plugin-scaffolder-react#LayoutOptions} instead as this has now been moved. */ export type LayoutOptions = LayoutOptionsTemp; + +/** + * TaskPageProps for constructing a TaskPage + * @param loadingText - Optional loading text shown before a task begins executing. + * @public + * @deprecated - this is a useless type that is no longer used. + */ +export type TaskPageProps = { + loadingText?: string; +}; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/DescriptionField.tsx b/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/DescriptionField.tsx deleted file mode 100644 index eded38beee..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/DescriptionField.tsx +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React from 'react'; -import { MarkdownContent } from '@backstage/core-components'; -import { FieldProps } from '@rjsf/utils'; - -export const DescriptionField = ({ description }: FieldProps) => - description && ; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/index.ts b/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/index.ts deleted file mode 100644 index d6d0bf3499..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/FieldOverrides/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { DescriptionField } from './DescriptionField'; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.test.tsx b/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.test.tsx deleted file mode 100644 index 3f8e153b65..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.test.tsx +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { getReviewData, getUiSchemasFromSteps } from './ReviewStep'; - -describe('MultistepJsonForm', () => { - const formDataMock = { - password: 'password', - masked: 'Some info to mask', - open: 'Some open info', - hidden: 'Some info to hide', - 'other-open': 'Other open info', - }; - - const stepsMock = [ - { - title: 'The test template', - schema: { - title: 'The test template', - properties: { - password: { - title: 'Password', - type: 'string', - 'ui:widget': 'password', - }, - masked: { - title: 'Masked', - type: 'string', - 'ui:backstage': { - review: { - mask: '******', - }, - }, - }, - open: { - title: 'Open info', - type: 'string', - }, - }, - }, - }, - { - title: 'Other fields', - schema: { - title: 'Other fields', - properties: { - hidden: { - title: 'Hidden', - type: 'string', - 'ui:backstage': { - review: { - show: false, - }, - }, - }, - 'other-open': { - title: 'Other Open Info', - type: 'string', - }, - }, - }, - }, - ]; - - test('Fields are defined to be hidden or masked', () => { - const schemas = getUiSchemasFromSteps(stepsMock); - const reviewData = getReviewData(formDataMock, schemas); - - expect(reviewData.password).toBe('******'); - expect(reviewData.masked).toBe('******'); - expect(reviewData.open).toBe('Some open info'); - expect(reviewData.hidden).toBeUndefined(); - expect(reviewData['other-open']).toBe('Other open info'); - }); -}); diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.tsx b/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.tsx deleted file mode 100644 index 68b25d97d2..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/MultistepJsonForm.tsx +++ /dev/null @@ -1,225 +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, - Step as StepUI, - StepContent, - StepLabel, - Stepper, - Typography, -} from '@material-ui/core'; -import { JsonObject } from '@backstage/types'; -import { - errorApiRef, - featureFlagsApiRef, - useAnalytics, - useRouteRefParams, - useApi, -} from '@backstage/core-plugin-api'; -import { FormProps, IChangeEvent, withTheme } from '@rjsf/core'; -import { Theme as MuiTheme } from '@rjsf/material-ui'; -import React, { ComponentType, useState } from 'react'; -import { transformSchemaToProps } from './schema'; -import cloneDeep from 'lodash/cloneDeep'; -import * as fieldOverrides from './FieldOverrides'; -import { ReviewStep } from './ReviewStep'; -import validator from '@rjsf/validator-ajv8'; -import { extractSchemaFromStep } from '@backstage/plugin-scaffolder-react/alpha'; -import { selectedTemplateRouteRef } from '../../routes'; -import { - LayoutOptions, - ReviewStepProps, -} from '@backstage/plugin-scaffolder-react'; - -const Form = withTheme(MuiTheme); - -type Step = { - schema: JsonObject; - title: string; -} & Partial, 'schema'>>; - -/** - * The props for a dynamic form of a scaffolder template. - */ -export type MultistepJsonFormProps = { - /** - * Steps for the form, each contains title and form schema - */ - steps: Step[]; - formData: Record; - onChange: (e: IChangeEvent) => void; - onReset: () => void; - onFinish?: () => Promise; - widgets?: FormProps['widgets']; - fields?: FormProps['fields']; - finishButtonLabel?: string; - layouts: LayoutOptions[]; - ReviewStepComponent?: ComponentType; -}; - -export function getSchemasFromSteps(steps: Step[]) { - return steps.map(({ schema }) => ({ - mergedSchema: schema, - ...extractSchemaFromStep(schema), - })); -} - -/** - * Creates the dynamic form for a scaffolder template. - */ -export const MultistepJsonForm = (props: MultistepJsonFormProps) => { - const { - formData, - onChange, - onReset, - onFinish, - fields, - widgets, - layouts, - ReviewStepComponent, - } = props; - const { templateName } = useRouteRefParams(selectedTemplateRouteRef); - const analytics = useAnalytics(); - const [activeStep, setActiveStep] = useState(0); - const [disableButtons, setDisableButtons] = useState(false); - const errorApi = useApi(errorApiRef); - const featureFlagApi = useApi(featureFlagsApiRef); - const featureFlagKey = 'backstage:featureFlag'; - const filterOutProperties = (step: Step): Step => { - const filteredStep = cloneDeep(step); - const removedPropertyKeys: Array = []; - if (filteredStep.schema.properties) { - filteredStep.schema.properties = Object.fromEntries( - Object.entries(filteredStep.schema.properties).filter( - ([key, value]) => { - if (value[featureFlagKey]) { - if (featureFlagApi.isActive(value[featureFlagKey])) { - return true; - } - removedPropertyKeys.push(key); - return false; - } - return true; - }, - ), - ); - - // remove the feature flag property key from required if they are not active - filteredStep.schema.required = Array.isArray(filteredStep.schema.required) - ? filteredStep.schema.required?.filter( - r => !removedPropertyKeys.includes(r as string), - ) - : filteredStep.schema.required; - } - return filteredStep; - }; - - const steps = props.steps - .filter(step => { - const featureFlag = step.schema[featureFlagKey]; - return ( - typeof featureFlag !== 'string' || featureFlagApi.isActive(featureFlag) - ); - }) - .map(filterOutProperties); - - const handleReset = () => { - setActiveStep(0); - onReset(); - }; - const handleNext = () => { - const stepNum = Math.min(activeStep + 1, steps.length); - setActiveStep(stepNum); - analytics.captureEvent('click', `Next Step (${stepNum})`); - }; - const handleBack = () => setActiveStep(Math.max(activeStep - 1, 0)); - const handleCreate = async () => { - if (!onFinish) { - return; - } - - setDisableButtons(true); - try { - await onFinish(); - analytics.captureEvent('create', formData.name || `new ${templateName}`); - } catch (err) { - errorApi.post(err); - } finally { - setDisableButtons(false); - } - }; - - const ReviewStepElement = ReviewStepComponent ?? ReviewStep; - - return ( - <> - - {steps.map(({ title, schema, ...formProps }, index) => { - return ( - - - - {title} - - - -
) => { - if (e.errors.length === 0) handleNext(); - }} - experimental_defaultFormStateBehavior={{ - allOf: 'populateDefaults', - }} - {...formProps} - {...transformSchemaToProps(schema, layouts)} - > - - -
-
-
- ); - })} -
- {activeStep === steps.length && ( - - )} - - ); -}; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/ReviewStep.tsx b/plugins/scaffolder/src/legacy/MultistepJsonForm/ReviewStep.tsx deleted file mode 100644 index c3b244e921..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/ReviewStep.tsx +++ /dev/null @@ -1,128 +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 { Box, Button, Paper, Typography } from '@material-ui/core'; -import React from 'react'; -import { Content, StructuredMetadataTable } from '@backstage/core-components'; -import { UiSchema } from '@rjsf/utils'; -import { JsonObject } from '@backstage/types'; -import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; - -export function getReviewData( - formData: Record, - uiSchemas: UiSchema[], -) { - const reviewData: Record = {}; - const orderedReviewProperties = new Set( - uiSchemas.map(us => us.name).concat(Object.getOwnPropertyNames(formData)), - ); - - for (const key of orderedReviewProperties) { - const uiSchema = uiSchemas.find(us => us.name === key); - - if (!uiSchema) { - reviewData[key] = formData[key]; - continue; - } - - if (uiSchema['ui:widget'] === 'password') { - reviewData[key] = '******'; - continue; - } - - if (!uiSchema['ui:backstage'] || !uiSchema['ui:backstage'].review) { - reviewData[key] = formData[key]; - continue; - } - - const review = uiSchema['ui:backstage'].review as JsonObject; - if (review.mask) { - reviewData[key] = review.mask; - continue; - } - - if (!review.show) { - continue; - } - reviewData[key] = formData[key]; - } - - return reviewData; -} - -export function getUiSchemasFromSteps( - steps: { - schema: JsonObject; - }[], -): UiSchema[] { - const uiSchemas: Array = []; - steps.forEach(step => { - const schemaProps = step.schema.properties as JsonObject; - for (const key in schemaProps) { - if (schemaProps.hasOwnProperty(key)) { - const uiSchema = schemaProps[key] as UiSchema; - uiSchema.name = key; - uiSchemas.push(uiSchema); - } - } - }); - return uiSchemas; -} - -/** - * The component displaying the Last Step in scaffolder template form. - * Which represents the summary of the input provided by the end user. - */ -export const ReviewStep = (props: ReviewStepProps) => { - const { - disableButtons, - formData, - handleBack, - handleCreate, - handleReset, - steps, - } = props; - return ( - - - Review and create - ({ schema: mergedSchema })), - ), - )} - /> - - - - - - - ); -}; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/index.ts b/plugins/scaffolder/src/legacy/MultistepJsonForm/index.ts deleted file mode 100644 index b125e7c4c2..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/index.ts +++ /dev/null @@ -1,16 +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. - */ -export { MultistepJsonForm } from './MultistepJsonForm'; diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.test.ts b/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.test.ts deleted file mode 100644 index af5bb7fac5..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.test.ts +++ /dev/null @@ -1,535 +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 { transformSchemaToProps } from './schema'; - -describe('transformSchemaToProps', () => { - it('transforms deep schema', () => { - const inputSchema = { - type: 'object', - 'ui:welp': 'warp', - properties: { - field1: { - type: 'string', - 'ui:derp': 'herp', - }, - field2: { - type: 'object', - properties: { - fieldX: { - type: 'string', - 'ui:derp': 'xerp', - }, - }, - }, - }, - }; - const expectedSchema = { - type: 'object', - properties: { - field1: { - type: 'string', - }, - field2: { - type: 'object', - properties: { - fieldX: { - type: 'string', - }, - }, - }, - }, - }; - const expectedUiSchema = { - 'ui:welp': 'warp', - field1: { - 'ui:derp': 'herp', - }, - field2: { - fieldX: { - 'ui:derp': 'xerp', - }, - }, - }; - - expect(transformSchemaToProps(inputSchema)).toEqual({ - schema: expectedSchema, - uiSchema: expectedUiSchema, - }); - }); - - it('transforms schema with anyOf fields', () => { - const inputSchema = { - type: 'object', - anyOf: [ - { - properties: { - field3: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - { - properties: { - field3: { - type: 'string', - default: 'Value 2', - 'ui:readonly': true, - }, - }, - }, - ], - oneOf: [ - { - properties: { - field4: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - ], - allOf: [ - { - properties: { - field5: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - ], - properties: { - field1: { - type: 'object', - anyOf: [ - { - properties: { - field3: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - { - properties: { - field3: { - type: 'string', - default: 'Value 2', - 'ui:readonly': true, - }, - }, - }, - ], - oneOf: [ - { - properties: { - field4: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - ], - allOf: [ - { - properties: { - field5: { - type: 'string', - default: 'Value 1', - 'ui:readonly': true, - }, - }, - }, - ], - }, - field2: { - type: 'string', - 'ui:derp': 'xerp', - }, - }, - }; - const expectedSchema = { - type: 'object', - anyOf: [ - { - properties: { - field3: { - type: 'string', - default: 'Value 1', - }, - }, - }, - { - properties: { - field3: { - type: 'string', - default: 'Value 2', - }, - }, - }, - ], - oneOf: [ - { - properties: { - field4: { - type: 'string', - default: 'Value 1', - }, - }, - }, - ], - allOf: [ - { - properties: { - field5: { - type: 'string', - default: 'Value 1', - }, - }, - }, - ], - properties: { - field1: { - type: 'object', - anyOf: [ - { - properties: { - field3: { - type: 'string', - default: 'Value 1', - }, - }, - }, - { - properties: { - field3: { - type: 'string', - default: 'Value 2', - }, - }, - }, - ], - oneOf: [ - { - properties: { - field4: { - type: 'string', - default: 'Value 1', - }, - }, - }, - ], - allOf: [ - { - properties: { - field5: { - type: 'string', - default: 'Value 1', - }, - }, - }, - ], - }, - field2: { - type: 'string', - }, - }, - }; - const expectedUiSchema = { - field3: { - 'ui:readonly': true, - }, - field4: { - 'ui:readonly': true, - }, - field5: { - 'ui:readonly': true, - }, - field1: { - field3: { - 'ui:readonly': true, - }, - field4: { - 'ui:readonly': true, - }, - field5: { - 'ui:readonly': true, - }, - }, - field2: { - 'ui:derp': 'xerp', - }, - }; - - expect(transformSchemaToProps(inputSchema)).toEqual({ - schema: expectedSchema, - uiSchema: expectedUiSchema, - }); - }); - - it('transforms schema with dependencies', () => { - const inputSchema = { - type: 'object', - properties: { - name: { - type: 'string', - }, - credit_card: { - type: 'number', - }, - }, - required: ['name'], - dependencies: { - credit_card: { - properties: { - billing_address: { - type: 'string', - 'ui:widget': 'textarea', - }, - }, - required: ['billing_address'], - }, - }, - }; - const expectedSchema = { - type: 'object', - properties: { - name: { - type: 'string', - }, - credit_card: { - type: 'number', - }, - }, - required: ['name'], - dependencies: { - credit_card: { - properties: { - billing_address: { - type: 'string', - }, - }, - required: ['billing_address'], - }, - }, - }; - const expectedUiSchema = { - billing_address: { - 'ui:widget': 'textarea', - }, - credit_card: {}, - name: {}, - }; - - expect(transformSchemaToProps(inputSchema)).toEqual({ - schema: expectedSchema, - uiSchema: expectedUiSchema, - }); - }); - - it('transforms schema with complex dependencies', () => { - const inputSchema = { - type: 'object', - properties: { - conditional: { - title: 'Person', - type: 'object', - properties: { - 'Do you have any pets?': { - type: 'string', - enum: ['No', 'Yes: One', 'Yes: More than one'], - default: 'No', - 'ui:widget': 'radio', - }, - }, - required: ['Do you have any pets?'], - dependencies: { - 'Do you have any pets?': { - oneOf: [ - { - properties: { - 'Do you have any pets?': { - enum: ['No'], - }, - }, - }, - { - properties: { - 'Do you have any pets?': { - enum: ['Yes: One'], - }, - 'How old is your pet?': { - type: 'number', - }, - }, - required: ['How old is your pet?'], - }, - { - properties: { - 'Do you have any pets?': { - enum: ['Yes: More than one'], - }, - 'Do you want to get rid of any?': { - type: 'boolean', - }, - }, - required: ['Do you want to get rid of any?'], - }, - ], - }, - }, - }, - }, - }; - const expectedSchema = { - type: 'object', - properties: { - conditional: { - title: 'Person', - type: 'object', - properties: { - 'Do you have any pets?': { - type: 'string', - enum: ['No', 'Yes: One', 'Yes: More than one'], - default: 'No', - }, - }, - required: ['Do you have any pets?'], - dependencies: { - 'Do you have any pets?': { - oneOf: [ - { - properties: { - 'Do you have any pets?': { - enum: ['No'], - }, - }, - }, - { - properties: { - 'Do you have any pets?': { - enum: ['Yes: One'], - }, - 'How old is your pet?': { - type: 'number', - }, - }, - required: ['How old is your pet?'], - }, - { - properties: { - 'Do you have any pets?': { - enum: ['Yes: More than one'], - }, - 'Do you want to get rid of any?': { - type: 'boolean', - }, - }, - required: ['Do you want to get rid of any?'], - }, - ], - }, - }, - }, - }, - }; - const expectedUiSchema = { - conditional: { - 'Do you have any pets?': { - 'ui:widget': 'radio', - }, - 'Do you want to get rid of any?': {}, - 'How old is your pet?': {}, - }, - }; - - expect(transformSchemaToProps(inputSchema)).toEqual({ - schema: expectedSchema, - uiSchema: expectedUiSchema, - }); - }); - - it('transforms schema with array items', () => { - const inputSchema = { - type: 'object', - properties: { - person: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - address: { - type: 'string', - 'ui:widget': 'textarea', - }, - }, - }, - }, - accountNumber: { - type: 'number', - }, - }, - }; - const expectedSchema = { - type: 'object', - properties: { - person: { - type: 'array', - items: { - type: 'object', - properties: { - name: { - type: 'string', - }, - address: { - type: 'string', - }, - }, - }, - }, - accountNumber: { - type: 'number', - }, - }, - }; - const expectedUiSchema = { - accountNumber: {}, - person: { - items: { - name: {}, - address: { - 'ui:widget': 'textarea', - }, - }, - }, - }; - - expect(transformSchemaToProps(inputSchema)).toEqual({ - schema: expectedSchema, - uiSchema: expectedUiSchema, - }); - }); -}); diff --git a/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.ts b/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.ts deleted file mode 100644 index e6221cb8d5..0000000000 --- a/plugins/scaffolder/src/legacy/MultistepJsonForm/schema.ts +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { JsonObject } from '@backstage/types'; -import { UiSchema } from '@rjsf/utils'; -import type { LayoutOptions } from '@backstage/plugin-scaffolder-react'; -import { FormProps } from '@rjsf/core'; - -function isObject(value: unknown): value is JsonObject { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -function extractUiSchema(schema: JsonObject, uiSchema: JsonObject) { - if (!isObject(schema)) { - return; - } - - const { properties, items, anyOf, oneOf, allOf, dependencies } = schema; - - for (const propName in schema) { - if (!schema.hasOwnProperty(propName)) { - continue; - } - - if (propName.startsWith('ui:')) { - uiSchema[propName] = schema[propName]; - delete schema[propName]; - } - } - - if (isObject(properties)) { - for (const propName in properties) { - if (!properties.hasOwnProperty(propName)) { - continue; - } - - const schemaNode = properties[propName]; - if (!isObject(schemaNode)) { - continue; - } - const innerUiSchema = {}; - uiSchema[propName] = uiSchema[propName] || innerUiSchema; - extractUiSchema(schemaNode, innerUiSchema); - } - } - - if (isObject(items)) { - const innerUiSchema = {}; - uiSchema.items = innerUiSchema; - extractUiSchema(items, innerUiSchema); - } - - if (Array.isArray(anyOf)) { - for (const schemaNode of anyOf) { - if (!isObject(schemaNode)) { - continue; - } - extractUiSchema(schemaNode, uiSchema); - } - } - - if (Array.isArray(oneOf)) { - for (const schemaNode of oneOf) { - if (!isObject(schemaNode)) { - continue; - } - extractUiSchema(schemaNode, uiSchema); - } - } - - if (Array.isArray(allOf)) { - for (const schemaNode of allOf) { - if (!isObject(schemaNode)) { - continue; - } - extractUiSchema(schemaNode, uiSchema); - } - } - - if (isObject(dependencies)) { - for (const depName of Object.keys(dependencies)) { - const schemaNode = dependencies[depName]; - if (!isObject(schemaNode)) { - continue; - } - extractUiSchema(schemaNode, uiSchema); - } - } -} - -export function transformSchemaToProps( - inputSchema: JsonObject, - layouts: LayoutOptions[] = [], -): { - schema: FormProps['schema']; - uiSchema: FormProps['uiSchema']; -} { - const customLayoutName = inputSchema['ui:ObjectFieldTemplate']; - inputSchema.type = inputSchema.type || 'object'; - const schema = JSON.parse(JSON.stringify(inputSchema)); - delete schema.title; // Rendered separately - const uiSchema: UiSchema = {}; - extractUiSchema(schema, uiSchema); - - if (customLayoutName) { - const Layout = layouts.find( - layout => layout.name === customLayoutName, - )?.component; - - if (Layout) { - uiSchema['ui:ObjectFieldTemplate'] = Layout; - } - } - - return { schema, uiSchema }; -} diff --git a/plugins/scaffolder/src/legacy/Router.tsx b/plugins/scaffolder/src/legacy/Router.tsx deleted file mode 100644 index ac183b9470..0000000000 --- a/plugins/scaffolder/src/legacy/Router.tsx +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { ComponentType, useEffect, PropsWithChildren } from 'react'; -import { Navigate, Route, Routes, useOutlet } from 'react-router-dom'; -import { Entity } from '@backstage/catalog-model'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { ScaffolderPage } from './ScaffolderPage'; -import { TemplatePage } from './TemplatePage'; -import { TaskPage } from './TaskPage'; -import { ActionsPage } from '../components/ActionsPage'; -import { DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS } from '../extensions/default'; -import { useRouteRef, useRouteRefParams } from '@backstage/core-plugin-api'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; -import { - ReviewStepProps, - SecretsContextProvider, - useCustomFieldExtensions, - useCustomLayouts, -} from '@backstage/plugin-scaffolder-react'; -import { ListTasksPage } from '../components/ListTasksPage'; -import { - actionsRouteRef, - editRouteRef, - legacySelectedTemplateRouteRef, - scaffolderListTaskRouteRef, - scaffolderTaskRouteRef, - selectedTemplateRouteRef, -} from '../routes'; -import { TemplateEditorPage } from './TemplateEditorPage'; - -/** - * The props for the entrypoint `ScaffolderPage` component the plugin. - * @alpha - */ -export type LegacyRouterProps = { - components?: { - ReviewStepComponent?: ComponentType; - TemplateCardComponent?: - | ComponentType<{ template: TemplateEntityV1beta3 }> - | undefined; - TaskPageComponent?: ComponentType>; - }; - groups?: Array<{ - title?: React.ReactNode; - filter: (entity: Entity) => boolean; - }>; - templateFilter?: (entity: TemplateEntityV1beta3) => boolean; - defaultPreviewTemplate?: string; - headerOptions?: { - pageTitleOverride?: string; - title?: string; - subtitle?: string; - }; - /** - * Options for the context menu on the scaffolder page. - */ - contextMenu?: { - /** Whether to show a link to the template editor */ - editor?: boolean; - /** Whether to show a link to the actions documentation */ - actions?: boolean; - }; -}; - -/** - * The legacy router - * - * @alpha - */ -export const LegacyRouter = (props: LegacyRouterProps) => { - const { - groups, - templateFilter, - components = {}, - defaultPreviewTemplate, - } = props; - - const { ReviewStepComponent, TemplateCardComponent, TaskPageComponent } = - components; - - const outlet = useOutlet(); - const TaskPageElement = TaskPageComponent ?? TaskPage; - - const customFieldExtensions = - useCustomFieldExtensions(outlet); - - const fieldExtensions = [ - ...customFieldExtensions, - ...DEFAULT_SCAFFOLDER_FIELD_EXTENSIONS.filter( - ({ name }) => - !customFieldExtensions.some( - customFieldExtension => customFieldExtension.name === name, - ), - ), - ] as LegacyFieldExtensionOptions[]; - - const customLayouts = useCustomLayouts(outlet); - - /** - * This component can be deleted once the older routes have been deprecated. - */ - const RedirectingComponent = () => { - const { templateName } = useRouteRefParams(legacySelectedTemplateRouteRef); - const newLink = useRouteRef(selectedTemplateRouteRef); - useEffect( - () => - // eslint-disable-next-line no-console - console.warn( - 'The route /template/:templateName is deprecated, please use the new /template/:namespace/:templateName route instead', - ), - [], - ); - return ; - }; - - return ( - - - } - /> - } - /> - - - - } - /> - } - /> - } /> - } /> - - - - } - /> - - } /> - - ); -}; diff --git a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPage.tsx b/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPage.tsx deleted file mode 100644 index 54122eddf0..0000000000 --- a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPage.tsx +++ /dev/null @@ -1,159 +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 { - Content, - ContentHeader, - CreateButton, - Header, - Page, - SupportButton, -} from '@backstage/core-components'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { useRouteRef } from '@backstage/core-plugin-api'; -import { - CatalogFilterLayout, - EntityKindPicker, - EntityListProvider, - EntitySearchBar, - EntityTagPicker, - UserListPicker, -} from '@backstage/plugin-catalog-react'; -import React, { ComponentType } from 'react'; -import { TemplateList } from '../TemplateList'; -import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; -import { usePermission } from '@backstage/plugin-permission-react'; -import { ScaffolderPageContextMenu } from './ScaffolderPageContextMenu'; -import { registerComponentRouteRef } from '../../routes'; -import { TemplateTypePicker } from '../../components'; - -export type ScaffolderPageProps = { - TemplateCardComponent?: - | ComponentType<{ template: TemplateEntityV1beta3 }> - | undefined; - groups?: Array<{ - title?: React.ReactNode; - filter: (entity: TemplateEntityV1beta3) => boolean; - }>; - templateFilter?: (entity: TemplateEntityV1beta3) => boolean; - contextMenu?: { - editor?: boolean; - actions?: boolean; - tasks?: boolean; - }; - headerOptions?: { - pageTitleOverride?: string; - title?: string; - subtitle?: string; - }; -}; - -export const ScaffolderPageContents = ({ - TemplateCardComponent, - groups, - templateFilter, - contextMenu, - headerOptions, -}: ScaffolderPageProps) => { - const registerComponentLink = useRouteRef(registerComponentRouteRef); - const otherTemplatesGroup = { - title: groups ? 'Other Templates' : 'Templates', - filter: (entity: TemplateEntityV1beta3) => { - const filtered = (groups ?? []).map(group => group.filter(entity)); - return !filtered.some(result => result === true); - }, - }; - - const { allowed } = usePermission({ - permission: catalogEntityCreatePermission, - }); - - return ( - -
- -
- - - {allowed && ( - - )} - - Create new software components using standard templates. Different - templates create different kinds of components (services, websites, - documentation, ...). - - - - - - - - - {groups && - groups.map((group, index) => ( - - ))} - - - - -
- ); -}; - -export const ScaffolderPage = ({ - TemplateCardComponent, - groups, - templateFilter, - contextMenu, - headerOptions, -}: ScaffolderPageProps) => ( - - - -); diff --git a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.test.tsx b/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.test.tsx deleted file mode 100644 index 66513591d0..0000000000 --- a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.test.tsx +++ /dev/null @@ -1,83 +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 userEvent from '@testing-library/user-event'; -import { renderInTestApp } from '@backstage/test-utils'; -import { screen } from '@testing-library/react'; -import React from 'react'; -import { ScaffolderPageContextMenu } from './ScaffolderPageContextMenu'; -import { rootRouteRef } from '../../routes'; - -describe('ScaffolderPageContextMenu', () => { - it('does not render anything if fully disabled', async () => { - await renderInTestApp( -
- -
, - { mountedRoutes: { '/': rootRouteRef } }, - ); - - expect(screen.getByTestId('container')).toBeEmptyDOMElement(); - }); - - it('renders the editor option', async () => { - await renderInTestApp( -
- -
, - { - mountedRoutes: { '/': rootRouteRef }, - }, - ); - - await userEvent.click(screen.getByTestId('container').firstElementChild!); - - expect(screen.getByText('Template Editor')).toBeInTheDocument(); - expect(screen.queryByText('Installed Actions')).not.toBeInTheDocument(); - }); - - it('renders the actions option', async () => { - await renderInTestApp( -
- -
, - { - mountedRoutes: { '/': rootRouteRef }, - }, - ); - - await userEvent.click(screen.getByTestId('container').firstElementChild!); - - expect(screen.queryByText('Template Editor')).not.toBeInTheDocument(); - expect(screen.getByText('Installed Actions')).toBeInTheDocument(); - }); - - it('renders all options', async () => { - await renderInTestApp( -
- -
, - { - mountedRoutes: { '/': rootRouteRef }, - }, - ); - - await userEvent.click(screen.getByTestId('container').firstElementChild!); - - expect(screen.getByText('Template Editor')).toBeInTheDocument(); - expect(screen.getByText('Installed Actions')).toBeInTheDocument(); - }); -}); diff --git a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.tsx b/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.tsx deleted file mode 100644 index f6151c227b..0000000000 --- a/plugins/scaffolder/src/legacy/ScaffolderPage/ScaffolderPageContextMenu.tsx +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2020 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { useRouteRef } from '@backstage/core-plugin-api'; -import IconButton from '@material-ui/core/IconButton'; -import ListItemIcon from '@material-ui/core/ListItemIcon'; -import ListItemText from '@material-ui/core/ListItemText'; -import MenuItem from '@material-ui/core/MenuItem'; -import MenuList from '@material-ui/core/MenuList'; -import Popover from '@material-ui/core/Popover'; -import { makeStyles } from '@material-ui/core/styles'; -import Description from '@material-ui/icons/Description'; -import Edit from '@material-ui/icons/Edit'; -import List from '@material-ui/icons/List'; -import MoreVert from '@material-ui/icons/MoreVert'; -import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { - actionsRouteRef, - editRouteRef, - scaffolderListTaskRouteRef, -} from '../../routes'; - -const useStyles = makeStyles(theme => ({ - button: { - color: theme.page.fontColor, - }, -})); - -export type ScaffolderPageContextMenuProps = { - editor?: boolean; - actions?: boolean; - tasks?: boolean; -}; - -export function ScaffolderPageContextMenu( - props: ScaffolderPageContextMenuProps, -) { - const classes = useStyles(); - const [anchorEl, setAnchorEl] = useState(); - const editLink = useRouteRef(editRouteRef); - const actionsLink = useRouteRef(actionsRouteRef); - const tasksLink = useRouteRef(scaffolderListTaskRouteRef); - - const navigate = useNavigate(); - - const showEditor = props.editor !== false; - const showActions = props.actions !== false; - const showTasks = props.tasks !== false; - - if (!showEditor && !showActions) { - return null; - } - - const onOpen = (event: React.SyntheticEvent) => { - setAnchorEl(event.currentTarget); - }; - - const onClose = () => { - setAnchorEl(undefined); - }; - - return ( - <> - - - - - - {showEditor && ( - navigate(editLink())}> - - - - - - )} - {showActions && ( - navigate(actionsLink())}> - - - - - - )} - {showTasks && ( - navigate(tasksLink())}> - - - - - - )} - - - - ); -} diff --git a/plugins/scaffolder/src/legacy/ScaffolderPage/index.ts b/plugins/scaffolder/src/legacy/ScaffolderPage/index.ts deleted file mode 100644 index 433e39723f..0000000000 --- a/plugins/scaffolder/src/legacy/ScaffolderPage/index.ts +++ /dev/null @@ -1,16 +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. - */ -export { ScaffolderPage } from './ScaffolderPage'; diff --git a/plugins/scaffolder/src/legacy/TaskPage/IconLink.test.tsx b/plugins/scaffolder/src/legacy/TaskPage/IconLink.test.tsx deleted file mode 100644 index 0b6092c37d..0000000000 --- a/plugins/scaffolder/src/legacy/TaskPage/IconLink.test.tsx +++ /dev/null @@ -1,30 +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 { renderInTestApp } from '@backstage/test-utils'; -import CloudIcon from '@material-ui/icons/Cloud'; -import React from 'react'; -import { IconLink } from './IconLink'; - -describe('IconLink', () => { - it('should render an icon link', async () => { - const rendered = await renderInTestApp( - , - ); - - expect(rendered.getByText('I am Link')).toBeInTheDocument(); - }); -}); diff --git a/plugins/scaffolder/src/legacy/TaskPage/TaskErrors.tsx b/plugins/scaffolder/src/legacy/TaskPage/TaskErrors.tsx deleted file mode 100644 index 4f8ce974c8..0000000000 --- a/plugins/scaffolder/src/legacy/TaskPage/TaskErrors.tsx +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Box } from '@material-ui/core'; -import React, { useEffect, useRef } from 'react'; -import { DismissableBanner } from '@backstage/core-components'; - -type TaskErrorsProps = { - error?: Error; -}; - -export const TaskErrors = ({ error }: TaskErrorsProps) => { - const id = useRef(''); - - useEffect(() => { - id.current = String(Math.random()); - }, [error]); - return error ? ( - - - - ) : null; -}; diff --git a/plugins/scaffolder/src/legacy/TaskPage/TaskPage.tsx b/plugins/scaffolder/src/legacy/TaskPage/TaskPage.tsx deleted file mode 100644 index affb9a31b7..0000000000 --- a/plugins/scaffolder/src/legacy/TaskPage/TaskPage.tsx +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { parseEntityRef } from '@backstage/catalog-model'; -import { - Content, - ErrorPage, - Header, - LogViewer, - Page, - Progress, -} from '@backstage/core-components'; -import { - useApi, - useRouteRef, - useRouteRefParams, -} from '@backstage/core-plugin-api'; -import { - Button, - CircularProgress, - Paper, - StepButton, - StepIconProps, -} from '@material-ui/core'; -import Grid from '@material-ui/core/Grid'; -import Step from '@material-ui/core/Step'; -import StepLabel from '@material-ui/core/StepLabel'; -import Stepper from '@material-ui/core/Stepper'; -import { createStyles, makeStyles, Theme } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import Cancel from '@material-ui/icons/Cancel'; -import Check from '@material-ui/icons/Check'; -import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; -import classNames from 'classnames'; -import { DateTime, Interval } from 'luxon'; -import qs from 'qs'; -import React, { memo, useEffect, useMemo, useState } from 'react'; -import { useNavigate } from 'react-router-dom'; -import useInterval from 'react-use/lib/useInterval'; -import { - ScaffolderTaskStatus, - ScaffolderTaskOutput, - useTaskEventStream, -} from '@backstage/plugin-scaffolder-react'; -import { TaskErrors } from './TaskErrors'; -import { TaskPageLinks } from './TaskPageLinks'; -import { - rootRouteRef, - scaffolderTaskRouteRef, - selectedTemplateRouteRef, -} from '../../routes'; -import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; -import humanizeDuration from 'humanize-duration'; - -const useStyles = makeStyles((theme: Theme) => - createStyles({ - root: { - width: '100%', - }, - button: { - marginBottom: theme.spacing(2), - marginLeft: theme.spacing(2), - }, - actionsContainer: { - marginBottom: theme.spacing(2), - }, - resetContainer: { - padding: theme.spacing(3), - }, - labelWrapper: { - display: 'flex', - flex: 1, - flexDirection: 'row', - justifyContent: 'space-between', - }, - stepWrapper: { - width: '100%', - }, - }), -); - -type TaskStep = { - id: string; - name: string; - status: ScaffolderTaskStatus; - startedAt?: string; - endedAt?: string; -}; - -const StepTimeTicker = ({ step }: { step: TaskStep }) => { - const [time, setTime] = useState(''); - - useInterval(() => { - if (!step.startedAt) { - setTime(''); - return; - } - - const end = step.endedAt - ? DateTime.fromISO(step.endedAt) - : DateTime.local(); - - const startedAt = DateTime.fromISO(step.startedAt); - const formatted = Interval.fromDateTimes(startedAt, end) - .toDuration() - .valueOf(); - - setTime(humanizeDuration(formatted, { round: true })); - }, 1000); - - return {time}; -}; - -const useStepIconStyles = makeStyles(theme => - createStyles({ - root: { - color: theme.palette.text.disabled, - display: 'flex', - height: 22, - alignItems: 'center', - }, - completed: { - color: theme.palette.status.ok, - }, - error: { - color: theme.palette.status.error, - }, - }), -); - -function TaskStepIconComponent(props: StepIconProps) { - const classes = useStepIconStyles(); - const { active, completed, error } = props; - - const getMiddle = () => { - if (active) { - return ; - } - if (completed) { - return ; - } - if (error) { - return ; - } - return ; - }; - - return ( -
- {getMiddle()} -
- ); -} - -export const TaskStatusStepper = memo( - (props: { - steps: TaskStep[]; - currentStepId: string | undefined; - onUserStepChange: (id: string) => void; - classes?: { - root?: string; - }; - }) => { - const { steps, currentStepId, onUserStepChange } = props; - const classes = useStyles(props); - - return ( -
- s.id === currentStepId)} - orientation="vertical" - nonLinear - > - {steps.map((step, index) => { - const isCancelled = step.status === 'cancelled'; - const isActive = step.status === 'processing'; - const isCompleted = step.status === 'completed'; - const isFailed = step.status === 'failed'; - const isSkipped = step.status === 'skipped'; - - return ( - - onUserStepChange(step.id)}> - -
- {step.name} - {isSkipped ? ( - Skipped - ) : ( - - )} -
-
-
-
- ); - })} -
-
- ); - }, -); - -const hasLinks = ({ links = [] }: ScaffolderTaskOutput): boolean => - links.length > 0; - -/** - * TaskPageProps for constructing a TaskPage - * @param loadingText - Optional loading text shown before a task begins executing. - * @public - * @deprecated - this is a useless type that is no longer used. - */ -export type TaskPageProps = { - loadingText?: string; -}; - -/** - * TaskPage for showing the status of the taskId provided as a param - * - * @alpha - */ -export const TaskPage = (props: TaskPageProps) => { - const { loadingText } = props; - const classes = useStyles(); - const navigate = useNavigate(); - const rootPath = useRouteRef(rootRouteRef); - const scaffolderApi = useApi(scaffolderApiRef); - const templateRoute = useRouteRef(selectedTemplateRouteRef); - const [userSelectedStepId, setUserSelectedStepId] = useState< - string | undefined - >(undefined); - const [clickedToCancel, setClickedToCancel] = useState(false); - const [lastActiveStepId, setLastActiveStepId] = useState( - undefined, - ); - const { taskId } = useRouteRefParams(scaffolderTaskRouteRef); - const taskStream = useTaskEventStream(taskId); - const completed = taskStream.completed; - const taskCancelled = taskStream.cancelled; - const steps = useMemo( - () => - taskStream.task?.spec.steps.map(step => ({ - ...step, - ...taskStream?.steps?.[step.id], - })) ?? [], - [taskStream], - ); - - useEffect(() => { - const mostRecentFailedOrActiveStep = steps.find(step => - ['failed', 'processing'].includes(step.status), - ); - if (completed && !mostRecentFailedOrActiveStep) { - setLastActiveStepId(steps[steps.length - 1]?.id); - return; - } - - setLastActiveStepId(mostRecentFailedOrActiveStep?.id); - }, [steps, completed]); - - const currentStepId = userSelectedStepId ?? lastActiveStepId; - - const logAsString = useMemo(() => { - if (!currentStepId) { - return loadingText ? loadingText : 'Loading...'; - } - const log = taskStream.stepLogs[currentStepId]; - - if (!log?.length) { - return 'Waiting for logs...'; - } - return log.join('\n'); - }, [taskStream.stepLogs, currentStepId, loadingText]); - - const taskNotFound = - taskStream.completed && !taskStream.loading && !taskStream.task; - - const { output } = taskStream; - - const handleStartOver = () => { - if (!taskStream.task || !taskStream.task?.spec.templateInfo?.entityRef) { - navigate(rootPath()); - return; - } - - const formData = taskStream.task!.spec.parameters; - - const { name, namespace } = parseEntityRef( - taskStream.task!.spec.templateInfo?.entityRef, - ); - - navigate( - `${templateRoute({ templateName: name, namespace })}?${qs.stringify({ - formData: JSON.stringify(formData), - })}`, - ); - }; - - const handleCancel = async () => { - setClickedToCancel(true); - await scaffolderApi.cancelTask(taskId); - }; - - return ( - -
- - {taskNotFound ? ( - - ) : ( -
- - - - - {output && hasLinks(output) && ( - - )} - - - - - - {!currentStepId && } - -
- - -
-
-
-
- )} -
- - ); -}; diff --git a/plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.test.tsx b/plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.test.tsx deleted file mode 100644 index 2f5d015f8d..0000000000 --- a/plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.test.tsx +++ /dev/null @@ -1,67 +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 { entityRouteRef } from '@backstage/plugin-catalog-react'; -import { renderInTestApp } from '@backstage/test-utils'; -import React from 'react'; -import { TaskPageLinks } from './TaskPageLinks'; - -describe('TaskPageLinks', () => { - beforeEach(() => {}); - - afterEach(() => { - jest.resetAllMocks(); - }); - - it('renders further links', async () => { - const output = { - links: [ - { url: 'https://first.url', title: 'Cool link 1' }, - { url: 'https://second.url', title: 'Cool link 2' }, - { entityRef: 'Component:default/my-app', title: 'Open in catalog' }, - { title: 'Skipped' }, - ], - }; - const { findByText, queryByText } = await renderInTestApp( - , - { - mountedRoutes: { - '/catalog/:namespace/:kind/:name/*': entityRouteRef, - }, - }, - ); - - let element = await findByText('Cool link 1'); - - expect(element).toBeInTheDocument(); - expect(element).toHaveAttribute('href', 'https://first.url'); - - element = await findByText('Cool link 2'); - - expect(element).toBeInTheDocument(); - expect(element).toHaveAttribute('href', 'https://second.url'); - - element = await findByText('Open in catalog'); - - expect(element).toBeInTheDocument(); - expect(element).toHaveAttribute( - 'href', - '/catalog/default/Component/my-app', - ); - - expect(queryByText('Skipped')).not.toBeInTheDocument(); - }); -}); diff --git a/plugins/scaffolder/src/legacy/TaskPage/index.ts b/plugins/scaffolder/src/legacy/TaskPage/index.ts deleted file mode 100644 index f7876888f9..0000000000 --- a/plugins/scaffolder/src/legacy/TaskPage/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { TaskPage } from './TaskPage'; -export type { TaskPageProps } from './TaskPage'; diff --git a/plugins/scaffolder/src/legacy/TemplateCard/TemplateCard.tsx b/plugins/scaffolder/src/legacy/TemplateCard/TemplateCard.tsx deleted file mode 100644 index 80f719bd36..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateCard/TemplateCard.tsx +++ /dev/null @@ -1,316 +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 { - DEFAULT_NAMESPACE, - Entity, - EntityLink, - parseEntityRef, - RELATION_OWNED_BY, - stringifyEntityRef, -} from '@backstage/catalog-model'; -import { - ItemCardHeader, - Link, - LinkButton, - MarkdownContent, -} from '@backstage/core-components'; -import { - IconComponent, - useApi, - useApp, - useRouteRef, -} from '@backstage/core-plugin-api'; -import { - ScmIntegrationIcon, - scmIntegrationsApiRef, -} from '@backstage/integration-react'; -import { - EntityRefLinks, - FavoriteEntity, - getEntityRelations, - getEntitySourceLocation, -} from '@backstage/plugin-catalog-react'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; -import { - Box, - Card, - CardActions, - CardContent, - CardMedia, - Chip, - IconButton, - makeStyles, - Theme, - Tooltip, - Typography, - useTheme, -} from '@material-ui/core'; -import LanguageIcon from '@material-ui/icons/Language'; -import WarningIcon from '@material-ui/icons/Warning'; -import React from 'react'; -import { selectedTemplateRouteRef, viewTechDocRouteRef } from '../../routes'; - -const useStyles = makeStyles< - Theme, - { fontColor: string; backgroundImage: string } ->(theme => ({ - cardHeader: { - position: 'relative', - }, - title: { - backgroundImage: ({ backgroundImage }) => backgroundImage, - color: ({ fontColor }) => fontColor, - }, - box: { - overflow: 'hidden', - textOverflow: 'ellipsis', - display: '-webkit-box', - '-webkit-line-clamp': 10, - '-webkit-box-orient': 'vertical', - }, - label: { - color: theme.palette.text.secondary, - textTransform: 'uppercase', - fontSize: '0.65rem', - fontWeight: 'bold', - letterSpacing: 0.5, - lineHeight: 1, - paddingBottom: '0.2rem', - }, - linksLabel: { - padding: '0 16px', - }, - description: { - '& p': { - margin: '0px', - }, - }, - leftButton: { - marginRight: 'auto', - }, - starButton: { - position: 'absolute', - top: theme.spacing(0.5), - right: theme.spacing(0.5), - padding: '0.25rem', - color: ({ fontColor }) => fontColor, - }, -})); - -const MuiIcon = ({ icon: Icon }: { icon: IconComponent }) => ; - -const useDeprecationStyles = makeStyles(theme => ({ - deprecationIcon: { - position: 'absolute', - top: theme.spacing(0.5), - right: theme.spacing(3.5), - padding: '0.25rem', - }, - link: { - color: theme.palette.warning.light, - }, -})); - -export type TemplateCardProps = { - template: TemplateEntityV1beta3; - deprecated?: boolean; -}; - -type TemplateProps = { - description: string; - tags: string[]; - title: string; - type: string; - name: string; - links: EntityLink[]; -}; - -const getTemplateCardProps = ( - template: TemplateEntityV1beta3, -): TemplateProps & { key: string } => { - return { - key: template.metadata.uid!, - name: template.metadata.name, - title: `${(template.metadata.title || template.metadata.name) ?? ''}`, - type: template.spec.type ?? '', - description: template.metadata.description ?? '-', - tags: (template.metadata?.tags as string[]) ?? [], - links: template.metadata.links ?? [], - }; -}; - -const DeprecationWarning = () => { - const styles = useDeprecationStyles(); - - const Title = ( - - This template uses a syntax that has been deprecated, and should be - migrated to a newer syntax. Click for more info. - - ); - - return ( -
- - - - - -
- ); -}; - -export const TemplateCard = ({ template, deprecated }: TemplateCardProps) => { - const app = useApp(); - const backstageTheme = useTheme(); - const templateRoute = useRouteRef(selectedTemplateRouteRef); - const templateProps = getTemplateCardProps(template); - const ownedByRelations = getEntityRelations( - template as Entity, - RELATION_OWNED_BY, - ); - const themeId = backstageTheme.getPageTheme({ themeId: templateProps.type }) - ? templateProps.type - : 'other'; - const theme = backstageTheme.getPageTheme({ themeId }); - const classes = useStyles({ - fontColor: theme.fontColor, - backgroundImage: theme.backgroundImage, - }); - const { name, namespace } = parseEntityRef(stringifyEntityRef(template)); - const href = templateRoute({ templateName: name, namespace }); - - // TechDocs Link - const viewTechDoc = useRouteRef(viewTechDocRouteRef); - const viewTechDocsAnnotation = - template.metadata.annotations?.['backstage.io/techdocs-ref']; - const viewTechDocsLink = - !!viewTechDocsAnnotation && - !!viewTechDoc && - viewTechDoc({ - namespace: template.metadata.namespace || DEFAULT_NAMESPACE, - kind: template.kind, - name: template.metadata.name, - }); - - const iconResolver = (key?: string): IconComponent => - key ? app.getSystemIcon(key) ?? LanguageIcon : LanguageIcon; - - const scmIntegrationsApi = useApi(scmIntegrationsApiRef); - const sourceLocation = getEntitySourceLocation(template, scmIntegrationsApi); - - return ( - - - - {deprecated && } - - - - - - Description - - - - - - Owner - - - - - - Tags - - {templateProps.tags?.map(tag => ( - - ))} - - - - Links - - -
- {sourceLocation && ( - - - - - - )} - {viewTechDocsLink && ( - - - - - - )} - {templateProps.links?.map((link, i) => ( - - - - - - ))} -
- - Choose - -
-
- ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplateCard/index.ts b/plugins/scaffolder/src/legacy/TemplateCard/index.ts deleted file mode 100644 index 291a06ed69..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateCard/index.ts +++ /dev/null @@ -1,17 +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. - */ -export { TemplateCard } from './TemplateCard'; -export type { TemplateCardProps } from './TemplateCard'; diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/CustomFieldExplorer.tsx b/plugins/scaffolder/src/legacy/TemplateEditorPage/CustomFieldExplorer.tsx deleted file mode 100644 index def3bf1796..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/CustomFieldExplorer.tsx +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { StreamLanguage } from '@codemirror/language'; -import { yaml as yamlSupport } from '@codemirror/legacy-modes/mode/yaml'; -import { - Button, - Card, - CardContent, - CardHeader, - FormControl, - IconButton, - InputLabel, - makeStyles, - MenuItem, - Select, -} from '@material-ui/core'; -import CloseIcon from '@material-ui/icons/Close'; -import CodeMirror from '@uiw/react-codemirror'; -import React, { useCallback, useMemo, useState } from 'react'; -import yaml from 'yaml'; -import { Form } from '@backstage/plugin-scaffolder-react/alpha'; -import { TemplateEditorForm } from './TemplateEditorForm'; -import validator from '@rjsf/validator-ajv8'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; - -const useStyles = makeStyles(theme => ({ - root: { - gridArea: 'pageContent', - display: 'grid', - gridTemplateAreas: ` - "controls controls" - "fieldForm preview" - `, - gridTemplateRows: 'auto 1fr', - gridTemplateColumns: '1fr 1fr', - }, - controls: { - gridArea: 'controls', - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - margin: theme.spacing(1), - }, - fieldForm: { - gridArea: 'fieldForm', - }, - preview: { - gridArea: 'preview', - }, -})); - -export const CustomFieldExplorer = ({ - customFieldExtensions = [], - onClose, -}: { - customFieldExtensions?: LegacyFieldExtensionOptions[]; - onClose?: () => void; -}) => { - const classes = useStyles(); - const fieldOptions = customFieldExtensions.filter(field => !!field.schema); - const [selectedField, setSelectedField] = useState(fieldOptions[0]); - const [fieldFormState, setFieldFormState] = useState({}); - const [refreshKey, setRefreshKey] = useState(Date.now()); - const [formState, setFormState] = useState({}); - const sampleFieldTemplate = useMemo( - () => - yaml.stringify({ - parameters: [ - { - title: `${selectedField.name} Example`, - properties: { - [selectedField.name]: { - type: selectedField.schema?.returnValue?.type, - 'ui:field': selectedField.name, - 'ui:options': fieldFormState, - }, - }, - }, - ], - }), - [fieldFormState, selectedField], - ); - - const fieldComponents = useMemo(() => { - return Object.fromEntries( - customFieldExtensions.map(({ name, component }) => [name, component]), - ); - }, [customFieldExtensions]); - - const handleSelectionChange = useCallback( - (selection: LegacyFieldExtensionOptions) => { - setSelectedField(selection); - setFieldFormState({}); - setFormState({}); - }, - [setFieldFormState, setSelectedField, setFormState], - ); - - const handleFieldConfigChange = useCallback( - (state: {}) => { - setFieldFormState(state); - // Force TemplateEditorForm to re-render since some fields - // may not be responsive to ui:option changes - setRefreshKey(Date.now()); - }, - [setFieldFormState, setRefreshKey], - ); - - return ( -
-
- - - Choose Custom Field Extension - - - - - - - -
-
- - - -
handleFieldConfigChange(e.formData)} - validator={validator} - schema={selectedField.schema?.uiOptions || {}} - experimental_defaultFormStateBehavior={{ - allOf: 'populateDefaults', - }} - > - -
-
-
-
-
- - - - - - - null} - /> -
-
- ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditor.tsx b/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditor.tsx deleted file mode 100644 index f6e7b38d7d..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditor.tsx +++ /dev/null @@ -1,92 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { makeStyles } from '@material-ui/core'; -import React, { useState } from 'react'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; -import { TemplateDirectoryAccess } from '../../lib/filesystem'; -import { LayoutOptions } from '@backstage/plugin-scaffolder-react'; -import { DirectoryEditorProvider } from '../../next/TemplateEditorPage/DirectoryEditorContext'; -import { DryRunProvider } from '../../next/TemplateEditorPage/DryRunContext'; -import { TemplateEditorBrowser } from '../../next/TemplateEditorPage/TemplateEditorBrowser'; -import { TemplateEditorTextArea } from '../../next/TemplateEditorPage/TemplateEditorTextArea'; -import { TemplateEditorForm } from './TemplateEditorForm'; -import { DryRunResults } from '../../next/TemplateEditorPage/DryRunResults'; - -const useStyles = makeStyles({ - // Reset and fix sizing to make sure scrolling behaves correctly - root: { - gridArea: 'pageContent', - - display: 'grid', - gridTemplateAreas: ` - "browser editor preview" - "results results results" - `, - gridTemplateColumns: '1fr 3fr 2fr', - gridTemplateRows: '1fr auto', - }, - browser: { - gridArea: 'browser', - overflow: 'auto', - }, - editor: { - gridArea: 'editor', - overflow: 'auto', - }, - preview: { - gridArea: 'preview', - overflow: 'auto', - }, - results: { - gridArea: 'results', - }, -}); - -export const TemplateEditor = (props: { - directory: TemplateDirectoryAccess; - fieldExtensions?: LegacyFieldExtensionOptions[]; - layouts?: LayoutOptions[]; - onClose?: () => void; -}) => { - const classes = useStyles(); - - const [errorText, setErrorText] = useState(); - - return ( - - -
-
- -
-
- -
-
- -
-
- -
-
-
-
- ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorForm.tsx b/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorForm.tsx deleted file mode 100644 index df05efc921..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorForm.tsx +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { useApiHolder } from '@backstage/core-plugin-api'; -import { JsonObject, JsonValue } from '@backstage/types'; -import { makeStyles } from '@material-ui/core/styles'; -import React, { Component, ReactNode, useMemo, useState } from 'react'; -import useDebounce from 'react-use/lib/useDebounce'; -import yaml from 'yaml'; -import { - LayoutOptions, - TemplateParameterSchema, -} from '@backstage/plugin-scaffolder-react'; - -import { useDryRun } from '../../next/TemplateEditorPage/DryRunContext'; -import { useDirectoryEditor } from '../../next/TemplateEditorPage/DirectoryEditorContext'; -import { MultistepJsonForm } from '../MultistepJsonForm'; -import { createValidator } from '../TemplatePage'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; - -const useStyles = makeStyles({ - containerWrapper: { - position: 'relative', - width: '100%', - height: '100%', - }, - container: { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - overflow: 'auto', - }, -}); - -interface ErrorBoundaryProps { - invalidator: unknown; - setErrorText(errorText: string | undefined): void; - children: ReactNode; -} - -interface ErrorBoundaryState { - shouldRender: boolean; -} - -class ErrorBoundary extends Component { - state = { - shouldRender: true, - }; - - componentDidUpdate(prevProps: { invalidator: unknown }) { - if (prevProps.invalidator !== this.props.invalidator) { - this.setState({ shouldRender: true }); - } - } - - componentDidCatch(error: Error) { - this.props.setErrorText(error.message); - this.setState({ shouldRender: false }); - } - - render() { - return this.state.shouldRender ? this.props.children : null; - } -} - -interface TemplateEditorFormProps { - content?: string; - /** Setting this to true will cause the content to be parsed as if it is the template entity spec */ - contentIsSpec?: boolean; - data: JsonObject; - onUpdate: (data: JsonObject) => void; - setErrorText: (errorText?: string) => void; - - onDryRun?: (data: JsonObject) => Promise; - fieldExtensions?: LegacyFieldExtensionOptions[]; - layouts?: LayoutOptions[]; -} - -function isJsonObject(value: JsonValue | undefined): value is JsonObject { - return typeof value === 'object' && value !== null && !Array.isArray(value); -} - -/** Shows the a template form that is parsed from the provided content */ -export function TemplateEditorForm(props: TemplateEditorFormProps) { - const { - content, - contentIsSpec, - data, - onUpdate, - onDryRun, - setErrorText, - fieldExtensions = [], - layouts = [], - } = props; - const classes = useStyles(); - const apiHolder = useApiHolder(); - - const [steps, setSteps] = useState(); - - const fields = useMemo(() => { - return Object.fromEntries( - fieldExtensions.map(({ name, component }) => [name, component]), - ); - }, [fieldExtensions]); - - useDebounce( - () => { - try { - if (!content) { - setSteps(undefined); - return; - } - const parsed: JsonValue = yaml - .parseAllDocuments(content) - .filter(c => c) - .map(c => c.toJSON())[0]; - - if (!isJsonObject(parsed)) { - setSteps(undefined); - return; - } - - let rootObj = parsed; - if (!contentIsSpec) { - const isTemplate = - String(parsed.kind).toLocaleLowerCase('en-US') === 'template'; - if (!isTemplate) { - setSteps(undefined); - return; - } - - rootObj = isJsonObject(parsed.spec) ? parsed.spec : {}; - } - - const { parameters } = rootObj; - - if (!Array.isArray(parameters)) { - setErrorText('Template parameters must be an array'); - setSteps(undefined); - return; - } - - const fieldValidators = Object.fromEntries( - fieldExtensions.map(({ name, validation }) => [name, validation]), - ); - - setErrorText(); - setSteps( - parameters.flatMap(param => - isJsonObject(param) - ? [ - { - title: String(param.title), - schema: param, - validate: createValidator(param, fieldValidators, { - apiHolder, - }), - }, - ] - : [], - ), - ); - } catch (e) { - setErrorText(e.message); - } - }, - 250, - [contentIsSpec, content, apiHolder], - ); - - if (!steps) { - return null; - } - - return ( -
-
- - onUpdate(e.formData)} - onReset={() => onUpdate({})} - finishButtonLabel={onDryRun && 'Try It'} - onFinish={onDryRun && (() => onDryRun(data))} - layouts={layouts} - /> - -
-
- ); -} - -/** A version of the TemplateEditorForm that is connected to the DirectoryEditor and DryRun contexts */ -export function TemplateEditorFormDirectoryEditorDryRun( - props: Pick< - TemplateEditorFormProps, - 'setErrorText' | 'fieldExtensions' | 'layouts' - >, -) { - const { setErrorText, fieldExtensions = [], layouts } = props; - const dryRun = useDryRun(); - - const directoryEditor = useDirectoryEditor(); - const { selectedFile } = directoryEditor; - - const [data, setData] = useState({}); - - const handleDryRun = async () => { - if (!selectedFile) { - return; - } - - try { - await dryRun.execute({ - templateContent: selectedFile.content, - values: data, - files: directoryEditor.files, - }); - setErrorText(); - } catch (e) { - setErrorText(String(e.cause || e)); - throw e; - } - }; - - const content = - selectedFile && selectedFile.path.match(/\.ya?ml$/) - ? selectedFile.content - : undefined; - - return ( - - ); -} - -TemplateEditorForm.DirectoryEditorDryRun = - TemplateEditorFormDirectoryEditorDryRun; diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorPage.tsx b/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorPage.tsx deleted file mode 100644 index 127c73dcf5..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateEditorPage.tsx +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import React, { useState } from 'react'; -import { Content, Header, Page } from '@backstage/core-components'; -import { - TemplateDirectoryAccess, - WebFileSystemAccess, -} from '../../lib/filesystem'; -import { type LayoutOptions } from '@backstage/plugin-scaffolder-react'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; -import { TemplateEditorIntro } from '../../next/TemplateEditorPage/TemplateEditorIntro'; -import { TemplateEditor } from './TemplateEditor'; -import { TemplateFormPreviewer } from './TemplateFormPreviewer'; -import { CustomFieldExplorer } from './CustomFieldExplorer'; - -type Selection = - | { - type: 'local'; - directory: TemplateDirectoryAccess; - } - | { - type: 'form'; - } - | { - type: 'field-explorer'; - }; - -interface TemplateEditorPageProps { - defaultPreviewTemplate?: string; - customFieldExtensions?: LegacyFieldExtensionOptions[]; - layouts?: LayoutOptions[]; -} - -export function TemplateEditorPage(props: TemplateEditorPageProps) { - const [selection, setSelection] = useState(); - - let content: JSX.Element | null = null; - if (selection?.type === 'local') { - content = ( - setSelection(undefined)} - layouts={props.layouts} - /> - ); - } else if (selection?.type === 'form') { - content = ( - setSelection(undefined)} - layouts={props.layouts} - /> - ); - } else if (selection?.type === 'field-explorer') { - content = ( - setSelection(undefined)} - /> - ); - } else { - content = ( - - { - if (option === 'local') { - WebFileSystemAccess.requestDirectoryAccess() - .then(directory => setSelection({ type: 'local', directory })) - .catch(() => {}); - } else if (option === 'form') { - setSelection({ type: 'form' }); - } else if (option === 'field-explorer') { - setSelection({ type: 'field-explorer' }); - } - }} - /> - - ); - } - - return ( - -
- {content} - - ); -} diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateFormPreviewer.tsx b/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateFormPreviewer.tsx deleted file mode 100644 index 9e92d12d93..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/TemplateFormPreviewer.tsx +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -import { Entity } from '@backstage/catalog-model'; -import { alertApiRef, useApi } from '@backstage/core-plugin-api'; -import { - catalogApiRef, - humanizeEntityRef, -} from '@backstage/plugin-catalog-react'; -import { - FormControl, - IconButton, - InputLabel, - LinearProgress, - makeStyles, - MenuItem, - Select, -} from '@material-ui/core'; -import CloseIcon from '@material-ui/icons/Close'; -import React, { useCallback, useState } from 'react'; -import useAsync from 'react-use/lib/useAsync'; -import yaml from 'yaml'; -import { LayoutOptions } from '@backstage/plugin-scaffolder-react'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; -import { TemplateEditorTextArea } from '../../next/TemplateEditorPage/TemplateEditorTextArea'; -import { TemplateEditorForm } from './TemplateEditorForm'; - -const EXAMPLE_TEMPLATE_PARAMS_YAML = `# Edit the template parameters below to see how they will render in the scaffolder form UI -parameters: - - title: Fill in some steps - required: - - name - properties: - name: - title: Name - type: string - description: Unique name of the component - owner: - title: Owner - type: string - description: Owner of the component - ui:field: OwnerPicker - ui:options: - catalogFilter: - kind: Group - - title: Choose a location - required: - - repoUrl - properties: - repoUrl: - title: Repository Location - type: string - ui:field: RepoUrlPicker - ui:options: - allowedHosts: - - github.com -steps: - - id: fetch-base - name: Fetch Base - action: fetch:template - input: - url: ./template - values: - name: \${{parameters.name}} -`; - -type TemplateOption = { - label: string; - value: Entity; -}; - -const useStyles = makeStyles(theme => ({ - root: { - gridArea: 'pageContent', - display: 'grid', - gridTemplateAreas: ` - "controls controls" - "textArea preview" - `, - gridTemplateRows: 'auto 1fr', - gridTemplateColumns: '1fr 1fr', - }, - controls: { - gridArea: 'controls', - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - margin: theme.spacing(1), - }, - textArea: { - gridArea: 'textArea', - }, - preview: { - gridArea: 'preview', - }, -})); - -export const TemplateFormPreviewer = ({ - defaultPreviewTemplate = EXAMPLE_TEMPLATE_PARAMS_YAML, - customFieldExtensions = [], - onClose, - layouts = [], -}: { - defaultPreviewTemplate?: string; - customFieldExtensions?: LegacyFieldExtensionOptions[]; - onClose?: () => void; - layouts?: LayoutOptions[]; -}) => { - const classes = useStyles(); - const alertApi = useApi(alertApiRef); - const catalogApi = useApi(catalogApiRef); - const [selectedTemplate, setSelectedTemplate] = useState(''); - const [errorText, setErrorText] = useState(); - const [templateOptions, setTemplateOptions] = useState([]); - const [templateYaml, setTemplateYaml] = useState(defaultPreviewTemplate); - const [formState, setFormState] = useState({}); - - const { loading } = useAsync( - () => - catalogApi - .getEntities({ - filter: { kind: 'template' }, - fields: [ - 'kind', - 'metadata.namespace', - 'metadata.name', - 'metadata.title', - 'spec.parameters', - 'spec.steps', - 'spec.output', - ], - }) - .then(({ items }) => - setTemplateOptions( - items.map(template => ({ - label: - template.metadata.title ?? - humanizeEntityRef(template, { defaultKind: 'template' }), - value: template, - })), - ), - ) - .catch(e => - alertApi.post({ - message: `Error loading exisiting templates: ${e.message}`, - severity: 'error', - }), - ), - [catalogApi], - ); - - const handleSelectChange = useCallback( - // TODO(Rugvip): Afaik this should be Entity, but didn't want to make runtime changes while fixing types - (selected: any) => { - setSelectedTemplate(selected); - setTemplateYaml(yaml.stringify(selected.spec)); - }, - [setTemplateYaml], - ); - - return ( - <> - {loading && } -
-
- - - Load Existing Template - - - - - - - -
-
- -
-
- -
-
- - ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplateEditorPage/index.ts b/plugins/scaffolder/src/legacy/TemplateEditorPage/index.ts deleted file mode 100644 index 7de0d3c679..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateEditorPage/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { TemplateEditorPage } from './TemplateEditorPage'; diff --git a/plugins/scaffolder/src/legacy/TemplateList/TemplateList.test.tsx b/plugins/scaffolder/src/legacy/TemplateList/TemplateList.test.tsx deleted file mode 100644 index c65613d2f5..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateList/TemplateList.test.tsx +++ /dev/null @@ -1,83 +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 { screen } from '@testing-library/react'; -import React from 'react'; -import { renderInTestApp, TestApiProvider } from '@backstage/test-utils'; -import { TemplateList } from './TemplateList'; -import { rootRouteRef } from '../../routes'; -import { - ScmIntegrationsApi, - scmIntegrationsApiRef, -} from '@backstage/integration-react'; -import { TemplateEntityV1beta3 } from '@backstage/plugin-scaffolder-common'; - -jest.mock('@backstage/plugin-catalog-react', () => ({ - useEntityList: jest.fn().mockReturnValue({ - loading: false, - entities: [ - { - apiVersion: 'scaffolder.backstage.io/v1beta3', - kind: 'Template', - metadata: { - name: 't1', - }, - spec: {}, - }, - { - apiVersion: 'scaffolder.backstage.io/v1beta3', - kind: 'Template', - metadata: { - name: 't2', - }, - spec: {}, - }, - ], - }), - getEntityRelations: jest.fn().mockImplementation(() => []), - getEntitySourceLocation: jest.fn().mockImplementation(() => ({})), -})); - -describe('TemplateList', () => { - const mockIntegrationsApi: Partial = { - byHost: () => ({ type: 'github' }), - }; - - it('should filter out templates based on provided filter condition', async () => { - const TemplateCardComponent = ({ - template, - }: { - template: TemplateEntityV1beta3; - }) => ( -
{template.metadata.name}
- ); - - await renderInTestApp( - -
- e.metadata.name === 't1'} - TemplateCardComponent={TemplateCardComponent} - /> -
-
, - { mountedRoutes: { '/': rootRouteRef } }, - ); - - expect(() => screen.getByTestId('t2')).toThrow(); - expect(screen.getByTestId('t1')).toBeDefined(); - }); -}); diff --git a/plugins/scaffolder/src/legacy/TemplateList/TemplateList.tsx b/plugins/scaffolder/src/legacy/TemplateList/TemplateList.tsx deleted file mode 100644 index c925cdf6ef..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateList/TemplateList.tsx +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2021 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import React, { ComponentType } from 'react'; -import { Entity, stringifyEntityRef } from '@backstage/catalog-model'; -import { - isTemplateEntityV1beta3, - TemplateEntityV1beta3, -} from '@backstage/plugin-scaffolder-common'; -import { - Content, - ContentHeader, - ItemCardGrid, - Link, - Progress, - WarningPanel, -} from '@backstage/core-components'; -import { useEntityList } from '@backstage/plugin-catalog-react'; -import { Typography } from '@material-ui/core'; -import { TemplateCard } from '../TemplateCard'; - -/** - * @internal - */ -export type TemplateListProps = { - TemplateCardComponent?: - | ComponentType<{ template: TemplateEntityV1beta3 }> - | undefined; - group?: { - title?: React.ReactNode; - filter: (entity: TemplateEntityV1beta3) => boolean; - }; - templateFilter?: (entity: TemplateEntityV1beta3) => boolean; -}; - -/** - * @internal - */ -export const TemplateList = ({ - TemplateCardComponent, - group, - templateFilter, -}: TemplateListProps) => { - const { loading, error, entities } = useEntityList(); - const Card = TemplateCardComponent || TemplateCard; - const templateEntities = entities.filter(isTemplateEntityV1beta3); - const maybeFilteredEntities = ( - group ? templateEntities.filter(group.filter) : templateEntities - ).filter(e => (templateFilter ? templateFilter(e) : true)); - - const titleComponent: React.ReactNode = (() => { - if (group && group.title) { - if (typeof group.title === 'string') { - return ; - } - return group.title; - } - - return ; - })(); - - if (group && maybeFilteredEntities.length === 0) { - return null; - } - return ( - <> - {loading && } - - {error && ( - - {error.message} - - )} - - {!error && !loading && !entities.length && ( - - No templates found that match your filter. Learn more about{' '} - - adding templates - - . - - )} - - - {titleComponent} - - {maybeFilteredEntities && - maybeFilteredEntities?.length > 0 && - maybeFilteredEntities.map((template: Entity) => ( - - ))} - - - - ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplateList/index.ts b/plugins/scaffolder/src/legacy/TemplateList/index.ts deleted file mode 100644 index 0b62f9cbb1..0000000000 --- a/plugins/scaffolder/src/legacy/TemplateList/index.ts +++ /dev/null @@ -1,17 +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. - */ -export { TemplateList } from './TemplateList'; -export type { TemplateListProps } from './TemplateList'; diff --git a/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.test.tsx b/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.test.tsx deleted file mode 100644 index 344929fa43..0000000000 --- a/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.test.tsx +++ /dev/null @@ -1,366 +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 { - MockAnalyticsApi, - renderInTestApp, - TestApiRegistry, -} from '@backstage/test-utils'; -import { act, fireEvent, screen, within } from '@testing-library/react'; -import React from 'react'; -import { Route, Routes } from 'react-router-dom'; -import { - scaffolderApiRef, - ScaffolderApi, - SecretsContextProvider, -} from '@backstage/plugin-scaffolder-react'; -import { TemplatePage } from './TemplatePage'; -import { - featureFlagsApiRef, - FeatureFlagsApi, - analyticsApiRef, -} from '@backstage/core-plugin-api'; -import { ApiProvider } from '@backstage/core-app-api'; -import { errorApiRef } from '@backstage/core-plugin-api'; -import { rootRouteRef } from '../../routes'; - -jest.mock('react-router-dom', () => { - return { - ...(jest.requireActual('react-router-dom') as any), - useParams: () => ({ - templateName: 'test', - }), - }; -}); - -const scaffolderApiMock: jest.Mocked = { - cancelTask: jest.fn(), - scaffold: jest.fn(), - getTemplateParameterSchema: jest.fn(), - getIntegrationsList: jest.fn(), - getTask: jest.fn(), - streamLogs: jest.fn(), - listActions: jest.fn(), - listTasks: jest.fn(), -}; - -const featureFlagsApiMock: jest.Mocked = { - isActive: jest.fn(), - registerFlag: jest.fn(), - getRegisteredFlags: jest.fn(), - save: jest.fn(), -}; - -const errorApiMock = { post: jest.fn(), error$: jest.fn() }; - -const analyticsMock = new MockAnalyticsApi(); - -const schemaMockValue = { - title: 'my-schema', - steps: [ - { - title: 'Fill in some steps', - schema: { - title: 'Fill in some steps', - 'backstage:featureFlag': 'experimental-feature', - properties: { - name: { - title: 'Name', - type: 'string', - 'backstage:featureFlag': 'should-show-some-stuff-first-option', - }, - description: { - title: 'Description', - type: 'string', - description: 'A description for the component', - }, - owner: { - title: 'Owner', - type: 'string', - description: 'Owner of the component', - }, - }, - type: 'object', - }, - }, - { - title: 'Send data', - schema: { - title: 'Send data', - properties: { - user: { - title: 'User', - type: 'string', - }, - }, - type: 'object', - }, - }, - ], -}; - -const apis = TestApiRegistry.from( - [scaffolderApiRef, scaffolderApiMock], - [errorApiRef, errorApiMock], - [featureFlagsApiRef, featureFlagsApiMock], - [analyticsApiRef, analyticsMock], -); - -describe('TemplatePage', () => { - beforeEach(() => jest.resetAllMocks()); - - it('renders correctly', async () => { - scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ - title: 'React SSR Template', - steps: [], - }); - const rendered = await renderInTestApp( - - - - - , - { - mountedRoutes: { - '/create': rootRouteRef, - }, - }, - ); - - expect(rendered.getByText('Create a New Component')).toBeInTheDocument(); - expect(rendered.getByText('React SSR Template')).toBeInTheDocument(); - }); - - it('renders spinner while loading', async () => { - let resolve: Function; - const promise = new Promise(res => { - resolve = res; - }); - scaffolderApiMock.getTemplateParameterSchema.mockReturnValueOnce(promise); - const rendered = await renderInTestApp( - - - - - , - { - mountedRoutes: { - '/create': rootRouteRef, - }, - }, - ); - - expect(rendered.getByText('Create a New Component')).toBeInTheDocument(); - expect(rendered.getByTestId('loading-progress')).toBeInTheDocument(); - - await act(async () => { - resolve!({ - title: 'React SSR Template', - steps: [], - }); - }); - }); - - it('captures expected analytics events', async () => { - scaffolderApiMock.scaffold.mockResolvedValue({ taskId: 'xyz' }); - scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ - title: 'schema-4-analytics', - steps: [ - { - title: 'Fill in some steps', - schema: { - properties: { - name: { - title: 'Name', - type: 'string', - }, - }, - required: ['name'], - }, - }, - ], - }); - const { findByLabelText, findByText } = await renderInTestApp( - - - - - , - { - mountedRoutes: { - '/create': rootRouteRef, - }, - }, - ); - - // Fill out the name field - expect(await findByText('Fill in some steps')).toBeInTheDocument(); - fireEvent.change(await findByLabelText('Name', { exact: false }), { - target: { value: 'expected-name' }, - }); - - // Go to the final page - fireEvent.click(await findByText('Next step')); - expect(await findByText('Reset')).toBeInTheDocument(); - - // Create the software - await act(async () => { - fireEvent.click(await findByText('Create')); - }); - - // The "Next Step" button should have fired an event - expect(analyticsMock.getEvents()[0]).toMatchObject({ - action: 'click', - subject: 'Next Step (1)', - context: { entityRef: 'template:default/test' }, - }); - - // And the "Create" button should have fired an event - expect(analyticsMock.getEvents()[1]).toMatchObject({ - action: 'create', - subject: 'expected-name', - context: { entityRef: 'template:default/test' }, - }); - }); - - it('navigates away if no template was loaded', async () => { - scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue( - undefined as any, - ); - - const rendered = await renderInTestApp( - - - - - - } - /> - This is root} /> - - , - { - routeEntries: ['/create'], - mountedRoutes: { '/create': rootRouteRef }, - }, - ); - - expect( - rendered.queryByText('Create a New Component'), - ).not.toBeInTheDocument(); - expect(rendered.getByText('This is root')).toBeInTheDocument(); - }); - - it('display template with oneOf', async () => { - scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue({ - title: 'my-schema', - steps: [ - { - title: 'Fill in some steps', - schema: { - oneOf: [ - { - title: 'First', - properties: { - name: { - title: 'Name', - type: 'string', - }, - }, - required: ['name'], - }, - { - title: 'Second', - properties: { - something: { - title: 'Something', - type: 'string', - }, - }, - required: ['something'], - }, - ], - }, - }, - ], - }); - - const { findByText, findByLabelText, findAllByRole, findByRole } = - await renderInTestApp( - - - - - , - { - mountedRoutes: { - '/create/actions': rootRouteRef, - }, - }, - ); - - expect(await findByText('Fill in some steps')).toBeInTheDocument(); - - // Fill the first option - fireEvent.change(await findByLabelText('Name', { exact: false }), { - target: { value: 'my-name' }, - }); - - // Switch to second option - fireEvent.mouseDown((await findAllByRole('button'))[0]); - const listbox = within(await findByRole('listbox')); - fireEvent.click(listbox.getByText(/Second/i)); - - // Fill the second option - fireEvent.change(await findByLabelText('Something', { exact: false }), { - target: { value: 'my-something' }, - }); - - // Go to the final page - fireEvent.click(await findByText('Next step')); - expect(await findByText('Reset')).toBeInTheDocument(); - }); - - it('should display a section or property based on a feature flag', async () => { - featureFlagsApiMock.isActive.mockImplementation(flag => { - return flag === 'experimental-feature'; - }); - scaffolderApiMock.getTemplateParameterSchema.mockResolvedValue( - schemaMockValue, - ); - - await renderInTestApp( - - - - - , - { - mountedRoutes: { - '/create/actions': rootRouteRef, - }, - }, - ); - - expect(screen.queryByText('Name')).not.toBeInTheDocument(); - expect(screen.getByText('Description')).toBeInTheDocument(); - expect(screen.getByText('Owner')).toBeInTheDocument(); - expect(screen.getByText('Send data')).toBeInTheDocument(); - }); -}); diff --git a/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.tsx b/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.tsx deleted file mode 100644 index ffb754bae3..0000000000 --- a/plugins/scaffolder/src/legacy/TemplatePage/TemplatePage.tsx +++ /dev/null @@ -1,187 +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 { LinearProgress } from '@material-ui/core'; -import { IChangeEvent } from '@rjsf/core'; -import qs from 'qs'; -import React, { ComponentType, useCallback, useState } from 'react'; -import { Navigate, useNavigate } from 'react-router-dom'; -import useAsync from 'react-use/lib/useAsync'; -import { - type LayoutOptions, - scaffolderApiRef, - useTemplateSecrets, - ReviewStepProps, -} from '@backstage/plugin-scaffolder-react'; -import { MultistepJsonForm } from '../MultistepJsonForm'; -import { createValidator } from './createValidator'; - -import { Content, Header, InfoCard, Page } from '@backstage/core-components'; -import { - AnalyticsContext, - errorApiRef, - useApi, - useApiHolder, - useRouteRef, - useRouteRefParams, -} from '@backstage/core-plugin-api'; -import { stringifyEntityRef } from '@backstage/catalog-model'; -import { - rootRouteRef, - scaffolderTaskRouteRef, - selectedTemplateRouteRef, -} from '../../routes'; -import { LegacyFieldExtensionOptions } from '@backstage/plugin-scaffolder-react/alpha'; - -const useTemplateParameterSchema = (templateRef: string) => { - const scaffolderApi = useApi(scaffolderApiRef); - const { value, loading, error } = useAsync( - () => scaffolderApi.getTemplateParameterSchema(templateRef), - [scaffolderApi, templateRef], - ); - return { schema: value, loading, error }; -}; - -type Props = { - ReviewStepComponent?: ComponentType; - customFieldExtensions?: LegacyFieldExtensionOptions[]; - layouts?: LayoutOptions[]; - headerOptions?: { - pageTitleOverride?: string; - title?: string; - subtitle?: string; - }; -}; - -export const TemplatePage = ({ - ReviewStepComponent, - customFieldExtensions = [], - layouts = [], - headerOptions, -}: Props) => { - const apiHolder = useApiHolder(); - const secretsContext = useTemplateSecrets(); - const errorApi = useApi(errorApiRef); - const scaffolderApi = useApi(scaffolderApiRef); - const { templateName, namespace } = useRouteRefParams( - selectedTemplateRouteRef, - ); - const templateRef = stringifyEntityRef({ - name: templateName, - kind: 'template', - namespace, - }); - const navigate = useNavigate(); - const scaffolderTaskRoute = useRouteRef(scaffolderTaskRouteRef); - const rootRoute = useRouteRef(rootRouteRef); - const { schema, loading, error } = useTemplateParameterSchema(templateRef); - const [formState, setFormState] = useState>(() => { - const query = qs.parse(window.location.search, { - ignoreQueryPrefix: true, - }); - - try { - return JSON.parse(query.formData as string); - } catch (e) { - return query.formData ?? {}; - } - }); - const handleFormReset = () => setFormState({}); - const handleChange = useCallback( - (e: IChangeEvent) => setFormState(e.formData), - [setFormState], - ); - - const handleCreate = async () => { - const { taskId } = await scaffolderApi.scaffold({ - templateRef, - values: formState, - secrets: secretsContext?.secrets, - }); - - const formParams = qs.stringify( - { formData: formState }, - { addQueryPrefix: true }, - ); - const newUrl = `${window.location.pathname}${formParams}`; - // We use direct history manipulation since useSearchParams and - // useNavigate in react-router-dom cause unnecessary extra rerenders. - // Also make sure to replace the state rather than pushing to avoid - // extra back/forward slots. - window.history?.replaceState(null, document.title, newUrl); - - navigate(scaffolderTaskRoute({ taskId })); - }; - - if (error) { - errorApi.post(new Error(`Failed to load template, ${error}`)); - return ; - } - if (!loading && !schema) { - errorApi.post(new Error('Template was not found.')); - return ; - } - - const customFieldComponents = Object.fromEntries( - customFieldExtensions.map(({ name, component }) => [name, component]), - ); - - const customFieldValidators = Object.fromEntries( - customFieldExtensions.map(({ name, validation }) => [name, validation]), - ); - - return ( - - -
- - {loading && } - {schema && ( - - { - return { - ...step, - validate: createValidator( - step.schema, - customFieldValidators, - { apiHolder }, - ), - }; - })} - /> - - )} - - - - ); -}; diff --git a/plugins/scaffolder/src/legacy/TemplatePage/createValidator.test.ts b/plugins/scaffolder/src/legacy/TemplatePage/createValidator.test.ts deleted file mode 100644 index d6a5cd7499..0000000000 --- a/plugins/scaffolder/src/legacy/TemplatePage/createValidator.test.ts +++ /dev/null @@ -1,200 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { createValidator } from './createValidator'; -import { LegacyCustomFieldValidator } from '@backstage/plugin-scaffolder-react/alpha'; -import { ApiHolder } from '@backstage/core-plugin-api'; -import { FieldValidation, FormValidation } from '@rjsf/utils'; - -type CustomLinkType = { - url: string; - title: string; - icon: string; -}; - -describe('createValidator', () => { - const validators: Record< - string, - undefined | LegacyCustomFieldValidator - > = { - CustomPicker: ( - value: unknown, - fieldValidation: FieldValidation, - _context: { apiHolder: ApiHolder }, - ) => { - if (!value || !(value as { value?: unknown }).value) { - fieldValidation.addError('Error !'); - } - }, - CustomLink: ( - values: unknown, - fieldValidation: FieldValidation, - _context: { apiHolder: ApiHolder }, - ) => { - const input = values as CustomLinkType[]; - for (const item of input) { - const validGitlabUrlRegex = - /gitlab\.(?:stg\.)?spotify\.com\?owner=.*&repo=.*/; - - if (!item || !validGitlabUrlRegex.test(item.url)) { - fieldValidation.addError( - `Make sure to put in a valid gitlab clone url.`, - ); - } - } - }, - TagPicker: ( - values: unknown, - fieldValidation: FieldValidation, - _context: { apiHolder: ApiHolder }, - ) => { - const input = values as string[]; - for (const item of input) { - if (!/^[a-z0-9-]+$/.test(item)) { - fieldValidation.addError( - 'A tag name can only contain lowercase letters, numeric characters or dashes', - ); - } - } - }, - }; - - const apiHolderMock: jest.Mocked = { - get: jest.fn().mockImplementation(() => { - return null; - }), - }; - - const context = { - apiHolder: apiHolderMock, - }; - - it('should call validator for object property from a custom field extension', () => { - /* GIVEN */ - const rootSchema = { - title: 'Title', - properties: { - p1: { - title: 'PropertyOn', - type: 'object', - 'ui:field': 'CustomPicker', - }, - }, - }; - const validator = createValidator(rootSchema, validators, context); - - const formData = { - p1: {}, - }; - const errors = { - addError: jest.fn(), - p1: { - addError: jest.fn(), - } as unknown as FormValidation, - } as unknown as FormValidation; - - /* WHEN */ - const result = validator(formData, errors); - - /* THEN */ - expect(result).not.toBeNull(); - expect(result.p1?.addError).toHaveBeenCalledTimes(1); - }); - - it('should call validator for array property from a custom field extension', () => { - /* GIVEN */ - const rootSchema = { - title: 'My form', - properties: { - tags: { - title: 'Tags', - type: 'array', - items: { - type: 'string', - 'ui:field': 'TagPicker', - }, - }, - }, - }; - const validator = createValidator(rootSchema, validators, context); - - const formData = { - tags: ['invalid-tag$$'], - }; - const errors = { - addError: jest.fn(), - tags: { - addError: jest.fn(), - } as unknown as FormValidation, - } as unknown as FormValidation; - - /* WHEN */ - const result = validator(formData, errors); - - /* THEN */ - expect(result).not.toBeNull(); - expect(result.tags?.addError).toHaveBeenCalledTimes(1); - }); - - it('should call validator for array object property from a custom field extension', () => { - /* GIVEN */ - const rootSchema = { - title: 'My links', - properties: { - links: { - title: 'Links', - type: 'array', - items: { - type: 'object', - required: ['url', 'title', 'icon'], - properties: { - url: { - title: 'url', - description: 'url', - type: 'object', - 'ui:field': 'CustomLink', - }, - }, - }, - }, - }, - }; - const validator = createValidator(rootSchema, validators, context); - - const formData = { - links: [ - { - url: 'http://gitlab.spotify.nl/owener=me&repo=test', - icon: 'subject', - title: 'My repository for testing features', - } as CustomLinkType, - ], - }; - const errors = { - addError: jest.fn(), - links: { - addError: jest.fn(), - } as unknown as FormValidation, - } as unknown as FormValidation; - - /* WHEN */ - const result = validator(formData, errors); - - /* THEN */ - expect(result).not.toBeNull(); - expect(result.links?.addError).toHaveBeenCalledTimes(1); - }); -}); diff --git a/plugins/scaffolder/src/legacy/TemplatePage/createValidator.ts b/plugins/scaffolder/src/legacy/TemplatePage/createValidator.ts deleted file mode 100644 index ae0b834fe9..0000000000 --- a/plugins/scaffolder/src/legacy/TemplatePage/createValidator.ts +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2022 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { LegacyCustomFieldValidator } from '@backstage/plugin-scaffolder-react/alpha'; -import { FieldValidation, FormValidation } from '@rjsf/utils'; -import { JsonObject, JsonValue } from '@backstage/types'; -import { ApiHolder } from '@backstage/core-plugin-api'; - -function isObject(obj: unknown): obj is JsonObject { - return typeof obj === 'object' && obj !== null && !Array.isArray(obj); -} - -function isArray(obj: unknown): obj is JsonObject { - return typeof obj === 'object' && obj !== null && Array.isArray(obj); -} - -export const createValidator = ( - rootSchema: JsonObject, - validators: Record>, - context: { - apiHolder: ApiHolder; - }, -) => { - function validate( - schema: JsonObject, - formData: JsonObject, - errors: FormValidation, - ) { - const schemaProps = schema.properties; - const customObject = schema.type === 'object' && schemaProps === undefined; - - if (!isObject(schemaProps) && !customObject) { - return; - } - - if (schemaProps) { - for (const [key, propData] of Object.entries(formData)) { - const propValidation = errors[key]; - - const doValidate = (item: JsonValue | undefined) => { - if (item && isObject(item)) { - const fieldName = item['ui:field'] as string; - if (fieldName && typeof validators[fieldName] === 'function') { - validators[fieldName]!( - propData as JsonObject[], - propValidation as FieldValidation, - context, - ); - } - } - }; - - const propSchemaProps = schemaProps[key]; - if (isObject(propData) && isObject(propSchemaProps)) { - validate( - propSchemaProps, - propData as JsonObject, - propValidation as FormValidation, - ); - } else if (isArray(propData)) { - if (isObject(propSchemaProps)) { - const { items } = propSchemaProps; - if (isObject(items)) { - if (items.type === 'object') { - const properties = (items?.properties ?? []) as JsonObject[]; - for (const [, value] of Object.entries(properties)) { - doValidate(value); - } - } else { - doValidate(items); - } - } - } - } else { - doValidate(propSchemaProps); - } - } - } else if (customObject) { - const fieldName = schema['ui:field'] as string; - if (fieldName && typeof validators[fieldName] === 'function') { - validators[fieldName]!(formData, errors, context); - } - } - } - - return (formData: JsonObject, errors: FormValidation) => { - validate(rootSchema, formData, errors); - return errors; - }; -}; diff --git a/plugins/scaffolder/src/legacy/TemplatePage/index.ts b/plugins/scaffolder/src/legacy/TemplatePage/index.ts deleted file mode 100644 index d3ae24504b..0000000000 --- a/plugins/scaffolder/src/legacy/TemplatePage/index.ts +++ /dev/null @@ -1,17 +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. - */ -export { TemplatePage } from './TemplatePage'; -export { createValidator } from './createValidator'; diff --git a/plugins/scaffolder/src/legacy/index.ts b/plugins/scaffolder/src/legacy/index.ts deleted file mode 100644 index 2393be0f69..0000000000 --- a/plugins/scaffolder/src/legacy/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright 2023 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -export { LegacyRouter, type LegacyRouterProps } from './Router'; -export { LegacyScaffolderPage } from '../plugin'; diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx index 68ed6f906f..fb69dabe13 100644 --- a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/DryRunResultsView.tsx @@ -27,8 +27,8 @@ import React, { useEffect, useMemo, useState } from 'react'; import { useDryRun } from '../DryRunContext'; import { DryRunResultsSplitView } from './DryRunResultsSplitView'; import { FileBrowser } from '../../../components/FileBrowser'; -import { TaskPageLinks } from '../../../legacy/TaskPage/TaskPageLinks'; -import { TaskStatusStepper } from '../../../legacy/TaskPage/TaskPage'; +import { TaskPageLinks } from './TaskPageLinks'; +import { TaskStatusStepper } from './TaskStatusStepper'; const useStyles = makeStyles({ root: { diff --git a/plugins/scaffolder/src/legacy/TaskPage/IconLink.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/IconLink.tsx similarity index 97% rename from plugins/scaffolder/src/legacy/TaskPage/IconLink.tsx rename to plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/IconLink.tsx index dd0b3da876..6676c622fa 100644 --- a/plugins/scaffolder/src/legacy/TaskPage/IconLink.tsx +++ b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/IconLink.tsx @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Backstage Authors + * Copyright 2024 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - import React from 'react'; import { Grid, LinkProps, makeStyles, Typography } from '@material-ui/core'; import LanguageIcon from '@material-ui/icons/Language'; diff --git a/plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskPageLinks.tsx similarity index 100% rename from plugins/scaffolder/src/legacy/TaskPage/TaskPageLinks.tsx rename to plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskPageLinks.tsx diff --git a/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.tsx b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.tsx new file mode 100644 index 0000000000..f776d133e0 --- /dev/null +++ b/plugins/scaffolder/src/next/TemplateEditorPage/DryRunResults/TaskStatusStepper.tsx @@ -0,0 +1,199 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import React, { useState } from 'react'; +import { memo } from 'react'; +import Step from '@material-ui/core/Step'; +import StepLabel from '@material-ui/core/StepLabel'; +import Stepper from '@material-ui/core/Stepper'; +import { ScaffolderTaskStatus } from '@backstage/plugin-scaffolder-react'; +import { + StepButton, + StepIconProps, + Theme, + createStyles, + makeStyles, + CircularProgress, +} from '@material-ui/core'; +import Cancel from '@material-ui/icons/Cancel'; +import Check from '@material-ui/icons/Check'; +import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord'; +import Typography from '@material-ui/core/Typography'; +import { DateTime, Interval } from 'luxon'; +import useInterval from 'react-use/lib/useInterval'; +import humanizeDuration from 'humanize-duration'; +import classNames from 'classnames'; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + width: '100%', + }, + button: { + marginBottom: theme.spacing(2), + marginLeft: theme.spacing(2), + }, + actionsContainer: { + marginBottom: theme.spacing(2), + }, + resetContainer: { + padding: theme.spacing(3), + }, + labelWrapper: { + display: 'flex', + flex: 1, + flexDirection: 'row', + justifyContent: 'space-between', + }, + stepWrapper: { + width: '100%', + }, + }), +); + +type TaskStep = { + id: string; + name: string; + status: ScaffolderTaskStatus; + startedAt?: string; + endedAt?: string; +}; + +const useStepIconStyles = makeStyles(theme => + createStyles({ + root: { + color: theme.palette.text.disabled, + display: 'flex', + height: 22, + alignItems: 'center', + }, + completed: { + color: theme.palette.status.ok, + }, + error: { + color: theme.palette.status.error, + }, + }), +); + +const StepTimeTicker = ({ step }: { step: TaskStep }) => { + const [time, setTime] = useState(''); + + useInterval(() => { + if (!step.startedAt) { + setTime(''); + return; + } + + const end = step.endedAt + ? DateTime.fromISO(step.endedAt) + : DateTime.local(); + + const startedAt = DateTime.fromISO(step.startedAt); + const formatted = Interval.fromDateTimes(startedAt, end) + .toDuration() + .valueOf(); + + setTime(humanizeDuration(formatted, { round: true })); + }, 1000); + + return {time}; +}; + +function TaskStepIconComponent(props: StepIconProps) { + const classes = useStepIconStyles(); + const { active, completed, error } = props; + + const getMiddle = () => { + if (active) { + return ; + } + if (completed) { + return ; + } + if (error) { + return ; + } + return ; + }; + + return ( +
+ {getMiddle()} +
+ ); +} + +export const TaskStatusStepper = memo( + (props: { + steps: TaskStep[]; + currentStepId: string | undefined; + onUserStepChange: (id: string) => void; + classes?: { + root?: string; + }; + }) => { + const { steps, currentStepId, onUserStepChange } = props; + const classes = useStyles(props); + + return ( +
+ s.id === currentStepId)} + orientation="vertical" + nonLinear + > + {steps.map((step, index) => { + const isCancelled = step.status === 'cancelled'; + const isActive = step.status === 'processing'; + const isCompleted = step.status === 'completed'; + const isFailed = step.status === 'failed'; + const isSkipped = step.status === 'skipped'; + + return ( + + onUserStepChange(step.id)}> + +
+ {step.name} + {isSkipped ? ( + Skipped + ) : ( + + )} +
+
+
+
+ ); + })} +
+
+ ); + }, +); diff --git a/plugins/scaffolder/src/plugin.tsx b/plugins/scaffolder/src/plugin.tsx index fdde81695d..7fa7dc1521 100644 --- a/plugins/scaffolder/src/plugin.tsx +++ b/plugins/scaffolder/src/plugin.tsx @@ -212,15 +212,3 @@ export const EntityTagsPickerFieldExtension = scaffolderPlugin.provide( schema: EntityTagsPickerSchema, }), ); - -/** - * @alpha - * The Router and main entrypoint to the Alpha Scaffolder plugin. - */ -export const LegacyScaffolderPage = scaffolderPlugin.provide( - createRoutableExtension({ - name: 'LegacyScaffolderPage', - component: () => import('./legacy/Router').then(m => m.LegacyRouter), - mountPoint: rootRouteRef, - }), -); From 4cca1bd66b82cdcd6af05d183aad5d0747734182 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jan 2024 14:18:31 +0100 Subject: [PATCH 101/152] chore: api-reports Signed-off-by: blam --- plugins/scaffolder-react/api-report-alpha.md | 45 -------------------- 1 file changed, 45 deletions(-) diff --git a/plugins/scaffolder-react/api-report-alpha.md b/plugins/scaffolder-react/api-report-alpha.md index 615b54df84..6976951670 100644 --- a/plugins/scaffolder-react/api-report-alpha.md +++ b/plugins/scaffolder-react/api-report-alpha.md @@ -7,11 +7,8 @@ import { ApiHolder } from '@backstage/core-plugin-api'; import { ComponentType } from 'react'; -import { CustomFieldExtensionSchema } from '@backstage/plugin-scaffolder-react'; import { CustomFieldValidator } from '@backstage/plugin-scaffolder-react'; import { Dispatch } from 'react'; -import { Extension } from '@backstage/core-plugin-api'; -import { FieldExtensionComponent } from '@backstage/plugin-scaffolder-react'; import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react'; import { FieldValidation } from '@rjsf/utils'; import { FormProps } from '@backstage/plugin-scaffolder-react'; @@ -24,7 +21,6 @@ import { default as React_2 } from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; -import { ScaffolderRJSFFieldProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderRJSFFormProps } from '@backstage/plugin-scaffolder-react'; import { ScaffolderStep } from '@backstage/plugin-scaffolder-react'; import { ScaffolderTaskOutput } from '@backstage/plugin-scaffolder-react'; @@ -51,14 +47,6 @@ export const createAsyncValidators: ( // @alpha export const createFieldValidation: () => FieldValidation; -// @alpha -export function createLegacyScaffolderFieldExtension< - TReturnValue = unknown, - TInputProps = unknown, ->( - options: LegacyFieldExtensionOptions, -): Extension>; - // @alpha export const DefaultTemplateOutputs: (props: { output?: ScaffolderTaskOutput; @@ -83,39 +71,6 @@ export type FormValidation = { [name: string]: FieldValidation | FormValidation; }; -// @alpha -export type LegacyCustomFieldValidator = ( - data: TFieldReturnValue, - field: FieldValidation, - context: { - apiHolder: ApiHolder; - }, -) => void | Promise; - -// @alpha -export interface LegacyFieldExtensionComponentProps< - TFieldReturnValue, - TUiOptions = unknown, -> extends ScaffolderRJSFFieldProps { - // (undocumented) - uiSchema: ScaffolderRJSFFieldProps['uiSchema'] & { - 'ui:options'?: TUiOptions; - }; -} - -// @alpha -export type LegacyFieldExtensionOptions< - TFieldReturnValue = unknown, - TInputProps = unknown, -> = { - name: string; - component: ( - props: LegacyFieldExtensionComponentProps, - ) => JSX.Element | null; - validation?: LegacyCustomFieldValidator; - schema?: CustomFieldExtensionSchema; -}; - // @alpha export interface ParsedTemplateSchema { // (undocumented) From c56f1a2e90ee4e0f2e2d1c3fa9152db8e0c96e99 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jan 2024 14:19:16 +0100 Subject: [PATCH 102/152] chore: added changeset Signed-off-by: blam --- .changeset/fast-jeans-walk.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/fast-jeans-walk.md diff --git a/.changeset/fast-jeans-walk.md b/.changeset/fast-jeans-walk.md new file mode 100644 index 0000000000..f733c9f464 --- /dev/null +++ b/.changeset/fast-jeans-walk.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-scaffolder-react': minor +'@backstage/plugin-scaffolder': minor +--- + +Remove the old legacy exports from `/alpha` From 73d7e2c6f9e6cdae234e9c81522732b6d302e166 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jan 2024 16:19:51 +0100 Subject: [PATCH 103/152] chore: fix tsdoc Signed-off-by: blam Signed-off-by: blam --- plugins/scaffolder/src/deprecated.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/deprecated.ts b/plugins/scaffolder/src/deprecated.ts index ffe923058b..907871946c 100644 --- a/plugins/scaffolder/src/deprecated.ts +++ b/plugins/scaffolder/src/deprecated.ts @@ -215,10 +215,10 @@ export type LayoutOptions = LayoutOptionsTemp; /** * TaskPageProps for constructing a TaskPage - * @param loadingText - Optional loading text shown before a task begins executing. * @public * @deprecated - this is a useless type that is no longer used. */ export type TaskPageProps = { + // Optional loading text shown before a task begins executing. loadingText?: string; }; From dbbfb664f8856b4055f22eed42722a6395a25e66 Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 19 Jan 2024 10:12:33 +0100 Subject: [PATCH 104/152] chore: fixing rebase issues Signed-off-by: blam --- plugins/scaffolder/api-report-alpha.md | 40 -------------------------- plugins/scaffolder/src/alpha.tsx | 2 -- 2 files changed, 42 deletions(-) diff --git a/plugins/scaffolder/api-report-alpha.md b/plugins/scaffolder/api-report-alpha.md index c8824d42da..3f0b60cd95 100644 --- a/plugins/scaffolder/api-report-alpha.md +++ b/plugins/scaffolder/api-report-alpha.md @@ -3,19 +3,13 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -/// - import { BackstagePlugin } from '@backstage/frontend-plugin-api'; -import { ComponentType } from 'react'; -import { Entity } from '@backstage/catalog-model'; import { ExternalRouteRef } from '@backstage/frontend-plugin-api'; import { FieldExtensionOptions } from '@backstage/plugin-scaffolder-react'; import type { FormProps as FormProps_2 } from '@rjsf/core'; import { FormProps as FormProps_3 } from '@backstage/plugin-scaffolder-react'; -import { JSX as JSX_2 } from 'react'; import { LayoutOptions } from '@backstage/plugin-scaffolder-react'; import { PathParams } from '@backstage/core-plugin-api'; -import { PropsWithChildren } from 'react'; import { default as React_2 } from 'react'; import { ReviewStepProps } from '@backstage/plugin-scaffolder-react'; import { RouteRef } from '@backstage/frontend-plugin-api'; @@ -55,40 +49,6 @@ export type FormProps = Pick< 'transformErrors' | 'noHtml5Validate' >; -// @alpha -export const LegacyRouter: (props: LegacyRouterProps) => React_2.JSX.Element; - -// @alpha -export type LegacyRouterProps = { - components?: { - ReviewStepComponent?: ComponentType; - TemplateCardComponent?: - | ComponentType<{ - template: TemplateEntityV1beta3; - }> - | undefined; - TaskPageComponent?: ComponentType>; - }; - groups?: Array<{ - title?: React_2.ReactNode; - filter: (entity: Entity) => boolean; - }>; - templateFilter?: (entity: TemplateEntityV1beta3) => boolean; - defaultPreviewTemplate?: string; - headerOptions?: { - pageTitleOverride?: string; - title?: string; - subtitle?: string; - }; - contextMenu?: { - editor?: boolean; - actions?: boolean; - }; -}; - -// @alpha -export const LegacyScaffolderPage: (props: LegacyRouterProps) => JSX_2.Element; - // @alpha (undocumented) export type TemplateListPageProps = { TemplateCardComponent?: React_2.ComponentType<{ diff --git a/plugins/scaffolder/src/alpha.tsx b/plugins/scaffolder/src/alpha.tsx index 152745eec1..0d079f9c40 100644 --- a/plugins/scaffolder/src/alpha.tsx +++ b/plugins/scaffolder/src/alpha.tsx @@ -50,8 +50,6 @@ export { type TemplateWizardPageProps, } from './next'; -export * from './legacy'; - const scaffolderApi = createApiExtension({ factory: createApiFactory({ api: scaffolderApiRef, From 7c88d8f49bc84a3292a1cc89a920cff42a0f64d7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:23:08 +0000 Subject: [PATCH 105/152] chore(deps): update dependency @types/node to v16.18.72 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9ecf43baa9..5496b78a2e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18370,9 +18370,9 @@ __metadata: linkType: hard "@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": - version: 16.18.71 - resolution: "@types/node@npm:16.18.71" - checksum: 7b61ec0abd2a298518b562618ddbcc1704a6427c666c9512a0b12700a2b26cad151ccfd3c147c9ab77ac5e0bcb62bfbf67c48fa0ef812a00859a1e33368e8802 + version: 16.18.72 + resolution: "@types/node@npm:16.18.72" + checksum: c0f1748b20566417d3b777d11962f84500b4cb9c77ebb59c51f2882c3e5141bd4636709dc63fe2e45d6d6799fc4c5a98931abcb2dd3a5b107e448d76ae848320 languageName: node linkType: hard From a8c4290ac62556a288cac93aa5c79f2f7751156e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:23:48 +0000 Subject: [PATCH 106/152] fix(deps): update dependency react-hook-form to v7.49.3 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9ecf43baa9..55a291eff6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -38311,11 +38311,11 @@ __metadata: linkType: hard "react-hook-form@npm:^7.12.2, react-hook-form@npm:^7.13.0": - version: 7.48.2 - resolution: "react-hook-form@npm:7.48.2" + version: 7.49.3 + resolution: "react-hook-form@npm:7.49.3" peerDependencies: react: ^16.8.0 || ^17 || ^18 - checksum: 39652d08f78f16e5234049ceeb794d8bcd4d146772aca99c6db8c5aea8926cd4a230d02098886a441074deefbc24ffcb5ed57bc7cb0009d950115f5b3e627f12 + checksum: 9769845e2749f5c9d1d033be3819bc572ffa67f19ec2e567c61a58d0d060107facdf8425aab253c2e667c00853b7706a50bd6739fca6228c284c9313420ca26f languageName: node linkType: hard From 7155c30a889b5f8ff45b6d401ee491b98e9ec33f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 19 Jan 2024 11:17:56 +0100 Subject: [PATCH 107/152] core-compat-api: added convertLegacyRouteRefs Signed-off-by: Patrik Oldsberg --- .changeset/olive-singers-accept.md | 5 +++ packages/core-compat-api/api-report.md | 21 +++++++++++ .../src/convertLegacyRouteRef.ts | 37 +++++++++++++++++++ packages/core-compat-api/src/index.ts | 6 ++- 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 .changeset/olive-singers-accept.md diff --git a/.changeset/olive-singers-accept.md b/.changeset/olive-singers-accept.md new file mode 100644 index 0000000000..7e8cf7ae8d --- /dev/null +++ b/.changeset/olive-singers-accept.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Added `convertLegacyRouteRefs` for bulk conversion of plugin routes. diff --git a/packages/core-compat-api/api-report.md b/packages/core-compat-api/api-report.md index 494747be60..a7aae2b5b9 100644 --- a/packages/core-compat-api/api-report.md +++ b/packages/core-compat-api/api-report.md @@ -40,5 +40,26 @@ export function convertLegacyRouteRef< ref: ExternalRouteRef, ): ExternalRouteRef_2; +// @public +export function convertLegacyRouteRefs< + TRefs extends { + [name in string]: RouteRef | SubRouteRef | ExternalRouteRef; + }, +>( + refs: TRefs, +): { + [KName in keyof TRefs]: ToNewRouteRef; +}; + +// @public +export type ToNewRouteRef = + T extends RouteRef + ? RouteRef_2 + : T extends SubRouteRef + ? SubRouteRef_2 + : T extends ExternalRouteRef + ? ExternalRouteRef_2 + : never; + // (No @packageDocumentation comment for this package) ``` diff --git a/packages/core-compat-api/src/convertLegacyRouteRef.ts b/packages/core-compat-api/src/convertLegacyRouteRef.ts index c5ee603722..36311dc081 100644 --- a/packages/core-compat-api/src/convertLegacyRouteRef.ts +++ b/packages/core-compat-api/src/convertLegacyRouteRef.ts @@ -40,6 +40,43 @@ import { toInternalSubRouteRef } from '../../frontend-plugin-api/src/routing/Sub // eslint-disable-next-line @backstage/no-relative-monorepo-imports import { toInternalExternalRouteRef } from '../../frontend-plugin-api/src/routing/ExternalRouteRef'; +/** + * Converts a legacy route ref type to the new system. + * + * @public + */ +export type ToNewRouteRef< + T extends LegacyRouteRef | LegacySubRouteRef | LegacyExternalRouteRef, +> = T extends LegacyRouteRef + ? RouteRef + : T extends LegacySubRouteRef + ? SubRouteRef + : T extends LegacyExternalRouteRef + ? ExternalRouteRef + : never; + +/** + * Converts a collection of legacy route refs to the new system. + * This is particularly useful when defining plugin `routes` and `externalRoutes`. + * + * @public + */ +export function convertLegacyRouteRefs< + TRefs extends { + [name in string]: + | LegacyRouteRef + | LegacySubRouteRef + | LegacyExternalRouteRef; + }, +>(refs: TRefs): { [KName in keyof TRefs]: ToNewRouteRef } { + return Object.fromEntries( + Object.entries(refs).map(([name, ref]) => [ + name, + convertLegacyRouteRef(ref as LegacyRouteRef), + ]), + ) as { [KName in keyof TRefs]: ToNewRouteRef }; +} + /** * A temporary helper to convert a legacy route ref to the new system. * diff --git a/packages/core-compat-api/src/index.ts b/packages/core-compat-api/src/index.ts index 68e5d9cf19..b632bf8a57 100644 --- a/packages/core-compat-api/src/index.ts +++ b/packages/core-compat-api/src/index.ts @@ -16,4 +16,8 @@ export * from './compatWrapper'; export { convertLegacyApp } from './convertLegacyApp'; -export { convertLegacyRouteRef } from './convertLegacyRouteRef'; +export { + convertLegacyRouteRef, + convertLegacyRouteRefs, + type ToNewRouteRef, +} from './convertLegacyRouteRef'; From 2f2a1d2ad24a5c9ff5a613e9e7abdad6b0102c4d Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 19 Jan 2024 11:19:43 +0100 Subject: [PATCH 108/152] core-compat-api: forward routes to converted plugins Signed-off-by: Patrik Oldsberg --- .changeset/twenty-taxis-mate.md | 5 +++++ packages/core-compat-api/src/collectLegacyRoutes.tsx | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 .changeset/twenty-taxis-mate.md diff --git a/.changeset/twenty-taxis-mate.md b/.changeset/twenty-taxis-mate.md new file mode 100644 index 0000000000..ab3cc6d406 --- /dev/null +++ b/.changeset/twenty-taxis-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-compat-api': patch +--- + +Plugins converted by `convertLegacyApp` now have their `routes` and `externalRoutes` included as well, allowing the to be used to bind external routes in configuration. diff --git a/packages/core-compat-api/src/collectLegacyRoutes.tsx b/packages/core-compat-api/src/collectLegacyRoutes.tsx index 4d560b81d5..7ddb8c4f7e 100644 --- a/packages/core-compat-api/src/collectLegacyRoutes.tsx +++ b/packages/core-compat-api/src/collectLegacyRoutes.tsx @@ -32,7 +32,10 @@ import { } from '@backstage/frontend-plugin-api'; import React, { Children, ReactNode, isValidElement } from 'react'; import { Route, Routes } from 'react-router-dom'; -import { convertLegacyRouteRef } from './convertLegacyRouteRef'; +import { + convertLegacyRouteRef, + convertLegacyRouteRefs, +} from './convertLegacyRouteRef'; import { compatWrapper } from './compatWrapper'; /* @@ -244,6 +247,8 @@ export function collectLegacyRoutes( createApiExtension({ factory }), ), ], + routes: convertLegacyRouteRefs(plugin.routes ?? {}), + externalRoutes: convertLegacyRouteRefs(plugin.externalRoutes ?? {}), }), ); } From 912ca7b583e01884638bff04859cd4d2871e0eac Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 19 Jan 2024 11:20:39 +0100 Subject: [PATCH 109/152] plugins: use convertLegacyRouteRefs Signed-off-by: Patrik Oldsberg --- .changeset/hot-pillows-poke.md | 11 +++++++++++ plugins/catalog/src/alpha/plugin.tsx | 22 +++++++++++----------- plugins/graphiql/src/alpha.tsx | 7 ++++--- plugins/scaffolder/src/alpha.tsx | 25 +++++++++++++------------ plugins/search/src/alpha.tsx | 7 ++++--- plugins/tech-radar/src/alpha.tsx | 7 ++++--- plugins/techdocs/src/alpha.tsx | 11 ++++++----- plugins/user-settings/src/alpha.tsx | 7 ++++--- 8 files changed, 57 insertions(+), 40 deletions(-) create mode 100644 .changeset/hot-pillows-poke.md diff --git a/.changeset/hot-pillows-poke.md b/.changeset/hot-pillows-poke.md new file mode 100644 index 0000000000..8060e7121f --- /dev/null +++ b/.changeset/hot-pillows-poke.md @@ -0,0 +1,11 @@ +--- +'@backstage/plugin-user-settings': patch +'@backstage/plugin-scaffolder': patch +'@backstage/plugin-tech-radar': patch +'@backstage/plugin-graphiql': patch +'@backstage/plugin-techdocs': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-search': patch +--- + +Use `convertLegacyRouteRefs` to define routes in `/alpha` export plugin. diff --git a/plugins/catalog/src/alpha/plugin.tsx b/plugins/catalog/src/alpha/plugin.tsx index 99aacd27da..9a964c4e46 100644 --- a/plugins/catalog/src/alpha/plugin.tsx +++ b/plugins/catalog/src/alpha/plugin.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { convertLegacyRouteRef } from '@backstage/core-compat-api'; +import { convertLegacyRouteRefs } from '@backstage/core-compat-api'; import { createPlugin } from '@backstage/frontend-plugin-api'; import { entityRouteRef } from '@backstage/plugin-catalog-react'; @@ -38,16 +38,16 @@ import searchResultItems from './searchResultItems'; /** @alpha */ export default createPlugin({ id: 'catalog', - routes: { - catalogIndex: convertLegacyRouteRef(rootRouteRef), - catalogEntity: convertLegacyRouteRef(entityRouteRef), - }, - externalRoutes: { - viewTechDoc: convertLegacyRouteRef(viewTechDocRouteRef), - createComponent: convertLegacyRouteRef(createComponentRouteRef), - createFromTemplate: convertLegacyRouteRef(createFromTemplateRouteRef), - unregisterRedirect: convertLegacyRouteRef(unregisterRedirectRouteRef), - }, + routes: convertLegacyRouteRefs({ + catalogIndex: rootRouteRef, + catalogEntity: entityRouteRef, + }), + externalRoutes: convertLegacyRouteRefs({ + viewTechDoc: viewTechDocRouteRef, + createComponent: createComponentRouteRef, + createFromTemplate: createFromTemplateRouteRef, + unregisterRedirect: unregisterRedirectRouteRef, + }), extensions: [ ...apis, ...pages, diff --git a/plugins/graphiql/src/alpha.tsx b/plugins/graphiql/src/alpha.tsx index fdd343020d..086f397d7a 100644 --- a/plugins/graphiql/src/alpha.tsx +++ b/plugins/graphiql/src/alpha.tsx @@ -37,6 +37,7 @@ import { graphiQLRouteRef } from './route-refs'; import { compatWrapper, convertLegacyRouteRef, + convertLegacyRouteRefs, } from '@backstage/core-compat-api'; /** @alpha */ @@ -128,7 +129,7 @@ export default createPlugin({ graphiqlGitlabGraphiQLEndpointExtension, graphiqlNavItem, ], - routes: { - root: convertLegacyRouteRef(graphiQLRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: graphiQLRouteRef, + }), }); diff --git a/plugins/scaffolder/src/alpha.tsx b/plugins/scaffolder/src/alpha.tsx index 0d079f9c40..37e2978802 100644 --- a/plugins/scaffolder/src/alpha.tsx +++ b/plugins/scaffolder/src/alpha.tsx @@ -29,6 +29,7 @@ import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import { compatWrapper, convertLegacyRouteRef, + convertLegacyRouteRefs, } from '@backstage/core-compat-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { scaffolderApiRef } from '@backstage/plugin-scaffolder-react'; @@ -85,17 +86,17 @@ const scaffolderNavItem = createNavItemExtension({ /** @alpha */ export default createPlugin({ id: 'scaffolder', - routes: { - root: convertLegacyRouteRef(rootRouteRef), - selectedTemplate: convertLegacyRouteRef(selectedTemplateRouteRef), - ongoingTask: convertLegacyRouteRef(scaffolderTaskRouteRef), - actions: convertLegacyRouteRef(actionsRouteRef), - listTasks: convertLegacyRouteRef(scaffolderListTaskRouteRef), - edit: convertLegacyRouteRef(editRouteRef), - }, - externalRoutes: { - registerComponent: convertLegacyRouteRef(registerComponentRouteRef), - viewTechDoc: convertLegacyRouteRef(viewTechDocRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + selectedTemplate: selectedTemplateRouteRef, + ongoingTask: scaffolderTaskRouteRef, + actions: actionsRouteRef, + listTasks: scaffolderListTaskRouteRef, + edit: editRouteRef, + }), + externalRoutes: convertLegacyRouteRefs({ + registerComponent: registerComponentRouteRef, + viewTechDoc: viewTechDocRouteRef, + }), extensions: [scaffolderApi, scaffolderPage, scaffolderNavItem], }); diff --git a/plugins/search/src/alpha.tsx b/plugins/search/src/alpha.tsx index a3a5023693..4f654548f5 100644 --- a/plugins/search/src/alpha.tsx +++ b/plugins/search/src/alpha.tsx @@ -70,6 +70,7 @@ import { UrlUpdater } from './components/SearchPage/SearchPage'; import { compatWrapper, convertLegacyRouteRef, + convertLegacyRouteRefs, } from '@backstage/core-compat-api'; /** @alpha */ @@ -248,7 +249,7 @@ export const searchNavItem = createNavItemExtension({ export default createPlugin({ id: 'search', extensions: [searchApi, searchPage, searchNavItem], - routes: { - root: convertLegacyRouteRef(rootRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + }), }); diff --git a/plugins/tech-radar/src/alpha.tsx b/plugins/tech-radar/src/alpha.tsx index 1fb5fa06f3..54e209fae3 100644 --- a/plugins/tech-radar/src/alpha.tsx +++ b/plugins/tech-radar/src/alpha.tsx @@ -27,6 +27,7 @@ import { SampleTechRadarApi } from './sample'; import { compatWrapper, convertLegacyRouteRef, + convertLegacyRouteRefs, } from '@backstage/core-compat-api'; import { rootRouteRef } from './plugin'; @@ -61,7 +62,7 @@ export const techRadarApi = createApiExtension({ export default createPlugin({ id: 'tech-radar', extensions: [techRadarPage, techRadarApi], - routes: { - root: convertLegacyRouteRef(rootRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + }), }); diff --git a/plugins/techdocs/src/alpha.tsx b/plugins/techdocs/src/alpha.tsx index ef564d469a..88dce3b48e 100644 --- a/plugins/techdocs/src/alpha.tsx +++ b/plugins/techdocs/src/alpha.tsx @@ -34,6 +34,7 @@ import { import { compatWrapper, convertLegacyRouteRef, + convertLegacyRouteRefs, } from '@backstage/core-compat-api'; import { techdocsApiRef, @@ -168,9 +169,9 @@ export default createPlugin({ techDocsEntityContent, techDocsSearchResultListItemExtension, ], - routes: { - root: convertLegacyRouteRef(rootRouteRef), - docRoot: convertLegacyRouteRef(rootDocsRouteRef), - entityContent: convertLegacyRouteRef(rootCatalogDocsRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: rootRouteRef, + docRoot: rootDocsRouteRef, + entityContent: rootCatalogDocsRouteRef, + }), }); diff --git a/plugins/user-settings/src/alpha.tsx b/plugins/user-settings/src/alpha.tsx index 5dabd70739..f14db7233d 100644 --- a/plugins/user-settings/src/alpha.tsx +++ b/plugins/user-settings/src/alpha.tsx @@ -22,6 +22,7 @@ import { } from '@backstage/frontend-plugin-api'; import { convertLegacyRouteRef, + convertLegacyRouteRefs, compatWrapper, } from '@backstage/core-compat-api'; import SettingsIcon from '@material-ui/icons/Settings'; @@ -65,7 +66,7 @@ export const settingsNavItem = createNavItemExtension({ export default createPlugin({ id: 'user-settings', extensions: [userSettingsPage, settingsNavItem], - routes: { - root: convertLegacyRouteRef(settingsRouteRef), - }, + routes: convertLegacyRouteRefs({ + root: settingsRouteRef, + }), }); From 53d2a7dcc2e224b6fa155c20f7146e232e3a9b1f Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 19 Jan 2024 11:33:35 +0100 Subject: [PATCH 110/152] app-next: add route binding from migrated app to a converted one Signed-off-by: Patrik Oldsberg --- packages/app-next/app-config.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 77f4284455..1b77205d5e 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -5,6 +5,7 @@ app: bindings: plugin.pages.externalRoutes.pageX: plugin.pages.routes.pageX plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot + plugin.catalog.externalRoutes.createComponent: plugin.catalog-import.routes.importPage extensions: # - apis.plugin.graphiql.browse.gitlab: true From d5d9912f3b7a444441bac4f57c2056468cd81713 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 19 Jan 2024 12:06:56 +0100 Subject: [PATCH 111/152] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Patrik Oldsberg --- .changeset/twenty-taxis-mate.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/twenty-taxis-mate.md b/.changeset/twenty-taxis-mate.md index ab3cc6d406..65ed158e9b 100644 --- a/.changeset/twenty-taxis-mate.md +++ b/.changeset/twenty-taxis-mate.md @@ -2,4 +2,4 @@ '@backstage/core-compat-api': patch --- -Plugins converted by `convertLegacyApp` now have their `routes` and `externalRoutes` included as well, allowing the to be used to bind external routes in configuration. +Plugins converted by `convertLegacyApp` now have their `routes` and `externalRoutes` included as well, allowing them to be used to bind external routes in configuration. From 5d3fd7942dba99c3a498a9b214f130ed421a3971 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:07:10 +0000 Subject: [PATCH 112/152] chore(deps): update dependency @openapitools/openapi-generator-cli to v2.9.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 55a291eff6..7d3134509b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -13966,8 +13966,8 @@ __metadata: linkType: hard "@openapitools/openapi-generator-cli@npm:^2.4.26, @openapitools/openapi-generator-cli@npm:^2.7.0": - version: 2.8.1 - resolution: "@openapitools/openapi-generator-cli@npm:2.8.1" + version: 2.9.0 + resolution: "@openapitools/openapi-generator-cli@npm:2.9.0" dependencies: "@nestjs/axios": 3.0.1 "@nestjs/common": 10.3.0 @@ -13988,7 +13988,7 @@ __metadata: tslib: 2.6.2 bin: openapi-generator-cli: main.js - checksum: 5e0e5460ec8cf5f96338703820066265d187ce984f24cfcf6f1abe70049b38a8953f0835658f5915ec41304a98bd76cf17ec9fb50fa746701116be6502dca39e + checksum: 102040381b42edbab4fa34aa49c3cf8188fdb892c3e251bcb5bdf7c599597f3572d52d44fed7bead5fcaec24d624491c20dc96d2f164fa44e7fc232c7d787f69 languageName: node linkType: hard From c7bf00693cba7e255ef68de96eae42d8af31aca2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:08:17 +0000 Subject: [PATCH 113/152] fix(deps): update dependency sass to v1.70.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index e9f027d3a7..a2489da001 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -10817,15 +10817,15 @@ __metadata: linkType: hard "sass@npm:^1.57.1": - version: 1.69.7 - resolution: "sass@npm:1.69.7" + version: 1.70.0 + resolution: "sass@npm:1.70.0" dependencies: chokidar: ">=3.0.0 <4.0.0" immutable: ^4.0.0 source-map-js: ">=0.6.2 <2.0.0" bin: sass: sass.js - checksum: c67cd32b69fb26a50e4535353e4145de8cbc8187db07c467cc335157fd56d03cae98754f86efe43b880b29f20c0a168ab972c7f74ebfe234e2bd2dfb868890cb + checksum: fd1b622cf9b7fa699a03ec634611997552ece45eb98ac365fef22f42bdcb8ed63b326b64173379c966830c8551ae801e44e4a00d2de16fdadda2dc8f35400bbb languageName: node linkType: hard From b679e61ee8bbb6af7fb2f5f24f825a4ed86753e7 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 12:08:52 +0000 Subject: [PATCH 114/152] fix(deps): update dependency sucrase to v3.35.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/yarn.lock b/yarn.lock index 55a291eff6..349abda05b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -28106,20 +28106,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.1.6": - version: 7.1.6 - resolution: "glob@npm:7.1.6" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: 351d549dd90553b87c2d3f90ce11aed9e1093c74130440e7ae0592e11bbcd2ce7f0ebb8ba6bfe63aaf9b62166a7f4c80cb84490ae5d78408bb2572bf7d4ee0a6 - languageName: node - linkType: hard - "glob@npm:7.2.3, glob@npm:^7.0.0, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:^7.1.7, glob@npm:^7.2.0": version: 7.2.3 resolution: "glob@npm:7.2.3" @@ -41669,12 +41655,12 @@ __metadata: linkType: hard "sucrase@npm:^3.20.2": - version: 3.34.0 - resolution: "sucrase@npm:3.34.0" + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" dependencies: "@jridgewell/gen-mapping": ^0.3.2 commander: ^4.0.0 - glob: 7.1.6 + glob: ^10.3.10 lines-and-columns: ^1.1.6 mz: ^2.7.0 pirates: ^4.0.1 @@ -41682,7 +41668,7 @@ __metadata: bin: sucrase: bin/sucrase sucrase-node: bin/sucrase-node - checksum: 61860063bdf6103413698e13247a3074d25843e91170825a9752e4af7668ffadd331b6e99e92fc32ee5b3c484ee134936f926fa9039d5711fafff29d017a2110 + checksum: 9fc5792a9ab8a14dcf9c47dcb704431d35c1cdff1d17d55d382a31c2e8e3063870ad32ce120a80915498486246d612e30cda44f1624d9d9a10423e1a43487ad1 languageName: node linkType: hard From 71f3794765805aa17cf1cd12586b4a68ec84f49b Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:31:14 +0000 Subject: [PATCH 115/152] fix(deps): update dependency swagger-ui-react to v5.11.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 581 +++++++++++++++++++++++++++++------------------------- 1 file changed, 315 insertions(+), 266 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9b30c4a836..897d934365 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3110,13 +3110,13 @@ __metadata: languageName: node linkType: hard -"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.23.5": - version: 7.23.6 - resolution: "@babel/runtime-corejs3@npm:7.23.6" +"@babel/runtime-corejs3@npm:^7.20.7, @babel/runtime-corejs3@npm:^7.22.15, @babel/runtime-corejs3@npm:^7.23.7": + version: 7.23.8 + resolution: "@babel/runtime-corejs3@npm:7.23.8" dependencies: core-js-pure: ^3.30.2 regenerator-runtime: ^0.14.0 - checksum: 92576ea17ff674025aa3cd208bfb8301e4bde432cfa404b56f94242948cbd40125c1a4c9c54a99134e08ded8bcde305633a22b1f4c676687c3b868557d23b762 + checksum: e786b79bcb3031bd7433fb4523e43f0acbd386cd7bb5b0a6df6e627c7965706b3d5612211ea3d729ce4459ba1d1b654ccdd8aefe791c6413f70882ee1be903b9 languageName: node linkType: hard @@ -9692,10 +9692,10 @@ __metadata: languageName: node linkType: hard -"@braintree/sanitize-url@npm:=6.0.4": - version: 6.0.4 - resolution: "@braintree/sanitize-url@npm:6.0.4" - checksum: f5ec6048973722ea1c46ae555d2e9eb848d7fa258994f8ea7d6db9514ee754ea3ef344ef71b3696d486776bcb839f3124e79f67c6b5b2814ed2da220b340627c +"@braintree/sanitize-url@npm:=7.0.0": + version: 7.0.0 + resolution: "@braintree/sanitize-url@npm:7.0.0" + checksum: 2ca9c617343d00c668486f5382cd6ef7d0bd54ba6c8d40deb0f3e7843390a056cf0c590f1d06d38a49b7d8b3f63c006f89873944996742835f2b523b707913a0 languageName: node linkType: hard @@ -16423,406 +16423,452 @@ __metadata: languageName: node linkType: hard -"@swagger-api/apidom-ast@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ast@npm:0.83.0" +"@swagger-api/apidom-ast@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ast@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-error": ^0.83.0 + "@swagger-api/apidom-error": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 unraw: ^3.0.0 - checksum: 7449e1c3eb316920e277d773a3504af9379ade513836342a55817888c73ad65bf08c6c11222fbd9f6862108ab3fad3d2ce2dfa7ab4f9acf48a3134f27f057f87 + checksum: 11770246944b3bb5964a88e71a7e32e37691b7a3b6ec9ae6acf58cd580571ca75c079d749bb952cd08c86be2155cbef3ed418ad6194681e1e2fea643a2128af2 languageName: node linkType: hard -"@swagger-api/apidom-core@npm:>=0.83.0 <1.0.0, @swagger-api/apidom-core@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-core@npm:0.83.0" +"@swagger-api/apidom-core@npm:>=0.90.0 <1.0.0, @swagger-api/apidom-core@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-core@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 + "@swagger-api/apidom-ast": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 "@types/ramda": ~0.29.6 minim: ~0.23.8 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 short-unique-id: ^5.0.2 stampit: ^4.3.2 - checksum: acbdd94eabb7ad44ba5c7c1e69a831bd5b53aca6f4e66abb66ad5e8d53717cb453a5885c51008b6a7b5f7e16de8660a1b5652a4498883c84d37cb62b093e3e91 + checksum: e02f1a838ab1d811960a48f98922c66bb306f1a5a1a154b8fe4c6445fb0a20c671409fee14a2e49051387bc7b5c28e218161bcc1519c6ad9cbc767bf432ee05d languageName: node linkType: hard -"@swagger-api/apidom-error@npm:>=0.83.0 <1.0.0, @swagger-api/apidom-error@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-error@npm:0.83.0" +"@swagger-api/apidom-error@npm:>=0.90.0 <1.0.0, @swagger-api/apidom-error@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-error@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - checksum: 94fee6b4ee4a60a7d121d17c92808bec452bd7c638c3ecceda6fc427f47a0297e78a2366a37c7216b84b4a95aead825eb421356fa17dc67dd44da86e27660d1c + checksum: bdd3051a47f4300344c0f1f2a55c4bb3dbe147ba8ebd27c94a77db2c90e8cc371bca63b72dfb49d0034794b82929edbf70d20ab33f59ba9f8c2d2fc8dc0430db languageName: node linkType: hard -"@swagger-api/apidom-json-pointer@npm:>=0.83.0 <1.0.0, @swagger-api/apidom-json-pointer@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-json-pointer@npm:0.83.0" +"@swagger-api/apidom-json-pointer@npm:>=0.90.0 <1.0.0, @swagger-api/apidom-json-pointer@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-json-pointer@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: b48d784f18a8d8e948292c1126a0b768a0df347d31b6b1e4fa28df64ceb83cbb5d465a80fc87e127529e339000eb97d28f7c4d632af47db3dbe7eeb92f9477c1 + checksum: 89d748fd1f52fce381d4e07710244d94c4d9f8a20411ca8bcfb33e7ec515432c7766660b296a9c603fa6b861b39eca4917151f98f3688f1bdcf10f25f9d90134 languageName: node linkType: hard -"@swagger-api/apidom-ns-api-design-systems@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-api-design-systems@npm:0.83.0" +"@swagger-api/apidom-ns-api-design-systems@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-api-design-systems@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-1": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 711a533d1a386e296525bbb131948e24ca548ee0d4de67acd8ae9f37097fef5801d82887eecdb9a45f954fb10c30666634fa78edb2e09d061beaab8dde47da35 + checksum: 8e993f6b8cd37cb9857a34437e2b16e087ccce524373726a167bf98ab622c44a03c47c81e218ff4b00aa7bd973a25bd696e2437c5fd018ace09ba2ad0fcda9de languageName: node linkType: hard -"@swagger-api/apidom-ns-asyncapi-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:0.83.0" +"@swagger-api/apidom-ns-asyncapi-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-asyncapi-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-json-schema-draft-7": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-json-schema-draft-7": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 0c7e6bc35c2b5e46bd08922309cd494133302ce3b6b8d0cbfbfb99106ea7c1e160ca4ab11f21161598c532c86fafa9127320e5b561560a01e51ceef9d545e767 + checksum: bef3de2c2a854590c8bb1cd892a5b170b65e14119a97528652457bf1e3e19f4a05d7da41ddb697f64de9972c8fad8afc7e6ccfa97ce8809fc9eba8c1305d509c languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-4@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:0.83.0" +"@swagger-api/apidom-ns-json-schema-draft-4@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-json-schema-draft-4@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.83.0 - "@swagger-api/apidom-core": ^0.83.0 + "@swagger-api/apidom-ast": ^0.92.0 + "@swagger-api/apidom-core": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 7fb5db60a95d3531b9fb38693a0b758a8bb3e946f7e7f4e202eaef093cdd53c6289031c8b8977e8548c6376b8e41a627827b64e2c9f5de2f63fc3a427e2f670b + checksum: 6b0d57acf1c34df314c401ab25d0feb3ad5638fc8c848f6b84c76691fef177a39a9e8ebfa16309d22c764d9bfb292ed5ad39943b2cece67dde746a6e9d5fdc12 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-6@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:0.83.0" +"@swagger-api/apidom-ns-json-schema-draft-6@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-json-schema-draft-6@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-ns-json-schema-draft-4": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-ns-json-schema-draft-4": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 60d3d359bf0e8054bad013fb2d7bd4c020d4fb0ce6819538b47986ea7f89269af5f1c3b65c7ddb864bc502a7c41e6069c042a9be753cd9d47dc13b9d0572d216 + checksum: 5660459753ff1359527e61820db59a6465ecc8b51cc5a6c14e155b2144b67570c6fc2c7c41d97617470d1478b85e26879fadc3f1ff4460c6309411c13db209e6 languageName: node linkType: hard -"@swagger-api/apidom-ns-json-schema-draft-7@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:0.83.0" +"@swagger-api/apidom-ns-json-schema-draft-7@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-json-schema-draft-7@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-ns-json-schema-draft-6": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-ns-json-schema-draft-6": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 01623f0c65795d1022e903d39858caac018f9fe338049247ea668805e1926d05713faae9f8ea43188e465a1e2f6783a5e3b38036965b56859036a16d8deb3b86 + checksum: 838d2b9917b4086880156e895d67adfd6400236e97b2a75814356dc55b0d45afd3e458bfe97b52af39aeea4c3483fc40f7a1551af14285977009513a168fb87c languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-openapi-2@npm:0.83.0" +"@swagger-api/apidom-ns-openapi-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-openapi-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-ns-json-schema-draft-4": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-ns-json-schema-draft-4": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 078b138517b0c0dffa67186f2a23938ba87c98c9dd98d431ff812a8be45b3b711e22749b62e22ccd70d4615a92c0ca442b793386fe201583aa8c52556260b082 + checksum: 154de0b299a3ff0fcac40cf995dafb8c173cb4e294c75f952a9fa9cd5452eaab7783a9e5abb425fbc6e7135466155645e9b96fa9495f3effa7aec18f3152e6c3 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-0@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:0.83.0" +"@swagger-api/apidom-ns-openapi-3-0@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-openapi-3-0@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-ns-json-schema-draft-4": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-ns-json-schema-draft-4": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: dabe0745da3c4a3e719f1d9d7d3fbcd177c3735ec00ad762414ed6d9d82eed43b0897896d48e3b8690186029b952146b8bfcab2406d91797e6a9f81f451c51f3 + checksum: 9ae883feea63c06f2514ba834b637bd00d6a56c35fe9b875507698d991199202d307a90bd06ebeaeb5642e3e42fe54238249858568c8a61a1797792fa5fff259 languageName: node linkType: hard -"@swagger-api/apidom-ns-openapi-3-1@npm:>=0.83.0 <1.0.0, @swagger-api/apidom-ns-openapi-3-1@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:0.83.0" +"@swagger-api/apidom-ns-openapi-3-1@npm:>=0.90.0 <1.0.0, @swagger-api/apidom-ns-openapi-3-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-openapi-3-1@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.83.0 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.83.0 + "@swagger-api/apidom-ast": ^0.92.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-0": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 - checksum: 499818a4e437017f7a7ca8861dc1b8e90ba8bb8c300a1f2754bb7b2b05f95e0e897f6305bec1aa5c5af0f03c94e541660a412da25467091bcf35847cc2ee6a4c + checksum: 316861ccd563d28c46039fa4202337f2266e0b99bf803bc71f64d307d824a74dcf7e4cdf7fc4ac25d6cf3521aebc74938c2001eb74ef0c364bc02470a1ffc723 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:0.83.0" +"@swagger-api/apidom-ns-workflows-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-ns-workflows-1@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-api-design-systems": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-1": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 + ramda-adjunct: ^4.1.1 + stampit: ^4.3.2 + checksum: 684804790b58b52a28ae10ffc7fc2fa5bf6b99d8cb3e5d9409e28325c4d4b9ce5b5cc10e0cc401fb21788753ea65287e33db79337388afccd54f6902f60d9159 + languageName: node + linkType: hard + +"@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-json@npm:0.92.0" + dependencies: + "@babel/runtime-corejs3": ^7.20.7 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-api-design-systems": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 + "@types/ramda": ~0.29.6 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 5a0ccf6158f4d9582002c3df4c30136ad8e0f7ee8e75a32f000d638e9b1906e1c42d9137fc4093532bbdbc7ebb3b23eb9fe2819690ae7643e9dde301710b264f + checksum: d9c361b8bb7b8a056acc2c52ef70a2d7304c511efc836cfebc315558ced7e93fe4e958dd554030a3315702d49ace30082ff7f2f91e9a76aaa2fff944a19fe39e languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-api-design-systems-yaml@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-api-design-systems": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-api-design-systems": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: add8a138f379eddf4ce0fa78ffaf91d65c66b073d7304e11ed9db7f6262e2a981e8c9cb5cd36bb2471f573a37179c0f5414be9934714cc9166a98bfb6766931e + checksum: 248e61ed6e202eeab048c7c5e3eff0e0f2b6bfea958a02cbc6d8b831693a015d063db118af25c2e7380a4828ab84bf991764d200409a8eb99ffadb0dc6db65c0 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-json-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-asyncapi-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 0f510d2fb6b69daaebbc55cb186820394584941495ca2d7ae3ad608cc78f48f7b49d8dbf7299ac3fde2c553c8b0244bf265037e435e1e79176afc30029f127ca + checksum: 0fc981bc839758af1b3a8dfa0695376919a515a11ae71e6f9f1e0c91dd1530aa659fa7405cb73109db28fd95f594f09b5ee941611cc4ce4f3d1817335b12ec4b languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-asyncapi-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 46c4fff48263ec0bb40698ded08018c2d6bb653da1e88b1a50ea1b433b83a922a65029d09a77b0febaed6b26822ac520222a8495eafc5f86d81be003b8d0f418 + checksum: a1554f73cc20b75df71296322f6bdf5b5a0f1d775bd9bff89d7798843606e1a383c48960ddc2b1f879c962d2d56962e6096c3000ec2ad6091d49627aa2992b2e languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-json@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-json@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-json@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-json@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.83.0 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 + "@swagger-api/apidom-ast": ^0.92.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 "@types/ramda": ~0.29.6 node-gyp: latest - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 - stampit: ^4.3.2 tree-sitter: =0.20.4 tree-sitter-json: =0.20.1 web-tree-sitter: =0.20.3 - checksum: be94a40fb8d342fe6394f01a803f0403612a796017bd6169dd52c201b26d60ed36cf6b7363faf2c8203fa67b65349d74c99b685c589ffbad30c26df29a3a53db + checksum: ca66dd9fffaa84249398420b9cf2301721d2b173340e0f07097348b73654f54698a370a94d814d7f51b7e662f968054b7fc745607074dbd71892c53714a18b62 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-2@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-json-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: e65c17bc9809af2b4c240c7f128258602bb8bdb440ad508ef08f5829b3347cc053624d117a4297c806a621ed61d9a53158a4aa4ed5cb3487a38935c32226d57d + checksum: 383827eec8fbc2bb36c095c8db4f8dce5264e5ffdfa7aca9a938d603294d99f0a552d57947222c160fbbbc1b79be7d08a7091348f3b373bbe25bd52a34bee529 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-0@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-0": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 3a0df777ad9dfdc6e9f6fb4984dfd3e3299a3ceff430719dc26eda9526e2102f786c89a08705cd780602eab5e85344ebe82dca1043741ecbf3ebd7b1961c109c + checksum: 592c1149d901a3c21594aca5c96a997382648ff559fb67cd6b75630440248a52f26df91ad96fa762e585e297725e20eb6f5623f91a6b6a5e2c950f57df4de760 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-json-3-1@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 5cec1f6237871f479233d75b862ccf774ffe5c6d63c9f08734fa6c0067e53ec9091683e5d309bf94cf080bd570bd4cc2496c85988ec0d52cdc378219033a8bdc + checksum: feaa0b825921ea9defde642b65a07049a8539905142baa113b3b5e90c76630a58ef2557a6f770740e32e5cfa46486fc9f7d3c468b8ec29aaa53cb507c4ad3469 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-2@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: bee0a9f82ddd02c1cbcf2b90896b70c2607a3ff53ca4431d176639878e0da0173946d6e303bab69d4d794dde178fcbd3c815428123f73eca2095ca89e828330d + checksum: efd8dbd705592accadce53b25fc293ee48d1d11ec191f618df4243e6fdfea1add502ef3a5d433051883092dd29bf0a9f2026f2314a0cced1b8f04af59e63e995 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-0": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: d6b3db2b4bf6280a14a76c29b9b6998380a1af09c0726bf736c7b64b30999268c2360776a284c76c2ec45d19f7c106816c98b2f399ec6225cfc9222dee3fb6ff + checksum: 47a034efae971b56d13e96bc64d0973a2015ca303bf63334117ebbcfb2b2676d50a0b68aa24ea5a3b669786a524035762d52ae1cc795f3c3dc65249297a23068 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.0.0 - checksum: 6bd8cf74b04d7f8fbf0cb0202253db349c7ce1ace7ccc9cff72ae36035a3dac5606e73fc5a829f0ac02af2369aff81ad3991701674091e692ffefb4ffed9499d + checksum: c565913065e401109e7e4494f1024d24958959c742e5f842247a56eca6b75dcc9c3495a4c550912a62d62db896b2803bd317642336b4b18c851adc141fa38b61 languageName: node linkType: hard -"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^0.83.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:0.83.0" +"@swagger-api/apidom-parser-adapter-workflows-json-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-workflows-json-1@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-ast": ^0.83.0 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-workflows-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 + "@types/ramda": ~0.29.6 + ramda: ~0.29.1 + ramda-adjunct: ^4.0.0 + checksum: 570f8323aaa5c67da3b5421d6ca6a9f0c3bf486a602584fa5969476e6a4943abe9a7f8f2bd7dfc1a7019286746e65cc76b050cadfabab276e0d452fbad4db59f + languageName: node + linkType: hard + +"@swagger-api/apidom-parser-adapter-workflows-yaml-1@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-workflows-yaml-1@npm:0.92.0" + dependencies: + "@babel/runtime-corejs3": ^7.20.7 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-ns-workflows-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 + "@types/ramda": ~0.29.6 + ramda: ~0.29.1 + ramda-adjunct: ^4.0.0 + checksum: 020ecd34fb45ba57977377f71dc0b28ebba95b950bb873e1f6222eb6b544f91243dfb6b1f7555fe5f279d7bee044089337216a3f4b8e58612fb7066530938f32 + languageName: node + linkType: hard + +"@swagger-api/apidom-parser-adapter-yaml-1-2@npm:^0.92.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-parser-adapter-yaml-1-2@npm:0.92.0" + dependencies: + "@babel/runtime-corejs3": ^7.20.7 + "@swagger-api/apidom-ast": ^0.92.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 "@types/ramda": ~0.29.6 node-gyp: latest - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 - stampit: ^4.3.2 tree-sitter: =0.20.4 tree-sitter-yaml: =0.5.0 web-tree-sitter: =0.20.3 - checksum: df777f06199a82b8ccfb3ea53def7bbd08d06f3e3f4b5083526661bc86dd920ac83ca5b34dcaa26c0194d1dd7e104b6f324454790504b6934eb684011f6a5749 + checksum: 1ee0bf2c2f7fed54ff194917034f31080ad4ad36e14dc2018c80d8bda38f8b6574ace974eca1a65aa336a86df375dabd0661215909551031e796dcee9d0d1a14 languageName: node linkType: hard -"@swagger-api/apidom-reference@npm:>=0.83.0 <1.0.0": - version: 0.83.0 - resolution: "@swagger-api/apidom-reference@npm:0.83.0" +"@swagger-api/apidom-reference@npm:>=0.90.0 <1.0.0": + version: 0.92.0 + resolution: "@swagger-api/apidom-reference@npm:0.92.0" dependencies: "@babel/runtime-corejs3": ^7.20.7 - "@swagger-api/apidom-core": ^0.83.0 - "@swagger-api/apidom-error": ^0.83.0 - "@swagger-api/apidom-json-pointer": ^0.83.0 - "@swagger-api/apidom-ns-asyncapi-2": ^0.83.0 - "@swagger-api/apidom-ns-openapi-2": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-0": ^0.83.0 - "@swagger-api/apidom-ns-openapi-3-1": ^0.83.0 - "@swagger-api/apidom-parser-adapter-api-design-systems-json": ^0.83.0 - "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": ^0.83.0 - "@swagger-api/apidom-parser-adapter-asyncapi-json-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-json": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-json-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-json-3-0": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-json-3-1": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-yaml-2": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": ^0.83.0 - "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": ^0.83.0 - "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.83.0 + "@swagger-api/apidom-core": ^0.92.0 + "@swagger-api/apidom-error": ^0.92.0 + "@swagger-api/apidom-json-pointer": ^0.92.0 + "@swagger-api/apidom-ns-asyncapi-2": ^0.92.0 + "@swagger-api/apidom-ns-openapi-2": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-0": ^0.92.0 + "@swagger-api/apidom-ns-openapi-3-1": ^0.92.0 + "@swagger-api/apidom-ns-workflows-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-api-design-systems-json": ^0.92.0 + "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": ^0.92.0 + "@swagger-api/apidom-parser-adapter-asyncapi-json-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-asyncapi-yaml-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-json": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-json-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-json-3-0": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-json-3-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-yaml-2": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-0": ^0.92.0 + "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-workflows-json-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-workflows-yaml-1": ^0.92.0 + "@swagger-api/apidom-parser-adapter-yaml-1-2": ^0.92.0 "@types/ramda": ~0.29.6 axios: ^1.4.0 minimatch: ^7.4.3 process: ^0.11.10 - ramda: ~0.29.0 + ramda: ~0.29.1 ramda-adjunct: ^4.1.1 stampit: ^4.3.2 dependenciesMeta: @@ -16838,6 +16884,8 @@ __metadata: optional: true "@swagger-api/apidom-ns-openapi-3-1": optional: true + "@swagger-api/apidom-ns-workflows-1": + optional: true "@swagger-api/apidom-parser-adapter-api-design-systems-json": optional: true "@swagger-api/apidom-parser-adapter-api-design-systems-yaml": @@ -16860,9 +16908,13 @@ __metadata: optional: true "@swagger-api/apidom-parser-adapter-openapi-yaml-3-1": optional: true + "@swagger-api/apidom-parser-adapter-workflows-json-1": + optional: true + "@swagger-api/apidom-parser-adapter-workflows-yaml-1": + optional: true "@swagger-api/apidom-parser-adapter-yaml-1-2": optional: true - checksum: b19c9c98cda575b139b1556327809c2367cca9e169ac50ea64049f6cc64148630709e5cc11573124460bcd5f5fc834c0c4434b8564fac4488e16de3cef65a16f + checksum: 05b2c97233f2f60879d2dc62a811a4b31520acb7b3c730d47e60e5927f861c1bf1376e790648b773b242ccb1e24596c567014c570d123655dc59fc2c590668c7 languageName: node linkType: hard @@ -17940,7 +17992,7 @@ __metadata: languageName: node linkType: hard -"@types/hoist-non-react-statics@npm:^3.3.0, @types/hoist-non-react-statics@npm:^3.3.1": +"@types/hoist-non-react-statics@npm:^3.3.0": version: 3.3.1 resolution: "@types/hoist-non-react-statics@npm:3.3.1" dependencies: @@ -22234,7 +22286,7 @@ __metadata: languageName: node linkType: hard -"classnames@npm:*, classnames@npm:^2.2.5, classnames@npm:^2.2.6, classnames@npm:^2.3.1": +"classnames@npm:*, classnames@npm:^2.2.5, classnames@npm:^2.2.6, classnames@npm:^2.3.1, classnames@npm:^2.5.1": version: 2.5.1 resolution: "classnames@npm:2.5.1" checksum: da424a8a6f3a96a2e87d01a432ba19315503294ac7e025f9fece656db6b6a0f7b5003bb1fbb51cbb0d9624d964f1b9bb35a51c73af9b2434c7b292c42231c1e5 @@ -37923,10 +37975,10 @@ __metadata: languageName: node linkType: hard -"ramda@npm:~0.29.0": - version: 0.29.0 - resolution: "ramda@npm:0.29.0" - checksum: 9ab26c06eb7545cbb7eebcf75526d6ee2fcaae19e338f165b2bf32772121e7b28192d6664d1ba222ff76188ba26ab307342d66e805dbb02c860560adc4d5dd57 +"ramda@npm:~0.29.1": + version: 0.29.1 + resolution: "ramda@npm:0.29.1" + checksum: df7c627597a66d5f317f6ba77ad17d7ba818efc9c2c4a638440bc23f2c651e823970ea05b05a28895738c0e4b738fadbdc17b2c97b3ad922826febc09c859c44 languageName: node linkType: hard @@ -38436,35 +38488,25 @@ __metadata: languageName: node linkType: hard -"react-redux@npm:^8.1.3": - version: 8.1.3 - resolution: "react-redux@npm:8.1.3" +"react-redux@npm:^9.0.4": + version: 9.1.0 + resolution: "react-redux@npm:9.1.0" dependencies: - "@babel/runtime": ^7.12.1 - "@types/hoist-non-react-statics": ^3.3.1 "@types/use-sync-external-store": ^0.0.3 - hoist-non-react-statics: ^3.3.2 - react-is: ^18.0.0 use-sync-external-store: ^1.0.0 peerDependencies: - "@types/react": ^16.8 || ^17.0 || ^18.0 - "@types/react-dom": ^16.8 || ^17.0 || ^18.0 - react: ^16.8 || ^17.0 || ^18.0 - react-dom: ^16.8 || ^17.0 || ^18.0 - react-native: ">=0.59" - redux: ^4 || ^5.0.0-beta.0 + "@types/react": ^18.2.25 + react: ^18.0 + react-native: ">=0.69" + redux: ^5.0.0 peerDependenciesMeta: "@types/react": optional: true - "@types/react-dom": - optional: true - react-dom: - optional: true react-native: optional: true redux: optional: true - checksum: 192ea6f6053148ec80a4148ec607bc259403b937e515f616a1104ca5ab357e97e98b8245ed505a17afee67a72341d4a559eaca9607968b4a422aa9b44ba7eb89 + checksum: 1b07714379ff0087b4fb43ffd138cc84efb7608c874d632a4e2162e1874f3e0f6b69a0a9b0f24f93cbf122c694ac9cfd315770676d4cabb167d159f1ef419560 languageName: node linkType: hard @@ -39070,7 +39112,7 @@ __metadata: languageName: node linkType: hard -"redux@npm:^4.0.0, redux@npm:^4.0.4, redux@npm:^4.1.2": +"redux@npm:^4.0.0, redux@npm:^4.0.4": version: 4.1.2 resolution: "redux@npm:4.1.2" dependencies: @@ -39079,6 +39121,13 @@ __metadata: languageName: node linkType: hard +"redux@npm:^5.0.0": + version: 5.0.1 + resolution: "redux@npm:5.0.1" + checksum: e74affa9009dd5d994878b9a1ce30d6569d986117175056edb003de2651c05b10fe7819d6fa94aea1a94de9a82f252f986547f007a2fbeb35c317a2e5f5ecf2c + languageName: node + linkType: hard + "reflect-metadata@npm:0.1.13": version: 0.1.13 resolution: "reflect-metadata@npm:0.1.13" @@ -39382,10 +39431,10 @@ __metadata: languageName: node linkType: hard -"reselect@npm:^4.1.8": - version: 4.1.8 - resolution: "reselect@npm:4.1.8" - checksum: a4ac87cedab198769a29be92bc221c32da76cfdad6911eda67b4d3e7136dca86208c3b210e31632eae31ebd2cded18596f0dd230d3ccc9e978df22f233b5583e +"reselect@npm:^5.0.1": + version: 5.1.0 + resolution: "reselect@npm:5.1.0" + checksum: 5bc9c5d03d7caea00d0c0e24330bf23d91801227346fec1cef6a60988ab8d3dd7cee76e6994ca0915bc1c20845bb2bd929b95753763e0a9db74c0f9dff5cb845 languageName: node linkType: hard @@ -41774,16 +41823,16 @@ __metadata: languageName: node linkType: hard -"swagger-client@npm:^3.24.6": - version: 3.24.6 - resolution: "swagger-client@npm:3.24.6" +"swagger-client@npm:^3.25.0": + version: 3.25.0 + resolution: "swagger-client@npm:3.25.0" dependencies: "@babel/runtime-corejs3": ^7.22.15 - "@swagger-api/apidom-core": ">=0.83.0 <1.0.0" - "@swagger-api/apidom-error": ">=0.83.0 <1.0.0" - "@swagger-api/apidom-json-pointer": ">=0.83.0 <1.0.0" - "@swagger-api/apidom-ns-openapi-3-1": ">=0.83.0 <1.0.0" - "@swagger-api/apidom-reference": ">=0.83.0 <1.0.0" + "@swagger-api/apidom-core": ">=0.90.0 <1.0.0" + "@swagger-api/apidom-error": ">=0.90.0 <1.0.0" + "@swagger-api/apidom-json-pointer": ">=0.90.0 <1.0.0" + "@swagger-api/apidom-ns-openapi-3-1": ">=0.90.0 <1.0.0" + "@swagger-api/apidom-reference": ">=0.90.0 <1.0.0" cookie: ~0.6.0 deepmerge: ~4.3.0 fast-json-patch: ^3.0.0-1 @@ -41794,18 +41843,18 @@ __metadata: qs: ^6.10.2 traverse: ~0.6.6 undici: ^5.24.0 - checksum: 8bb1aeea4576fdd88543c4e011b1f087c52b34e5a069d560937a5c32a6e1e6c7eda243810886f8b09e4e12062e8f2768edaa5e6193140a28f9604bac42b18104 + checksum: 2e989059b4edeab26b148993cea8a68c4ad34927d4058479f2722c01d8bd0f67bfb1f69dc93ce24a5adb5a8e0f45e4438ad38f8bfb70f8d0b1a8d66650abe060 languageName: node linkType: hard "swagger-ui-react@npm:^5.0.0": - version: 5.10.5 - resolution: "swagger-ui-react@npm:5.10.5" + version: 5.11.0 + resolution: "swagger-ui-react@npm:5.11.0" dependencies: - "@babel/runtime-corejs3": ^7.23.5 - "@braintree/sanitize-url": =6.0.4 + "@babel/runtime-corejs3": ^7.23.7 + "@braintree/sanitize-url": =7.0.0 base64-js: ^1.5.1 - classnames: ^2.3.1 + classnames: ^2.5.1 css.escape: 1.5.1 deep-extend: 0.6.0 dompurify: =3.0.6 @@ -41823,23 +41872,23 @@ __metadata: react-immutable-proptypes: 2.2.0 react-immutable-pure-component: ^2.2.0 react-inspector: ^6.0.1 - react-redux: ^8.1.3 + react-redux: ^9.0.4 react-syntax-highlighter: ^15.5.0 - redux: ^4.1.2 + redux: ^5.0.0 redux-immutable: ^4.0.0 remarkable: ^2.0.1 - reselect: ^4.1.8 + reselect: ^5.0.1 serialize-error: ^8.1.0 sha.js: ^2.4.11 - swagger-client: ^3.24.6 + swagger-client: ^3.25.0 url-parse: ^1.5.10 xml: =1.0.1 xml-but-prettier: ^1.0.1 zenscroll: ^4.0.2 peerDependencies: - react: ">=17.0.0" - react-dom: ">=17.0.0" - checksum: 79256d68d56389d089a953f7d6c3a95a67c0b4e2794ae3992ba60adc0312aeb4446d8ba1e20687fcd7708d562e7c15fa733dbcb54cd8f99948ebf4c4513fa042 + react: ">=16.8.0 <19" + react-dom: ">=16.8.0 <19" + checksum: f46acfe503f80566f5a28c5a7a27091f80a764d4602e81ef2dc59466097bdf0e6f6568b866a0a90ab23db39a88fe6f5e4d96b3d5367d05b8ecedb256477fb1cb languageName: node linkType: hard From 92864eb1667f94db29371905fa2c782feda9bd25 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 13:32:07 +0000 Subject: [PATCH 116/152] fix(deps): update dependency tsx to v4.7.0 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9b30c4a836..02252c2b43 100644 --- a/yarn.lock +++ b/yarn.lock @@ -25534,7 +25534,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.18.10, esbuild@npm:~0.18.20": +"esbuild@npm:^0.18.10": version: 0.18.20 resolution: "esbuild@npm:0.18.20" dependencies: @@ -25611,7 +25611,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.19.0": +"esbuild@npm:^0.19.0, esbuild@npm:~0.19.10": version: 0.19.11 resolution: "esbuild@npm:0.19.11" dependencies: @@ -42607,10 +42607,10 @@ __metadata: linkType: hard "tsx@npm:^4.0.0": - version: 4.6.2 - resolution: "tsx@npm:4.6.2" + version: 4.7.0 + resolution: "tsx@npm:4.7.0" dependencies: - esbuild: ~0.18.20 + esbuild: ~0.19.10 fsevents: ~2.3.3 get-tsconfig: ^4.7.2 dependenciesMeta: @@ -42618,7 +42618,7 @@ __metadata: optional: true bin: tsx: dist/cli.mjs - checksum: a9f13bdb67bfb316bbfc92303d8464323ab1b673aa93ea97271c211a8ba7c59274d4b32eeec5ad8fbd0b04260a092a3ad2116abeb6913254ba456010ff685743 + checksum: a3a17fa8a40dbe0aff26fb2bc71a069e568152e0685b0bd9a31ea1091806274ba14882551433ed01efa7eae16f1aa965e2e47f3075ec1e914c42cf5dfce1f924 languageName: node linkType: hard From c1d2ba2d1cefeb012ec076c09221a0fc653f998c Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 14:15:47 +0000 Subject: [PATCH 117/152] chore(deps): update dependency vite to v4.5.2 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7c5f8e3917..10e2ce02d4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -43925,8 +43925,8 @@ __metadata: linkType: hard "vite@npm:^4.4.9": - version: 4.5.1 - resolution: "vite@npm:4.5.1" + version: 4.5.2 + resolution: "vite@npm:4.5.2" dependencies: esbuild: ^0.18.10 fsevents: ~2.3.2 @@ -43960,7 +43960,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 72b3584b3d3b8d14e8a37f0248e47fb8b4d02ab35de5b5a8e5ca8ae55c3be2aab73760dc36edac4fa722de182f78cc492eb44888fcb4a9a0712c4605dad644f9 + checksum: 9d1f84f703c2660aced34deee7f309278ed368880f66e9570ac115c793d91f7fffb80ab19c602b3c8bc1341fe23437d86a3fcca2a9ef82f7ef0cdac5a40d0c86 languageName: node linkType: hard From 326f556251a6611ac1914416f494a62c7c431675 Mon Sep 17 00:00:00 2001 From: Jason Nguyen Date: Fri, 19 Jan 2024 07:56:51 -0700 Subject: [PATCH 118/152] simplify changeset Signed-off-by: Jason Nguyen --- .changeset/seven-dots-serve.md | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/.changeset/seven-dots-serve.md b/.changeset/seven-dots-serve.md index f48ab1f19c..80919b5d28 100644 --- a/.changeset/seven-dots-serve.md +++ b/.changeset/seven-dots-serve.md @@ -2,33 +2,4 @@ '@backstage/plugin-catalog': minor --- -Exported `CatalogTable.defaultColumnsFunc` to create a seam for defining the columns in `` of some Kinds while using the default columns for the others. -This is useful for defining the columns of a custom Kind or to redefine the columns for a built-in Kind. - -```diff -// packages/app/src/App.tsx - -import { - CatalogEntityPage, - CatalogIndexPage, - catalogPlugin, -+ CatalogTable, -+ CatalogTableColumnsFunc, -} from '@backstage/plugin-catalog'; - -+ const myColumnsFunc: CatalogTableColumnsFunc = (entityListContext) => { -+ if (entityListContext.filters.kind?.value === 'MyKind') { -+ return [ -+ CatalogTable.columns.createNameColumn(), -+ CatalogTable.columns.createOwnerColumn() -+ ]; -+ } -+ -+ return CatalogTable.defaultColumnsFunc(entityListContext); -+ }; - -... - -- } /> -+ } /> -``` +Exported `CatalogTable.defaultColumnsFunc` for defining the columns in `` of some Kinds while using the default columns for the others. From 2067689453dee0a61ebcbda60e9eba78cb95cf01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 19 Jan 2024 16:11:36 +0100 Subject: [PATCH 119/152] fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/hot-tips-doubt.md | 5 +++++ packages/backend-openapi-utils/api-report.md | 6 +++--- packages/backend-openapi-utils/src/types/common.ts | 4 ++-- packages/backend-openapi-utils/src/types/params.ts | 4 ++-- 4 files changed, 12 insertions(+), 7 deletions(-) create mode 100644 .changeset/hot-tips-doubt.md diff --git a/.changeset/hot-tips-doubt.md b/.changeset/hot-tips-doubt.md new file mode 100644 index 0000000000..464ddec302 --- /dev/null +++ b/.changeset/hot-tips-doubt.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-openapi-utils': patch +--- + +Internal updates due to `json-schema-to-ts` diff --git a/packages/backend-openapi-utils/api-report.md b/packages/backend-openapi-utils/api-report.md index 0d78ec9671..8e702b8c23 100644 --- a/packages/backend-openapi-utils/api-report.md +++ b/packages/backend-openapi-utils/api-report.md @@ -7,7 +7,7 @@ import type { ContentObject } from 'openapi3-ts'; import type core from 'express-serve-static-core'; import { Express as Express_2 } from 'express'; import { FromSchema } from 'json-schema-to-ts'; -import { JSONSchema7 } from 'json-schema-to-ts'; +import { JSONSchema } from 'json-schema-to-ts'; import { middleware } from 'express-openapi-validator'; import type { OpenAPIObject } from 'openapi3-ts'; import type { ParameterObject } from 'openapi3-ts'; @@ -62,7 +62,7 @@ type ComponentTypes = Extract< // @public (undocumented) type ConvertAll> = { - [Index in keyof T]: T[Index] extends JSONSchema7 + [Index in keyof T]: T[Index] extends JSONSchema ? FromSchema : T[Index]; } & { @@ -463,7 +463,7 @@ type ParameterSchema< Schema extends ImmutableParameterObject['schema'], > = SchemaRef extends infer R ? R extends ImmutableSchemaObject - ? R extends JSONSchema7 + ? R extends JSONSchema ? FromSchema : never : never diff --git a/packages/backend-openapi-utils/src/types/common.ts b/packages/backend-openapi-utils/src/types/common.ts index f9dedf2f84..a43ad4449f 100644 --- a/packages/backend-openapi-utils/src/types/common.ts +++ b/packages/backend-openapi-utils/src/types/common.ts @@ -18,7 +18,7 @@ * Pulled from https://github.com/varanauskas/oatx. */ -import { FromSchema, JSONSchema7 } from 'json-schema-to-ts'; +import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { ImmutableContentObject, ImmutableOpenAPIObject, @@ -221,7 +221,7 @@ export type TuplifyUnion< * @public */ export type ConvertAll> = { - [Index in keyof T]: T[Index] extends JSONSchema7 + [Index in keyof T]: T[Index] extends JSONSchema ? FromSchema : T[Index]; } & { length: T['length'] }; diff --git a/packages/backend-openapi-utils/src/types/params.ts b/packages/backend-openapi-utils/src/types/params.ts index 208cbd4e9a..969657bac7 100644 --- a/packages/backend-openapi-utils/src/types/params.ts +++ b/packages/backend-openapi-utils/src/types/params.ts @@ -36,7 +36,7 @@ import { SchemaRef, ValueOf, } from './common'; -import { FromSchema, JSONSchema7 } from 'json-schema-to-ts'; +import { FromSchema, JSONSchema } from 'json-schema-to-ts'; /** * @public @@ -96,7 +96,7 @@ export type ParameterSchema< Schema extends ImmutableParameterObject['schema'], > = SchemaRef extends infer R ? R extends ImmutableSchemaObject - ? R extends JSONSchema7 + ? R extends JSONSchema ? FromSchema : never : never From 551616a7f694a25cb639b7dce78cd0bef6aaf09e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 15:58:39 +0000 Subject: [PATCH 120/152] fix(deps): update opentelemetry-js monorepo Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- packages/backend/package.json | 2 +- yarn.lock | 58 +++++++++++++++++------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index cdd0a548c8..4223a92a28 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -82,7 +82,7 @@ "@gitbeaker/node": "^35.1.0", "@octokit/rest": "^19.0.3", "@opentelemetry/api": "^1.4.1", - "@opentelemetry/exporter-prometheus": "^0.45.0", + "@opentelemetry/exporter-prometheus": "^0.47.0", "@opentelemetry/sdk-metrics": "^1.13.0", "azure-devops-node-api": "^12.0.0", "better-sqlite3": "^9.0.0", diff --git a/yarn.lock b/yarn.lock index 8501156914..ede8d0e77a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14012,59 +14012,59 @@ __metadata: languageName: node linkType: hard -"@opentelemetry/core@npm:1.18.1": - version: 1.18.1 - resolution: "@opentelemetry/core@npm:1.18.1" +"@opentelemetry/core@npm:1.20.0": + version: 1.20.0 + resolution: "@opentelemetry/core@npm:1.20.0" dependencies: - "@opentelemetry/semantic-conventions": 1.18.1 + "@opentelemetry/semantic-conventions": 1.20.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.8.0" - checksum: dfb3181836ce04d2e983c0e8382e4bd0228ec42280e0a3f5330e2742903c0fb1db0efc2792479d27f928533a386f163c2e0fce2a2f45b05e66b2809d268915dc + checksum: 90a01430902926894a3d7ac6a5bd68b3bcb59aede568d08ca09abdd6d9cc46e9423f2fc65a496c7a8c13a2d3df259586f1a0d500fac6271524f189247e97d8fa languageName: node linkType: hard -"@opentelemetry/exporter-prometheus@npm:^0.45.0": - version: 0.45.1 - resolution: "@opentelemetry/exporter-prometheus@npm:0.45.1" +"@opentelemetry/exporter-prometheus@npm:^0.47.0": + version: 0.47.0 + resolution: "@opentelemetry/exporter-prometheus@npm:0.47.0" dependencies: - "@opentelemetry/core": 1.18.1 - "@opentelemetry/resources": 1.18.1 - "@opentelemetry/sdk-metrics": 1.18.1 + "@opentelemetry/core": 1.20.0 + "@opentelemetry/resources": 1.20.0 + "@opentelemetry/sdk-metrics": 1.20.0 peerDependencies: "@opentelemetry/api": ^1.3.0 - checksum: 033fda6759610350d7f11c91d66a2c0e3cc7ac1182267e6ed4aba2fb90add66e344925e6ab89db76ff91c7fa67c82c0c4099657c5a9b68c4140ca4d0a3c1abac + checksum: 7b9af3e1912f4ef796f29d34e6a7a35cc51c6c4bef1252678c58180ff4e90a121663acbaf5c4ff90a197258e2ab596f7b873364ca80ea3096bc675e97df5bead languageName: node linkType: hard -"@opentelemetry/resources@npm:1.18.1": - version: 1.18.1 - resolution: "@opentelemetry/resources@npm:1.18.1" +"@opentelemetry/resources@npm:1.20.0": + version: 1.20.0 + resolution: "@opentelemetry/resources@npm:1.20.0" dependencies: - "@opentelemetry/core": 1.18.1 - "@opentelemetry/semantic-conventions": 1.18.1 + "@opentelemetry/core": 1.20.0 + "@opentelemetry/semantic-conventions": 1.20.0 peerDependencies: "@opentelemetry/api": ">=1.0.0 <1.8.0" - checksum: b3311734802dca77eb379331ae7c409867ffe82cf17bca0e362de49e2d28313441a0177f62fc8c6c1f605bfc82c50e80ac065f17d81fc4fa131afff146db6432 + checksum: 22b851789bccd778901ea095f51006b57b0059ddeb0ff153ce548c92cd83202af5739fc9dd5c1d2e4640c8eacf7a08ffd24d5ae30f2ebae60855b026ecb87d33 languageName: node linkType: hard -"@opentelemetry/sdk-metrics@npm:1.18.1, @opentelemetry/sdk-metrics@npm:^1.13.0": - version: 1.18.1 - resolution: "@opentelemetry/sdk-metrics@npm:1.18.1" +"@opentelemetry/sdk-metrics@npm:1.20.0, @opentelemetry/sdk-metrics@npm:^1.13.0": + version: 1.20.0 + resolution: "@opentelemetry/sdk-metrics@npm:1.20.0" dependencies: - "@opentelemetry/core": 1.18.1 - "@opentelemetry/resources": 1.18.1 + "@opentelemetry/core": 1.20.0 + "@opentelemetry/resources": 1.20.0 lodash.merge: ^4.6.2 peerDependencies: "@opentelemetry/api": ">=1.3.0 <1.8.0" - checksum: ed2b87ea6380adc04bf50955cf7ae65220c6f35292b7d0535562c2e83d8b672306103c48fd45f3cc03ab3d640a8d2c6e367b265f5f4b0cbe03b0f51eb85759c7 + checksum: 6bd2d77ee14d85d9894ba08715969bc18d75582a53d9d31e2fed6a77f46c32e5fbae6daf6f1c32446a3cc5169620b7f29da27ca006d7c16750f9d83966fd864e languageName: node linkType: hard -"@opentelemetry/semantic-conventions@npm:1.18.1": - version: 1.18.1 - resolution: "@opentelemetry/semantic-conventions@npm:1.18.1" - checksum: b60c008c01067c0e8f130ab5d61f5207c85b6db08fa926f629c854ab9917ca93fbabd7ae8d1586f9f82e3b29706b0444ded9d6781f7fb7a003eeb27d89af468f +"@opentelemetry/semantic-conventions@npm:1.20.0": + version: 1.20.0 + resolution: "@opentelemetry/semantic-conventions@npm:1.20.0" + checksum: c71d466ab37158c57ff15a8fcb55682c812993b9d0d8e05114094abbe70f5ce2735909d70b749fddbc0621e5cdae19fecbb4e087034f606bce98e3bf9c74613e languageName: node linkType: hard @@ -26633,7 +26633,7 @@ __metadata: "@gitbeaker/node": ^35.1.0 "@octokit/rest": ^19.0.3 "@opentelemetry/api": ^1.4.1 - "@opentelemetry/exporter-prometheus": ^0.45.0 + "@opentelemetry/exporter-prometheus": ^0.47.0 "@opentelemetry/sdk-metrics": ^1.13.0 "@types/dockerode": ^3.3.0 "@types/express": ^4.17.6 From 88802efe66675582911b7273b297d20f23917c61 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:53:38 +0000 Subject: [PATCH 121/152] fix(deps): update dependency @smithy/node-http-handler to v2.3.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 62 +++++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/yarn.lock b/yarn.lock index ede8d0e77a..4992eb3807 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15347,13 +15347,13 @@ __metadata: languageName: node linkType: hard -"@smithy/abort-controller@npm:^2.1.0": - version: 2.1.0 - resolution: "@smithy/abort-controller@npm:2.1.0" +"@smithy/abort-controller@npm:^2.1.0, @smithy/abort-controller@npm:^2.1.1": + version: 2.1.1 + resolution: "@smithy/abort-controller@npm:2.1.1" dependencies: - "@smithy/types": ^2.9.0 + "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 52ad57706997dd1fabeb5b4d1f43d65403f31f8c7bf2fd6addb84b8132b406763574911499ab757be1c0c7329c06c574174750f17383507cfb05e1df05b26882 + checksum: 4bfad0d6b3a75bd1e6f997aa41cc9d8ba8bfdf548cfe703553ad7b42f0bf3e06b595d584be7b9ab90d2e3b22aacad94c02c32e21bea96e46933443f09c59523a languageName: node linkType: hard @@ -15627,15 +15627,15 @@ __metadata: linkType: hard "@smithy/node-http-handler@npm:^2.1.7, @smithy/node-http-handler@npm:^2.3.0": - version: 2.3.0 - resolution: "@smithy/node-http-handler@npm:2.3.0" + version: 2.3.1 + resolution: "@smithy/node-http-handler@npm:2.3.1" dependencies: - "@smithy/abort-controller": ^2.1.0 - "@smithy/protocol-http": ^3.1.0 - "@smithy/querystring-builder": ^2.1.0 - "@smithy/types": ^2.9.0 + "@smithy/abort-controller": ^2.1.1 + "@smithy/protocol-http": ^3.1.1 + "@smithy/querystring-builder": ^2.1.1 + "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 8f431d7fa4e07908ecfd58d13721b5adc41519415e9ea58d104ab44da008c0244fd551a6fddc27e9bac1ca8ba98d3f117b37c379c096e5038ff70b723466dce6 + checksum: e6a514098f44cfc962318b15df79bb5e9de7fffe883fe073965879b2cf2436726709b5be14262871794104272e8506f793f8e77b8bf5b36398714a3a51512516 languageName: node linkType: hard @@ -15649,24 +15649,24 @@ __metadata: languageName: node linkType: hard -"@smithy/protocol-http@npm:^3.1.0": - version: 3.1.0 - resolution: "@smithy/protocol-http@npm:3.1.0" +"@smithy/protocol-http@npm:^3.1.0, @smithy/protocol-http@npm:^3.1.1": + version: 3.1.1 + resolution: "@smithy/protocol-http@npm:3.1.1" dependencies: - "@smithy/types": ^2.9.0 + "@smithy/types": ^2.9.1 tslib: ^2.5.0 - checksum: 3aae910192708bfd5cc3de85dc28c2eb7f6b425d6edc2b930f769330bc4912f8e7ff2a5dc8b9dd98614d1f9fa8d4f64a9f9eb45e54c4da94e29612accc3218fc + checksum: a5be1c5b5bff18c5a35c23870e1ffa38da33e56f93bdd8f26c615f4c0d2d3e1effffe441e756c0b0ba3aad2dd0845332f634702bf8455ed865a04eebfef1329b languageName: node linkType: hard -"@smithy/querystring-builder@npm:^2.1.0": - version: 2.1.0 - resolution: "@smithy/querystring-builder@npm:2.1.0" +"@smithy/querystring-builder@npm:^2.1.0, @smithy/querystring-builder@npm:^2.1.1": + version: 2.1.1 + resolution: "@smithy/querystring-builder@npm:2.1.1" dependencies: - "@smithy/types": ^2.9.0 - "@smithy/util-uri-escape": ^2.1.0 + "@smithy/types": ^2.9.1 + "@smithy/util-uri-escape": ^2.1.1 tslib: ^2.5.0 - checksum: 307cfec9c5765b9aca10318913cd91513d323895d9cfb863309b49fe634dfdb3ebf74ef3dc17e3d4aa60d1c4e8f48a63cfd11c2b2099b36cd4d9f53198e933a0 + checksum: b8623c7ef6d19fb21c41bfda29cce9c673ac501914085b39642ff5a72cf5742b19cd9de1a1851d13f2e1bbfc2e9522070b5ca32ed906aacf93f732a56e76098a languageName: node linkType: hard @@ -15738,12 +15738,12 @@ __metadata: languageName: node linkType: hard -"@smithy/types@npm:^2.9.0": - version: 2.9.0 - resolution: "@smithy/types@npm:2.9.0" +"@smithy/types@npm:^2.9.0, @smithy/types@npm:^2.9.1": + version: 2.9.1 + resolution: "@smithy/types@npm:2.9.1" dependencies: tslib: ^2.5.0 - checksum: 74d59ddcc2eca419c493b99d09f912e72511d088df0c6b6548df876dd49407b3cbb68550c9d6fa1d03d65f96bb099c450bdad1b4724d52144ca65f547850a4bb + checksum: 8570affb4abb5d0ead57293977fc915d44be481120defcabb87a3fb1c7b5d2501b117835eca357b5d54ea4bbee08032f9dc3d909ecbf0abb0cec2ca9678ae7bd languageName: node linkType: hard @@ -15890,12 +15890,12 @@ __metadata: languageName: node linkType: hard -"@smithy/util-uri-escape@npm:^2.1.0": - version: 2.1.0 - resolution: "@smithy/util-uri-escape@npm:2.1.0" +"@smithy/util-uri-escape@npm:^2.1.0, @smithy/util-uri-escape@npm:^2.1.1": + version: 2.1.1 + resolution: "@smithy/util-uri-escape@npm:2.1.1" dependencies: tslib: ^2.5.0 - checksum: 11aca3e04a57428266fdb09f408f21bb51d5fa0b6b31ceb5769a63c670ad8f24c95bcbc1d6046d625b7992293e86f46207d1018233130e9f4c4e97d236ff9cd8 + checksum: 822ed7390e28d5c7b8dab5e5c5a8de998e0778220137962a71b47b2d8900289d48a3a2c9945e68e1cac921d43f61660045e7fdffe8df9e63004575fcf2aa99b2 languageName: node linkType: hard From cd1a1bae673fcb28af0c49d32efb248dfdafa4f0 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:54:00 +0000 Subject: [PATCH 122/152] chore(deps): update actions/cache action to v4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- .github/workflows/uffizzi-preview.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/uffizzi-preview.yaml b/.github/workflows/uffizzi-preview.yaml index 2c1c353847..47029df5e0 100644 --- a/.github/workflows/uffizzi-preview.yaml +++ b/.github/workflows/uffizzi-preview.yaml @@ -76,7 +76,7 @@ jobs: - name: Cache Manifests File if: ${{ steps.event.outputs.ACTION != 'closed' }} - uses: actions/cache@v3.3.3 + uses: actions/cache@v4.0.0 with: path: manifests.rendered.yml key: ${{ steps.hash.outputs.MANIFESTS_FILE_HASH }} @@ -140,7 +140,7 @@ jobs: - name: Fetch cached Manifests File id: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: manifests.rendered.yml key: ${{ needs.cache-manifests-file.outputs.manifests-cache-key }} From 7fe1460bb59e422569448f30ec69bbcb8c4626e6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 19 Jan 2024 18:25:33 +0000 Subject: [PATCH 123/152] fix(deps): update docusaurus monorepo to v0.0.0-5809 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- microsite/package.json | 8 +- microsite/yarn.lock | 2497 +++++++++++++++------------------------- 2 files changed, 945 insertions(+), 1560 deletions(-) diff --git a/microsite/package.json b/microsite/package.json index 53309fb358..637327f83e 100644 --- a/microsite/package.json +++ b/microsite/package.json @@ -18,7 +18,7 @@ "docusaurus": "docusaurus" }, "devDependencies": { - "@docusaurus/module-type-aliases": "0.0.0-5703", + "@docusaurus/module-type-aliases": "0.0.0-5809", "@spotify/prettier-config": "^14.0.0", "@tsconfig/docusaurus": "^2.0.0", "@types/luxon": "^3.0.0", @@ -30,9 +30,9 @@ }, "prettier": "@spotify/prettier-config", "dependencies": { - "@docusaurus/core": "0.0.0-5703", - "@docusaurus/plugin-client-redirects": "0.0.0-5703", - "@docusaurus/preset-classic": "0.0.0-5703", + "@docusaurus/core": "0.0.0-5809", + "@docusaurus/plugin-client-redirects": "0.0.0-5809", + "@docusaurus/preset-classic": "0.0.0-5809", "@swc/core": "^1.3.46", "clsx": "^2.0.0", "docusaurus-plugin-sass": "^0.2.3", diff --git a/microsite/yarn.lock b/microsite/yarn.lock index a2489da001..1841fcb678 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -197,55 +197,55 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.8.3": - version: 7.22.13 - resolution: "@babel/code-frame@npm:7.22.13" +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.16.0, @babel/code-frame@npm:^7.22.13, @babel/code-frame@npm:^7.23.5, @babel/code-frame@npm:^7.8.3": + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" dependencies: - "@babel/highlight": ^7.22.13 + "@babel/highlight": ^7.23.4 chalk: ^2.4.2 - checksum: 22e342c8077c8b77eeb11f554ecca2ba14153f707b85294fcf6070b6f6150aae88a7b7436dd88d8c9289970585f3fe5b9b941c5aa3aa26a6d5a8ef3f292da058 + checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a languageName: node linkType: hard -"@babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9": - version: 7.22.20 - resolution: "@babel/compat-data@npm:7.22.20" - checksum: efedd1d18878c10fde95e4d82b1236a9aba41395ef798cbb651f58dbf5632dbff475736c507b8d13d4c8f44809d41c0eb2ef0d694283af9ba5dd8339b6dab451 +"@babel/compat-data@npm:^7.22.20, @babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.22.9, @babel/compat-data@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/compat-data@npm:7.23.5" + checksum: 06ce244cda5763295a0ea924728c09bae57d35713b675175227278896946f922a63edf803c322f855a3878323d48d0255a2a3023409d2a123483c8a69ebb4744 languageName: node linkType: hard -"@babel/core@npm:^7.19.6, @babel/core@npm:^7.22.9": - version: 7.23.0 - resolution: "@babel/core@npm:7.23.0" +"@babel/core@npm:^7.19.6, @babel/core@npm:^7.23.3": + version: 7.23.7 + resolution: "@babel/core@npm:7.23.7" dependencies: "@ampproject/remapping": ^2.2.0 - "@babel/code-frame": ^7.22.13 - "@babel/generator": ^7.23.0 - "@babel/helper-compilation-targets": ^7.22.15 - "@babel/helper-module-transforms": ^7.23.0 - "@babel/helpers": ^7.23.0 - "@babel/parser": ^7.23.0 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 + "@babel/helper-compilation-targets": ^7.23.6 + "@babel/helper-module-transforms": ^7.23.3 + "@babel/helpers": ^7.23.7 + "@babel/parser": ^7.23.6 "@babel/template": ^7.22.15 - "@babel/traverse": ^7.23.0 - "@babel/types": ^7.23.0 + "@babel/traverse": ^7.23.7 + "@babel/types": ^7.23.6 convert-source-map: ^2.0.0 debug: ^4.1.0 gensync: ^1.0.0-beta.2 json5: ^2.2.3 semver: ^6.3.1 - checksum: cebd9b48dbc970a7548522f207f245c69567e5ea17ebb1a4e4de563823cf20a01177fe8d2fe19b6e1461361f92fa169fd0b29f8ee9d44eeec84842be1feee5f2 + checksum: 32d5bf73372a47429afaae9adb0af39e47bcea6a831c4b5dcbb4791380cda6949cb8cb1a2fea8b60bb1ebe189209c80e333903df1fa8e9dcb04798c0ce5bf59e languageName: node linkType: hard -"@babel/generator@npm:^7.22.9, @babel/generator@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/generator@npm:7.23.0" +"@babel/generator@npm:^7.23.3, @babel/generator@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/generator@npm:7.23.6" dependencies: - "@babel/types": ^7.23.0 + "@babel/types": ^7.23.6 "@jridgewell/gen-mapping": ^0.3.2 "@jridgewell/trace-mapping": ^0.3.17 jsesc: ^2.5.1 - checksum: 8efe24adad34300f1f8ea2add420b28171a646edc70f2a1b3e1683842f23b8b7ffa7e35ef0119294e1901f45bfea5b3dc70abe1f10a1917ccdfb41bed69be5f1 + checksum: 1a1a1c4eac210f174cd108d479464d053930a812798e09fee069377de39a893422df5b5b146199ead7239ae6d3a04697b45fc9ac6e38e0f6b76374390f91fc6c languageName: node linkType: hard @@ -267,16 +267,16 @@ __metadata: languageName: node linkType: hard -"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6": - version: 7.22.15 - resolution: "@babel/helper-compilation-targets@npm:7.22.15" +"@babel/helper-compilation-targets@npm:^7.22.15, @babel/helper-compilation-targets@npm:^7.22.5, @babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/helper-compilation-targets@npm:7.23.6" dependencies: - "@babel/compat-data": ^7.22.9 - "@babel/helper-validator-option": ^7.22.15 - browserslist: ^4.21.9 + "@babel/compat-data": ^7.23.5 + "@babel/helper-validator-option": ^7.23.5 + browserslist: ^4.22.2 lru-cache: ^5.1.1 semver: ^6.3.1 - checksum: ce85196769e091ae54dd39e4a80c2a9df1793da8588e335c383d536d54f06baf648d0a08fc873044f226398c4ded15c4ae9120ee18e7dfd7c639a68e3cdc9980 + checksum: c630b98d4527ac8fe2c58d9a06e785dfb2b73ec71b7c4f2ddf90f814b5f75b547f3c015f110a010fd31f76e3864daaf09f3adcd2f6acdbfb18a8de3a48717590 languageName: node linkType: hard @@ -371,9 +371,9 @@ __metadata: languageName: node linkType: hard -"@babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/helper-module-transforms@npm:7.23.0" +"@babel/helper-module-transforms@npm:^7.22.5, @babel/helper-module-transforms@npm:^7.23.0, @babel/helper-module-transforms@npm:^7.23.3": + version: 7.23.3 + resolution: "@babel/helper-module-transforms@npm:7.23.3" dependencies: "@babel/helper-environment-visitor": ^7.22.20 "@babel/helper-module-imports": ^7.22.15 @@ -382,7 +382,7 @@ __metadata: "@babel/helper-validator-identifier": ^7.22.20 peerDependencies: "@babel/core": ^7.0.0 - checksum: 6e2afffb058cf3f8ce92f5116f710dda4341c81cfcd872f9a0197ea594f7ce0ab3cb940b0590af2fe99e60d2e5448bfba6bca8156ed70a2ed4be2adc8586c891 + checksum: 5d0895cfba0e16ae16f3aa92fee108517023ad89a855289c4eb1d46f7aef4519adf8e6f971e1d55ac20c5461610e17213f1144097a8f932e768a9132e2278d71 languageName: node linkType: hard @@ -455,10 +455,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.22.5": - version: 7.22.5 - resolution: "@babel/helper-string-parser@npm:7.22.5" - checksum: 836851ca5ec813077bbb303acc992d75a360267aa3b5de7134d220411c852a6f17de7c0d0b8c8dcc0f567f67874c00f4528672b2a4f1bc978a3ada64c8c78467 +"@babel/helper-string-parser@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/helper-string-parser@npm:7.23.4" + checksum: c0641144cf1a7e7dc93f3d5f16d5327465b6cf5d036b48be61ecba41e1eece161b48f46b7f960951b67f8c3533ce506b16dece576baef4d8b3b49f8c65410f90 languageName: node linkType: hard @@ -469,10 +469,10 @@ __metadata: languageName: node linkType: hard -"@babel/helper-validator-option@npm:^7.22.15": - version: 7.22.15 - resolution: "@babel/helper-validator-option@npm:7.22.15" - checksum: 68da52b1e10002a543161494c4bc0f4d0398c8fdf361d5f7f4272e95c45d5b32d974896d44f6a0ea7378c9204988879d73613ca683e13bd1304e46d25ff67a8d +"@babel/helper-validator-option@npm:^7.22.15, @babel/helper-validator-option@npm:^7.23.5": + version: 7.23.5 + resolution: "@babel/helper-validator-option@npm:7.23.5" + checksum: 537cde2330a8aede223552510e8a13e9c1c8798afee3757995a7d4acae564124fe2bf7e7c3d90d62d3657434a74340a274b3b3b1c6f17e9a2be1f48af29cb09e languageName: node linkType: hard @@ -487,34 +487,34 @@ __metadata: languageName: node linkType: hard -"@babel/helpers@npm:^7.23.0": - version: 7.23.1 - resolution: "@babel/helpers@npm:7.23.1" +"@babel/helpers@npm:^7.23.7": + version: 7.23.8 + resolution: "@babel/helpers@npm:7.23.8" dependencies: "@babel/template": ^7.22.15 - "@babel/traverse": ^7.23.0 - "@babel/types": ^7.23.0 - checksum: acfc345102045c24ea2a4d60e00dcf8220e215af3add4520e2167700661338e6a80bd56baf44bb764af05ec6621101c9afc315dc107e18c61fa6da8acbdbb893 + "@babel/traverse": ^7.23.7 + "@babel/types": ^7.23.6 + checksum: 8b522d527921f8df45a983dc7b8e790c021250addf81ba7900ba016e165442a527348f6f877aa55e1debb3eef9e860a334b4e8d834e6c9b438ed61a63d9a7ad4 languageName: node linkType: hard -"@babel/highlight@npm:^7.22.13": - version: 7.22.20 - resolution: "@babel/highlight@npm:7.22.20" +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" dependencies: "@babel/helper-validator-identifier": ^7.22.20 chalk: ^2.4.2 js-tokens: ^4.0.0 - checksum: 84bd034dca309a5e680083cd827a766780ca63cef37308404f17653d32366ea76262bd2364b2d38776232f2d01b649f26721417d507e8b4b6da3e4e739f6d134 + checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 languageName: node linkType: hard -"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.22.7, @babel/parser@npm:^7.23.0": - version: 7.23.0 - resolution: "@babel/parser@npm:7.23.0" +"@babel/parser@npm:^7.22.15, @babel/parser@npm:^7.23.6": + version: 7.23.6 + resolution: "@babel/parser@npm:7.23.6" bin: parser: ./bin/babel-parser.js - checksum: 453fdf8b9e2c2b7d7b02139e0ce003d1af21947bbc03eb350fb248ee335c9b85e4ab41697ddbdd97079698de825a265e45a0846bb2ed47a2c7c1df833f42a354 + checksum: 140801c43731a6c41fd193f5c02bc71fd647a0360ca616b23d2db8be4b9739b9f951a03fc7c2db4f9b9214f4b27c1074db0f18bc3fa653783082d5af7c8860d5 languageName: node linkType: hard @@ -1593,7 +1593,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4": +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.8.4": version: 7.23.1 resolution: "@babel/runtime@npm:7.23.1" dependencies: @@ -1613,32 +1613,32 @@ __metadata: languageName: node linkType: hard -"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.23.0": - version: 7.23.2 - resolution: "@babel/traverse@npm:7.23.2" +"@babel/traverse@npm:^7.22.8, @babel/traverse@npm:^7.23.7": + version: 7.23.7 + resolution: "@babel/traverse@npm:7.23.7" dependencies: - "@babel/code-frame": ^7.22.13 - "@babel/generator": ^7.23.0 + "@babel/code-frame": ^7.23.5 + "@babel/generator": ^7.23.6 "@babel/helper-environment-visitor": ^7.22.20 "@babel/helper-function-name": ^7.23.0 "@babel/helper-hoist-variables": ^7.22.5 "@babel/helper-split-export-declaration": ^7.22.6 - "@babel/parser": ^7.23.0 - "@babel/types": ^7.23.0 - debug: ^4.1.0 + "@babel/parser": ^7.23.6 + "@babel/types": ^7.23.6 + debug: ^4.3.1 globals: ^11.1.0 - checksum: 26a1eea0dde41ab99dde8b9773a013a0dc50324e5110a049f5d634e721ff08afffd54940b3974a20308d7952085ac769689369e9127dea655f868c0f6e1ab35d + checksum: d4a7afb922361f710efc97b1e25ec343fab8b2a4ddc81ca84f9a153f22d4482112cba8f263774be8d297918b6c4767c7a98988ab4e53ac73686c986711dd002e languageName: node linkType: hard -"@babel/types@npm:^7.20.0, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": - version: 7.23.0 - resolution: "@babel/types@npm:7.23.0" +"@babel/types@npm:^7.20.0, @babel/types@npm:^7.22.15, @babel/types@npm:^7.22.19, @babel/types@npm:^7.22.5, @babel/types@npm:^7.23.0, @babel/types@npm:^7.23.6, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.23.6 + resolution: "@babel/types@npm:7.23.6" dependencies: - "@babel/helper-string-parser": ^7.22.5 + "@babel/helper-string-parser": ^7.23.4 "@babel/helper-validator-identifier": ^7.22.20 to-fast-properties: ^2.0.0 - checksum: 215fe04bd7feef79eeb4d33374b39909ce9cad1611c4135a4f7fdf41fe3280594105af6d7094354751514625ea92d0875aba355f53e86a92600f290e77b0e604 + checksum: 68187dbec0d637f79bc96263ac95ec8b06d424396678e7e225492be866414ce28ebc918a75354d4c28659be6efe30020b4f0f6df81cc418a2d30645b690a8de0 languageName: node linkType: hard @@ -1689,12 +1689,12 @@ __metadata: languageName: node linkType: hard -"@docusaurus/core@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/core@npm:0.0.0-5703" +"@docusaurus/core@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/core@npm:0.0.0-5809" dependencies: - "@babel/core": ^7.22.9 - "@babel/generator": ^7.22.9 + "@babel/core": ^7.23.3 + "@babel/generator": ^7.23.3 "@babel/plugin-syntax-dynamic-import": ^7.8.3 "@babel/plugin-transform-runtime": ^7.22.9 "@babel/preset-env": ^7.22.9 @@ -1703,13 +1703,13 @@ __metadata: "@babel/runtime": ^7.22.6 "@babel/runtime-corejs3": ^7.22.6 "@babel/traverse": ^7.22.8 - "@docusaurus/cssnano-preset": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/mdx-loader": 0.0.0-5703 + "@docusaurus/cssnano-preset": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/mdx-loader": 0.0.0-5809 "@docusaurus/react-loadable": 5.5.2 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 "@slorber/static-site-generator-webpack-plugin": ^4.0.7 "@svgr/webpack": ^6.5.1 autoprefixer: ^10.4.14 @@ -1736,7 +1736,6 @@ __metadata: html-minifier-terser: ^7.2.0 html-tags: ^3.3.1 html-webpack-plugin: ^5.5.3 - import-fresh: ^3.3.0 leven: ^3.1.0 lodash: ^4.17.21 mini-css-extract-plugin: ^2.7.6 @@ -1758,7 +1757,6 @@ __metadata: tslib: ^2.6.0 update-notifier: ^6.0.2 url-loader: ^4.1.1 - wait-on: ^7.0.1 webpack: ^5.88.1 webpack-bundle-analyzer: ^4.9.0 webpack-dev-server: ^4.15.1 @@ -1769,76 +1767,73 @@ __metadata: react-dom: ^18.0.0 bin: docusaurus: bin/docusaurus.mjs - checksum: 0e2912a09964778db626dc190317badb074f2f5e09157b8070f281eff69ce2e89846529584ce9bf6e6898293409c353adc80572484aceda1d2136aa2b0b334ca + checksum: 4b448bc4fc5df42de82afb7fd6e05aa1faec9edd4b534f982bd36efadc75e10f5fd94275286ee28be0c849f22504f4441a9918d179bfe1a0783f341bb6ccac3e languageName: node linkType: hard -"@docusaurus/cssnano-preset@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/cssnano-preset@npm:0.0.0-5703" +"@docusaurus/cssnano-preset@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/cssnano-preset@npm:0.0.0-5809" dependencies: cssnano-preset-advanced: ^5.3.10 postcss: ^8.4.26 postcss-sort-media-queries: ^4.4.1 tslib: ^2.6.0 - checksum: 5f6e2a69765fb18de7d8f5e3037bf4f08695034762fca092229ae0ba9a40e9b76048914ffbf6b1d339d496e23580f47aa419f51f446c98fca0ba8d500a06e396 + checksum: 92ec22fb1dfcc8d85cd55f9f365a22809ea65d2181ace4610889e804a322c9fccf983eacbf56b8d848515d505eb1cdc9a8ac6d8d0409dd764d6cd784d029826e languageName: node linkType: hard -"@docusaurus/logger@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/logger@npm:0.0.0-5703" +"@docusaurus/logger@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/logger@npm:0.0.0-5809" dependencies: chalk: ^4.1.2 tslib: ^2.6.0 - checksum: 9ee30b054388e5a618876316df50b73a7c4c8300ce205ace3c09f689a1a38b85d51891a1f0613341fd038a0d06290f0675d64c3e9e5b87f1eee3cb76fd45b55c + checksum: 818f0493308a3261efbd098ca7b37cc76b15280e133f5aa7d8e344e8e39b54e8d07cd011f494b964be6ef39740ec0edf3d4829361e40fb1dbb3027599fc08fdd languageName: node linkType: hard -"@docusaurus/mdx-loader@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/mdx-loader@npm:0.0.0-5703" +"@docusaurus/mdx-loader@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/mdx-loader@npm:0.0.0-5809" dependencies: - "@babel/parser": ^7.22.7 - "@babel/traverse": ^7.22.8 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 - "@mdx-js/mdx": ^2.1.5 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 + "@mdx-js/mdx": ^3.0.0 "@slorber/remark-comment": ^1.0.0 escape-html: ^1.0.3 - estree-util-value-to-estree: ^2.1.0 + estree-util-value-to-estree: ^3.0.1 file-loader: ^6.2.0 fs-extra: ^11.1.1 - hastscript: ^7.1.0 image-size: ^1.0.2 - mdast-util-mdx: ^2.0.0 - mdast-util-to-string: ^3.2.0 - rehype-raw: ^6.1.1 - remark-directive: ^2.0.1 - remark-emoji: ^2.2.0 + mdast-util-mdx: ^3.0.0 + mdast-util-to-string: ^4.0.0 + rehype-raw: ^7.0.0 + remark-directive: ^3.0.0 + remark-emoji: ^4.0.0 remark-frontmatter: ^5.0.0 - remark-gfm: ^3.0.1 + remark-gfm: ^4.0.0 stringify-object: ^3.3.0 tslib: ^2.6.0 - unified: ^10.1.2 - unist-util-visit: ^2.0.3 + unified: ^11.0.3 + unist-util-visit: ^5.0.0 url-loader: ^4.1.1 - vfile: ^5.3.7 + vfile: ^6.0.1 webpack: ^5.88.1 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: d57b08d7d676b9246be129af06a803fc55162359578e486456cb96a2372809c2093ef4115c926cf8e8be35e285b35d3ea444d808d46afb78edf66b49f58474fb + checksum: d92126b7b32aa5b73c9556e9e04ddf4055992301d4df0cc0d1de5a618fa48e9133038ef8ee15395bbc1fa1c66552bd5cb8c901a2239130e1ee6499d679f0eae5 languageName: node linkType: hard -"@docusaurus/module-type-aliases@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/module-type-aliases@npm:0.0.0-5703" +"@docusaurus/module-type-aliases@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/module-type-aliases@npm:0.0.0-5809" dependencies: "@docusaurus/react-loadable": 5.5.2 - "@docusaurus/types": 0.0.0-5703 + "@docusaurus/types": 0.0.0-5809 "@types/history": ^4.7.11 "@types/react": "*" "@types/react-router-config": "*" @@ -1848,19 +1843,19 @@ __metadata: peerDependencies: react: "*" react-dom: "*" - checksum: b0dbf719f3c6510b6706f36310b185bdf8a8a2ab5d3b0b76f8962b08d3b1a86783c3f4c23455c03d528500871f0a18ede4e03a0df8c7a44096b91097a5ffda7d + checksum: 3bd545d550989111b00274f3f06df9367895688060afb08b6c7ec3b4967768a78f5d2e1bf7a1b43d77276b116e62f077f0131cc8f1b7e4acbc1db9d3d49471aa languageName: node linkType: hard -"@docusaurus/plugin-client-redirects@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-client-redirects@npm:0.0.0-5703" +"@docusaurus/plugin-client-redirects@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-client-redirects@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 eta: ^2.2.0 fs-extra: ^11.1.1 lodash: ^4.17.21 @@ -1868,21 +1863,21 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: ac246eeb75028acd6a1d8804572fb89bac5575facf82c35ca3c8441d916980c183f5342a768d31cccc95f2034657bd25cfda24d480a2b8b4abdd693a51d9754e + checksum: d0b9b842f184e0b21ae9fcb718b8eccb7d0103ed847c7e27b782ba67b6eaba1875f3de796823363ccfbc648d6721a5d8ac1512ffc61e55e9c1d9a47bc7ad1646 languageName: node linkType: hard -"@docusaurus/plugin-content-blog@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-content-blog@npm:0.0.0-5703" +"@docusaurus/plugin-content-blog@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-content-blog@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/mdx-loader": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/mdx-loader": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 cheerio: ^1.0.0-rc.12 feed: ^4.2.2 fs-extra: ^11.1.1 @@ -1890,31 +1885,30 @@ __metadata: reading-time: ^1.5.0 srcset: ^4.0.0 tslib: ^2.6.0 - unist-util-visit: ^2.0.3 + unist-util-visit: ^5.0.0 utility-types: ^3.10.0 webpack: ^5.88.1 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 46e4a4fac407a8014ce0918933a476b03adca3d3b7e76b27a87bf0eeeb446f20e25db60b292f743652f3ad88232824f7a1541fcb7d27bcdbf9cb6d1c94e9a1b5 + checksum: 40981493af1fd0f835e3a5cf42c56718bc41feb73af36a8641c425875cd366e1e0c44841469649f55a014bfb5d1782d459a6ad8876849b12ee73e97b3f3b73cc languageName: node linkType: hard -"@docusaurus/plugin-content-docs@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-content-docs@npm:0.0.0-5703" +"@docusaurus/plugin-content-docs@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-content-docs@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/mdx-loader": 0.0.0-5703 - "@docusaurus/module-type-aliases": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/mdx-loader": 0.0.0-5809 + "@docusaurus/module-type-aliases": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 "@types/react-router-config": ^5.0.7 combine-promises: ^1.1.0 fs-extra: ^11.1.1 - import-fresh: ^3.3.0 js-yaml: ^4.1.0 lodash: ^4.17.21 tslib: ^2.6.0 @@ -1923,133 +1917,133 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: b08fbbac78f76ca68fc4a8ba488f41709733727e073d5f2d1e671dab484c4d37729a657305b315052369292ec7ae83c32992e551eda1d0990c035aa84fecbac6 + checksum: 1ce8c1e1783d08e41172d0db0e858847c2de4c9a750fc2206f7344bfa9186fdc1d818ad0f28542b109b0cb5d45a5ddfb9145014555225198f41881f57fb9df83 languageName: node linkType: hard -"@docusaurus/plugin-content-pages@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-content-pages@npm:0.0.0-5703" +"@docusaurus/plugin-content-pages@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-content-pages@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/mdx-loader": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/mdx-loader": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 fs-extra: ^11.1.1 tslib: ^2.6.0 webpack: ^5.88.1 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 9d0b904259c625d82175fa3fdeae89de8503f409a7dc47c9077f35d25f72fcf76a0a6392f5e790c3bf2604896e1aa0fced00386b9df553f6ba01247ccadd6503 + checksum: 362fa40e3c92b8637ed13f67ce244306e668484e985e2e1267c92ea0506a742c0d3b552ed508d3bd77259cfca0fb7ebc7722df9434e8d819c93c8a4edd546b1b languageName: node linkType: hard -"@docusaurus/plugin-debug@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-debug@npm:0.0.0-5703" +"@docusaurus/plugin-debug@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-debug@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@microlink/react-json-view": ^1.22.2 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 fs-extra: ^11.1.1 + react-json-view-lite: ^1.2.0 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: b9bf04c27144dc797fa25cae6df95f5a8b15c888993194bf1b0fae06ce6ae55cf338270b7a914ef07d6551addd6261e628a465c4f277a4e13753c207e533557d + checksum: 7dcb4ae3fff5352b9080b4fda26c834907bcafb230201154d1e13cefc9dbaafc0f9013bdd485f247cf390a084154695f6b9ef589936c66c30fa66b02f774a775 languageName: node linkType: hard -"@docusaurus/plugin-google-analytics@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-google-analytics@npm:0.0.0-5703" +"@docusaurus/plugin-google-analytics@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-google-analytics@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: ad98ea301caa4908197a3c460ae88dd5598c1485d8643708e8fb3f757ae3bf854efc1b5a1ab9c8ba15371c3a989704d7f9fdb55dd25d1d1b81936526068a877b + checksum: b7a6d4ea53098db90602d5359d807c3b7d164c335294155538565be29cb85b2853a8e5b7c76d57efb0b50901d0beb60689116933ab4940fc370ff6f194acd675 languageName: node linkType: hard -"@docusaurus/plugin-google-gtag@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-google-gtag@npm:0.0.0-5703" +"@docusaurus/plugin-google-gtag@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-google-gtag@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 "@types/gtag.js": ^0.0.12 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 9fbdbc0569f1ff634c357d197c1442da17c4a09c03163640db1d1bde0be8612dd432e494a4f02eb43ac58dd7df2cbd335911fdb7fa315a9de95e173090daf5dc + checksum: 0ce11539963fb62dc417bcd704bcdf3b5fab46f2e035286187d44bc5d9bc5f8238541e1da0cf3a9ab930871d21d08681fd3ec1291c1f0e088a70d85bf704579a languageName: node linkType: hard -"@docusaurus/plugin-google-tag-manager@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-google-tag-manager@npm:0.0.0-5703" +"@docusaurus/plugin-google-tag-manager@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-google-tag-manager@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 3ed2c6eb05087db3877ffe98cc6eca59be80889411e3cdbd981ac4d951a5efa269a1011a39a94c997d93474fd916e3634905a6537e10575e5d5a954a9bb00c5d + checksum: 7041f2b5574b6b77300ea3082a090057d389e1059d45d4ff2d87386d362f55f00cc26d329b8cdeec4fb10c9fcfe4702823c3f4f2ceef29b557086c4c20819ab3 languageName: node linkType: hard -"@docusaurus/plugin-sitemap@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/plugin-sitemap@npm:0.0.0-5703" +"@docusaurus/plugin-sitemap@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/plugin-sitemap@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 fs-extra: ^11.1.1 sitemap: ^7.1.1 tslib: ^2.6.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 1afbd2e3575ae09428fb4c53a2563e313c3e1a2323c79bdc256779c56f8d796c2c94473d055d6d7a97d477f06e35f0922bed441f7c3e1467feeb5329e1f7761b + checksum: cdb41c318786913923b1381882d7f5c956dba98f958f14cfd27fb962c540732b940ab7c01d04a97e30c229b98637d57fa3897dd0bdd738ac68ba55e27080bb08 languageName: node linkType: hard -"@docusaurus/preset-classic@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/preset-classic@npm:0.0.0-5703" +"@docusaurus/preset-classic@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/preset-classic@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/plugin-content-blog": 0.0.0-5703 - "@docusaurus/plugin-content-docs": 0.0.0-5703 - "@docusaurus/plugin-content-pages": 0.0.0-5703 - "@docusaurus/plugin-debug": 0.0.0-5703 - "@docusaurus/plugin-google-analytics": 0.0.0-5703 - "@docusaurus/plugin-google-gtag": 0.0.0-5703 - "@docusaurus/plugin-google-tag-manager": 0.0.0-5703 - "@docusaurus/plugin-sitemap": 0.0.0-5703 - "@docusaurus/theme-classic": 0.0.0-5703 - "@docusaurus/theme-common": 0.0.0-5703 - "@docusaurus/theme-search-algolia": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/plugin-content-blog": 0.0.0-5809 + "@docusaurus/plugin-content-docs": 0.0.0-5809 + "@docusaurus/plugin-content-pages": 0.0.0-5809 + "@docusaurus/plugin-debug": 0.0.0-5809 + "@docusaurus/plugin-google-analytics": 0.0.0-5809 + "@docusaurus/plugin-google-gtag": 0.0.0-5809 + "@docusaurus/plugin-google-tag-manager": 0.0.0-5809 + "@docusaurus/plugin-sitemap": 0.0.0-5809 + "@docusaurus/theme-classic": 0.0.0-5809 + "@docusaurus/theme-common": 0.0.0-5809 + "@docusaurus/theme-search-algolia": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: d4c2e6e02b828b248351819410fe03e94a4b4fd8489e398b60d128ba8f173548f8353a3875ff0347179ee59e84df7e3e9e1e5b0be24b84f015c8c02b650eb80c + checksum: 425337b7d91c761919cd1c7f6d7845932c78c1df7b86647b1129f122502798505577f4be87b243f4ff58fff55c71ab20ee49ae6015b3aba09c644a7154a0b9b9 languageName: node linkType: hard @@ -2065,30 +2059,30 @@ __metadata: languageName: node linkType: hard -"@docusaurus/theme-classic@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/theme-classic@npm:0.0.0-5703" +"@docusaurus/theme-classic@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/theme-classic@npm:0.0.0-5809" dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/mdx-loader": 0.0.0-5703 - "@docusaurus/module-type-aliases": 0.0.0-5703 - "@docusaurus/plugin-content-blog": 0.0.0-5703 - "@docusaurus/plugin-content-docs": 0.0.0-5703 - "@docusaurus/plugin-content-pages": 0.0.0-5703 - "@docusaurus/theme-common": 0.0.0-5703 - "@docusaurus/theme-translations": 0.0.0-5703 - "@docusaurus/types": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 - "@mdx-js/react": ^2.1.5 - clsx: ^1.2.1 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/mdx-loader": 0.0.0-5809 + "@docusaurus/module-type-aliases": 0.0.0-5809 + "@docusaurus/plugin-content-blog": 0.0.0-5809 + "@docusaurus/plugin-content-docs": 0.0.0-5809 + "@docusaurus/plugin-content-pages": 0.0.0-5809 + "@docusaurus/theme-common": 0.0.0-5809 + "@docusaurus/theme-translations": 0.0.0-5809 + "@docusaurus/types": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 + "@mdx-js/react": ^3.0.0 + clsx: ^2.0.0 copy-text-to-clipboard: ^3.2.0 infima: 0.2.0-alpha.43 lodash: ^4.17.21 nprogress: ^0.2.0 postcss: ^8.4.26 - prism-react-renderer: ^2.1.0 + prism-react-renderer: ^2.3.0 prismjs: ^1.29.0 react-router-dom: ^5.3.4 rtlcss: ^4.1.0 @@ -2097,51 +2091,51 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: ba078e06a07bbcc4a474f67938760852146e0cb612e44bc81b22879d374372eeea076de6e46e9f955dd351f37dadd98ced2e70b6b8a476b88684fae40855ecb9 + checksum: 9df071f67a7048e7a33ec2b0d627c8c342574ac8fd4596d108b88c3d6fe9b454cbc84a558aaa09a0ef4d7d6887c0bb75bc60d3ec1182ee69b7de87c035cee843 languageName: node linkType: hard -"@docusaurus/theme-common@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/theme-common@npm:0.0.0-5703" +"@docusaurus/theme-common@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/theme-common@npm:0.0.0-5809" dependencies: - "@docusaurus/mdx-loader": 0.0.0-5703 - "@docusaurus/module-type-aliases": 0.0.0-5703 - "@docusaurus/plugin-content-blog": 0.0.0-5703 - "@docusaurus/plugin-content-docs": 0.0.0-5703 - "@docusaurus/plugin-content-pages": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-common": 0.0.0-5703 + "@docusaurus/mdx-loader": 0.0.0-5809 + "@docusaurus/module-type-aliases": 0.0.0-5809 + "@docusaurus/plugin-content-blog": 0.0.0-5809 + "@docusaurus/plugin-content-docs": 0.0.0-5809 + "@docusaurus/plugin-content-pages": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-common": 0.0.0-5809 "@types/history": ^4.7.11 "@types/react": "*" "@types/react-router-config": "*" - clsx: ^1.2.1 + clsx: ^2.0.0 parse-numeric-range: ^1.3.0 - prism-react-renderer: ^2.1.0 + prism-react-renderer: ^2.3.0 tslib: ^2.6.0 utility-types: ^3.10.0 peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: f9b23318dbb504bcce0992de475f1ab32860620523dfe749bfdb00ac8e7fc75be705fd5177db2ce893ed269de40cef7f6a7290d44a12c6a160d9461bbca05552 + checksum: 6561d5526ac8d0e818fa5d9eaf1b1969d8f2623b8b6358d0360c3808da49798f4bdbe07f97af56bd69f2604f158e62bf454c8f4e6a9c0233f57c3a81ddafec5c languageName: node linkType: hard -"@docusaurus/theme-search-algolia@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/theme-search-algolia@npm:0.0.0-5703" +"@docusaurus/theme-search-algolia@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/theme-search-algolia@npm:0.0.0-5809" dependencies: "@docsearch/react": ^3.5.2 - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/plugin-content-docs": 0.0.0-5703 - "@docusaurus/theme-common": 0.0.0-5703 - "@docusaurus/theme-translations": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 - "@docusaurus/utils-validation": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/plugin-content-docs": 0.0.0-5809 + "@docusaurus/theme-common": 0.0.0-5809 + "@docusaurus/theme-translations": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 + "@docusaurus/utils-validation": 0.0.0-5809 algoliasearch: ^4.18.0 algoliasearch-helper: ^3.13.3 - clsx: ^1.2.1 + clsx: ^2.0.0 eta: ^2.2.0 fs-extra: ^11.1.1 lodash: ^4.17.21 @@ -2150,24 +2144,25 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 2e8ccc603126634488332ae108f9ac9ad3d2e1e46b5abef48720f2a25ae5cfc93850301d4ea30a6dc66034877b6abedd31229f622c6fd181347c3c14f7ba3c66 + checksum: 7fb83216b8d091dd108c13b7af86b59f26d3772ad0e0d0e8b5493c4c547dd9554b3184d6e4574e456cc6bb5a598a1dae8929453f4a94dffc7ae1da0665968ddf languageName: node linkType: hard -"@docusaurus/theme-translations@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/theme-translations@npm:0.0.0-5703" +"@docusaurus/theme-translations@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/theme-translations@npm:0.0.0-5809" dependencies: fs-extra: ^11.1.1 tslib: ^2.6.0 - checksum: 1f952386982008e2e7b07d6b95b1a902a57891321ae2670f13f391a541944ec2c82f5e03d3ed2c459a17113387a51cb5efe824d03037a3cc0b25251451287d1d + checksum: 8ca34bb5e02f021995bea8213f1ed0bb2c9493156b9495ab94a437d3d893a5a970dad871d07d2a80b003ba28c1fd5bd39e175c766884cb2ea442c226400091cf languageName: node linkType: hard -"@docusaurus/types@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/types@npm:0.0.0-5703" +"@docusaurus/types@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/types@npm:0.0.0-5809" dependencies: + "@mdx-js/mdx": ^3.0.0 "@types/history": ^4.7.11 "@types/react": "*" commander: ^5.1.0 @@ -2179,13 +2174,13 @@ __metadata: peerDependencies: react: ^18.0.0 react-dom: ^18.0.0 - checksum: 46ca6a27c8a0d1b69f744375ee2c24be2deed20c2801231a462b2e37b7a6e1f73ab17b6a5506e05f628957be8be74e4cd960780b9986c8fa4ac1757bd8c74231 + checksum: 97800e1b3f52ed167b78c812c2c652fe84308fc033f057b81043d485f3afb935dd5c30761c9ed7868fcf5ee0728fe3a841edd745285793167d0d1d48a298f5cd languageName: node linkType: hard -"@docusaurus/utils-common@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/utils-common@npm:0.0.0-5703" +"@docusaurus/utils-common@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/utils-common@npm:0.0.0-5809" dependencies: tslib: ^2.6.0 peerDependencies: @@ -2193,28 +2188,28 @@ __metadata: peerDependenciesMeta: "@docusaurus/types": optional: true - checksum: 8f70f176a99bcbbde22abd759621087ad555e7967cddf85662402f6d55c84d5f44cc1a90bf4e64670fc75b6db646dca4f573b3656a1718d1596d60e0e6c2d23a + checksum: 4cab3b5aa8b2ddbcfa5245f628fd84257f39312301b9813e36d616996a43dfe4f77abb26907271480128ce983125b4d20837c42e94d18875e6d07edcd4daaef7 languageName: node linkType: hard -"@docusaurus/utils-validation@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/utils-validation@npm:0.0.0-5703" +"@docusaurus/utils-validation@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/utils-validation@npm:0.0.0-5809" dependencies: - "@docusaurus/logger": 0.0.0-5703 - "@docusaurus/utils": 0.0.0-5703 + "@docusaurus/logger": 0.0.0-5809 + "@docusaurus/utils": 0.0.0-5809 joi: ^17.9.2 js-yaml: ^4.1.0 tslib: ^2.6.0 - checksum: e0d6df549644d8e732daf5407ce110dbccba17dcf84fe1a80091119675d2ce515e26bf6612fefa60b7df2c65dbaeee7ceb1a9f2b947a3705fe1efc7c229794e2 + checksum: 2ed6483a1e752a9c291d9ee607cfc3fc9f906cd6fa85687cc2e951cf0400264578e6f2b6bc282ee44eef52ed4c7503896f43e52f18d96064e37f5cb53096dbc4 languageName: node linkType: hard -"@docusaurus/utils@npm:0.0.0-5703": - version: 0.0.0-5703 - resolution: "@docusaurus/utils@npm:0.0.0-5703" +"@docusaurus/utils@npm:0.0.0-5809": + version: 0.0.0-5809 + resolution: "@docusaurus/utils@npm:0.0.0-5809" dependencies: - "@docusaurus/logger": 0.0.0-5703 + "@docusaurus/logger": 0.0.0-5809 "@svgr/webpack": ^6.5.1 escape-string-regexp: ^4.0.0 file-loader: ^6.2.0 @@ -2222,6 +2217,7 @@ __metadata: github-slugger: ^1.5.0 globby: ^11.1.0 gray-matter: ^4.0.3 + jiti: ^1.20.0 js-yaml: ^4.1.0 lodash: ^4.17.21 micromatch: ^4.0.5 @@ -2235,7 +2231,7 @@ __metadata: peerDependenciesMeta: "@docusaurus/types": optional: true - checksum: 91f53b17393e29fadb01c039f5394fc257c54bd7d80068f3a3e8a5e52d635d2e3de9fe781340de2ff4eaf691a2d4104865291c05c22ffc8b48a0894f588f3a9e + checksum: 408969f77fddf7fd621e3025927db7766a6513fa01a2c357cbcd57d1cd41db0abf72282bdb3e94cf6a6ad1b03138e48833c65da84c1c67b917e5660491ce40b6 languageName: node linkType: hard @@ -2344,55 +2340,46 @@ __metadata: languageName: node linkType: hard -"@mdx-js/mdx@npm:^2.1.5": - version: 2.3.0 - resolution: "@mdx-js/mdx@npm:2.3.0" +"@mdx-js/mdx@npm:^3.0.0": + version: 3.0.0 + resolution: "@mdx-js/mdx@npm:3.0.0" dependencies: + "@types/estree": ^1.0.0 "@types/estree-jsx": ^1.0.0 + "@types/hast": ^3.0.0 "@types/mdx": ^2.0.0 - estree-util-build-jsx: ^2.0.0 - estree-util-is-identifier-name: ^2.0.0 - estree-util-to-js: ^1.1.0 + collapse-white-space: ^2.0.0 + devlop: ^1.0.0 + estree-util-build-jsx: ^3.0.0 + estree-util-is-identifier-name: ^3.0.0 + estree-util-to-js: ^2.0.0 estree-walker: ^3.0.0 - hast-util-to-estree: ^2.0.0 - markdown-extensions: ^1.0.0 + hast-util-to-estree: ^3.0.0 + hast-util-to-jsx-runtime: ^2.0.0 + markdown-extensions: ^2.0.0 periscopic: ^3.0.0 - remark-mdx: ^2.0.0 - remark-parse: ^10.0.0 - remark-rehype: ^10.0.0 - unified: ^10.0.0 - unist-util-position-from-estree: ^1.0.0 - unist-util-stringify-position: ^3.0.0 - unist-util-visit: ^4.0.0 - vfile: ^5.0.0 - checksum: d918766a326502ec0b54adee61dc2930daf5b748acb9107f9bfd1ab0dbc4d7b1a4d0dbb9e21da9dd2a9fc2f9950b2973a43c6ba62d3a72eb67a30f6c953e5be8 + remark-mdx: ^3.0.0 + remark-parse: ^11.0.0 + remark-rehype: ^11.0.0 + source-map: ^0.7.0 + unified: ^11.0.0 + unist-util-position-from-estree: ^2.0.0 + unist-util-stringify-position: ^4.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 + checksum: da4305dcfd9012521170e0ed439eb336900fb84a5784e5e3dac2144855fa603325477855e17a04b7c673cc24699cf2bfd611c958f591bb3a9afb5608c259bbd3 languageName: node linkType: hard -"@mdx-js/react@npm:^2.1.5": - version: 2.3.0 - resolution: "@mdx-js/react@npm:2.3.0" +"@mdx-js/react@npm:^3.0.0": + version: 3.0.0 + resolution: "@mdx-js/react@npm:3.0.0" dependencies: "@types/mdx": ^2.0.0 + peerDependencies: "@types/react": ">=16" - peerDependencies: react: ">=16" - checksum: f45fe779556e6cd9a787f711274480e0638b63c460f192ebdcd77cc07ffa61e23c98cb46dd46e577093e1cb4997a232a848d1fb0ba850ae204422cf603add524 - languageName: node - linkType: hard - -"@microlink/react-json-view@npm:^1.22.2": - version: 1.22.2 - resolution: "@microlink/react-json-view@npm:1.22.2" - dependencies: - flux: ~4.0.1 - react-base16-styling: ~0.6.0 - react-lifecycles-compat: ~3.0.4 - react-textarea-autosize: ~8.3.2 - peerDependencies: - react: ">= 15" - react-dom: ">= 15" - checksum: 7cc31fcc06f6bac2062aedda565e55a03e1275f5b810f0efee27de8ea60967713f3a13859a16ba775b9b3e8782c044928cf3faee48902a19ce1c58d7ecc6e8d0 + checksum: a780cff9d7f7639d6fc21c9d4e0a6ac1370c3209ea0db176923df7f9145785309591cf871f227f5135d1fe2accce0d5df9a22fc0530e5dda0c7b4b105705f20d languageName: node linkType: hard @@ -2507,6 +2494,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/is@npm:^4.6.0": + version: 4.6.0 + resolution: "@sindresorhus/is@npm:4.6.0" + checksum: 83839f13da2c29d55c97abc3bc2c55b250d33a0447554997a85c539e058e57b8da092da396e252b11ec24a0279a0bed1f537fa26302209327060643e327f81d2 + languageName: node + linkType: hard + "@sindresorhus/is@npm:^5.2.0": version: 5.3.0 resolution: "@sindresorhus/is@npm:5.3.0" @@ -2983,12 +2977,12 @@ __metadata: languageName: node linkType: hard -"@types/hast@npm:^2.0.0": - version: 2.3.4 - resolution: "@types/hast@npm:2.3.4" +"@types/hast@npm:^3.0.0": + version: 3.0.3 + resolution: "@types/hast@npm:3.0.3" dependencies: "@types/unist": "*" - checksum: fff47998f4c11e21a7454b58673f70478740ecdafd95aaf50b70a3daa7da9cdc57315545bf9c039613732c40b7b0e9e49d11d03fe9a4304721cdc3b29a88141e + checksum: ca204207550fd6848ee20b5ba2018fd54f515d59a8b80375cdbe392ba2b4b130dac25fdfbaf9f2a70d2aec9d074a34dc14d4d59d31fa3ede80ef9850afad5d3c languageName: node linkType: hard @@ -3061,21 +3055,12 @@ __metadata: languageName: node linkType: hard -"@types/mdast@npm:^3.0.0": - version: 3.0.10 - resolution: "@types/mdast@npm:3.0.10" +"@types/mdast@npm:^4.0.0, @types/mdast@npm:^4.0.2": + version: 4.0.3 + resolution: "@types/mdast@npm:4.0.3" dependencies: "@types/unist": "*" - checksum: 3f587bfc0a9a2403ecadc220e61031b01734fedaf82e27eb4d5ba039c0eb54db8c85681ccc070ab4df3f7ec711b736a82b990e69caa14c74bf7ac0ccf2ac7313 - languageName: node - linkType: hard - -"@types/mdast@npm:^4.0.0": - version: 4.0.1 - resolution: "@types/mdast@npm:4.0.1" - dependencies: - "@types/unist": "*" - checksum: 3d8fe54a6fb747376c4cc2f05c319730a5737b77844d8ea58d2d696417fa933cd270c20e197f531fc1b4be5e340dc416129f8b4f5fa2f0d2d0cf51850928340a + checksum: 345c5a22fccf05f35239ea6313ee4aaf6ebed5927c03ac79744abccb69b9ba5e692f9b771e36a012b79e17429082cada30f579e9c43b8a54e0ffb365431498b6 languageName: node linkType: hard @@ -3121,13 +3106,6 @@ __metadata: languageName: node linkType: hard -"@types/parse5@npm:^6.0.0": - version: 6.0.3 - resolution: "@types/parse5@npm:6.0.3" - checksum: ddb59ee4144af5dfcc508a8dcf32f37879d11e12559561e65788756b95b33e6f03ea027d88e1f5408f9b7bfb656bf630ace31a2169edf44151daaf8dd58df1b7 - languageName: node - linkType: hard - "@types/prismjs@npm:^1.26.0": version: 1.26.1 resolution: "@types/prismjs@npm:1.26.1" @@ -3188,7 +3166,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:*, @types/react@npm:>=16": +"@types/react@npm:*": version: 18.2.0 resolution: "@types/react@npm:18.2.0" dependencies: @@ -3296,6 +3274,13 @@ __metadata: languageName: node linkType: hard +"@ungap/structured-clone@npm:^1.0.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.5, @webassemblyjs/ast@npm:^1.11.5": version: 1.11.5 resolution: "@webassemblyjs/ast@npm:1.11.5" @@ -3768,13 +3753,6 @@ __metadata: languageName: node linkType: hard -"asap@npm:~2.0.3": - version: 2.0.6 - resolution: "asap@npm:2.0.6" - checksum: b296c92c4b969e973260e47523207cd5769abd27c245a68c26dc7a0fe8053c55bb04360237cb51cab1df52be939da77150ace99ad331fb7fb13b3423ed73ff3d - languageName: node - linkType: hard - "astring@npm:^1.8.0": version: 1.8.4 resolution: "astring@npm:1.8.4" @@ -3784,13 +3762,6 @@ __metadata: languageName: node linkType: hard -"asynckit@npm:^0.4.0": - version: 0.4.0 - resolution: "asynckit@npm:0.4.0" - checksum: 7b78c451df768adba04e2d02e63e2d0bf3b07adcd6e42b4cf665cb7ce899bedd344c69a1dcbce355b5f972d597b25aaa1c1742b52cffd9caccb22f348114f6be - languageName: node - linkType: hard - "at-least-node@npm:^1.0.0": version: 1.0.0 resolution: "at-least-node@npm:1.0.0" @@ -3816,16 +3787,6 @@ __metadata: languageName: node linkType: hard -"axios@npm:^0.27.2": - version: 0.27.2 - resolution: "axios@npm:0.27.2" - dependencies: - follow-redirects: ^1.14.9 - form-data: ^4.0.0 - checksum: 38cb7540465fe8c4102850c4368053c21683af85c5fdf0ea619f9628abbcb59415d1e22ebc8a6390d2bbc9b58a9806c874f139767389c862ec9b772235f06854 - languageName: node - linkType: hard - "babel-loader@npm:^9.1.3": version: 9.1.3 resolution: "babel-loader@npm:9.1.3" @@ -3888,10 +3849,10 @@ __metadata: version: 0.0.0-use.local resolution: "backstage-microsite@workspace:." dependencies: - "@docusaurus/core": 0.0.0-5703 - "@docusaurus/module-type-aliases": 0.0.0-5703 - "@docusaurus/plugin-client-redirects": 0.0.0-5703 - "@docusaurus/preset-classic": 0.0.0-5703 + "@docusaurus/core": 0.0.0-5809 + "@docusaurus/module-type-aliases": 0.0.0-5809 + "@docusaurus/plugin-client-redirects": 0.0.0-5809 + "@docusaurus/preset-classic": 0.0.0-5809 "@spotify/prettier-config": ^14.0.0 "@swc/core": ^1.3.46 "@tsconfig/docusaurus": ^2.0.0 @@ -3926,13 +3887,6 @@ __metadata: languageName: node linkType: hard -"base16@npm:^1.0.0": - version: 1.0.0 - resolution: "base16@npm:1.0.0" - checksum: 0cd449a2db0f0f957e4b6b57e33bc43c9e20d4f1dd744065db94b5da35e8e71fa4dc4bc7a901e59a84d5f8b6936e3c520e2471787f667fc155fb0f50d8540f5d - languageName: node - linkType: hard - "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -4053,17 +4007,17 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.21.9, browserslist@npm:^4.22.1": - version: 4.22.1 - resolution: "browserslist@npm:4.22.1" +"browserslist@npm:^4.0.0, browserslist@npm:^4.14.5, browserslist@npm:^4.18.1, browserslist@npm:^4.21.10, browserslist@npm:^4.21.4, browserslist@npm:^4.22.1, browserslist@npm:^4.22.2": + version: 4.22.2 + resolution: "browserslist@npm:4.22.2" dependencies: - caniuse-lite: ^1.0.30001541 - electron-to-chromium: ^1.4.535 - node-releases: ^2.0.13 + caniuse-lite: ^1.0.30001565 + electron-to-chromium: ^1.4.601 + node-releases: ^2.0.14 update-browserslist-db: ^1.0.13 bin: browserslist: cli.js - checksum: 7e6b10c53f7dd5d83fd2b95b00518889096382539fed6403829d447e05df4744088de46a571071afb447046abc3c66ad06fbc790e70234ec2517452e32ffd862 + checksum: 33ddfcd9145220099a7a1ac533cecfe5b7548ffeb29b313e1b57be6459000a1f8fa67e781cf4abee97268ac594d44134fcc4a6b2b4750ceddc9796e3a22076d9 languageName: node linkType: hard @@ -4189,10 +4143,10 @@ __metadata: languageName: node linkType: hard -"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001541": - version: 1.0.30001546 - resolution: "caniuse-lite@npm:1.0.30001546" - checksum: d3ef82f5ee94743002c5b2dd61c84342debcc94b2d5907b64ade3514ecfc4f20bbe86a6bc453fd6436d5fbcf6582e07405d7c2077565675a71c83adc238a11fa +"caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001538, caniuse-lite@npm:^1.0.30001565": + version: 1.0.30001579 + resolution: "caniuse-lite@npm:1.0.30001579" + checksum: 7539dcff74d2243a30c428393dc690c87fa34d7da36434731853e9bcfe783757763b2971f5cc878e25242a93e184e53f167d11bd74955af956579f7af71cc764 languageName: node linkType: hard @@ -4231,6 +4185,13 @@ __metadata: languageName: node linkType: hard +"char-regex@npm:^1.0.2": + version: 1.0.2 + resolution: "char-regex@npm:1.0.2" + checksum: b563e4b6039b15213114626621e7a3d12f31008bdce20f9c741d69987f62aeaace7ec30f6018890ad77b2e9b4d95324c9f5acfca58a9441e3b1dcdd1e2525d17 + languageName: node + linkType: hard + "character-entities-html4@npm:^2.0.0": version: 2.1.0 resolution: "character-entities-html4@npm:2.1.0" @@ -4375,13 +4336,6 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^1.2.1": - version: 1.2.1 - resolution: "clsx@npm:1.2.1" - checksum: 30befca8019b2eb7dbad38cff6266cf543091dae2825c856a62a8ccf2c3ab9c2907c4d12b288b73101196767f66812365400a227581484a05f968b0307cfaf12 - languageName: node - linkType: hard - "clsx@npm:^2.0.0": version: 2.1.0 resolution: "clsx@npm:2.1.0" @@ -4389,6 +4343,13 @@ __metadata: languageName: node linkType: hard +"collapse-white-space@npm:^2.0.0": + version: 2.1.0 + resolution: "collapse-white-space@npm:2.1.0" + checksum: c8978b1f4e7d68bf846cfdba6c6689ce8910511df7d331eb6e6757e51ceffb52768d59a28db26186c91dcf9594955b59be9f8ccd473c485790f5d8b90dc6726f + languageName: node + linkType: hard + "color-convert@npm:^1.9.0": version: 1.9.3 resolution: "color-convert@npm:1.9.3" @@ -4451,15 +4412,6 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.8": - version: 1.0.8 - resolution: "combined-stream@npm:1.0.8" - dependencies: - delayed-stream: ~1.0.0 - checksum: 49fa4aeb4916567e33ea81d088f6584749fc90c7abec76fd516bf1c5aa5c79f3584b5ba3de6b86d26ddd64bae5329c4c7479343250cfe71c75bb366eae53bb7c - languageName: node - linkType: hard - "comma-separated-tokens@npm:^2.0.0": version: 2.0.3 resolution: "comma-separated-tokens@npm:2.0.3" @@ -4724,15 +4676,6 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.1.5": - version: 3.1.5 - resolution: "cross-fetch@npm:3.1.5" - dependencies: - node-fetch: 2.6.7 - checksum: f6b8c6ee3ef993ace6277fd789c71b6acf1b504fd5f5c7128df4ef2f125a429e29cd62dc8c127523f04a5f2fa4771ed80e3f3d9695617f441425045f505cf3bb - languageName: node - linkType: hard - "cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" @@ -4963,7 +4906,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.3": +"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.3": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5056,13 +4999,6 @@ __metadata: languageName: node linkType: hard -"delayed-stream@npm:~1.0.0": - version: 1.0.0 - resolution: "delayed-stream@npm:1.0.0" - checksum: 46fe6e83e2cb1d85ba50bd52803c68be9bd953282fa7096f51fc29edd5d67ff84ff753c51966061e5ba7cb5e47ef6d36a91924eddb7f3f3483b1c560f77a0020 - languageName: node - linkType: hard - "delegates@npm:^1.0.0": version: 1.0.0 resolution: "delegates@npm:1.0.0" @@ -5131,7 +5067,7 @@ __metadata: languageName: node linkType: hard -"devlop@npm:^1.0.0": +"devlop@npm:^1.0.0, devlop@npm:^1.1.0": version: 1.1.0 resolution: "devlop@npm:1.1.0" dependencies: @@ -5140,13 +5076,6 @@ __metadata: languageName: node linkType: hard -"diff@npm:^5.0.0": - version: 5.1.0 - resolution: "diff@npm:5.1.0" - checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90 - languageName: node - linkType: hard - "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -5302,10 +5231,10 @@ __metadata: languageName: node linkType: hard -"electron-to-chromium@npm:^1.4.535": - version: 1.4.544 - resolution: "electron-to-chromium@npm:1.4.544" - checksum: 78e88e4c56fc4faaa9a405de5e0b51305531e9cdf2c71bcc9296c2c59fb68001472e5b924f8701c873bc855ab5174cf0340642712d7af05c1d8e92356529397e +"electron-to-chromium@npm:^1.4.601": + version: 1.4.639 + resolution: "electron-to-chromium@npm:1.4.639" + checksum: f16293e69c5c31d836d1a3fdafb07f7a19a0f9a1137893aea879758f6de7d212480f4204e14f0669e653a5a3a1c35f6d1cbd54d4dd9e6ace7e22638270c9984e languageName: node linkType: hard @@ -5323,6 +5252,13 @@ __metadata: languageName: node linkType: hard +"emojilib@npm:^2.4.0": + version: 2.4.0 + resolution: "emojilib@npm:2.4.0" + checksum: ea241c342abda5a86ffd3a15d8f4871a616d485f700e03daea38c6ce38205847cea9f6ff8d5e962c00516b004949cc96c6e37b05559ea71a0a496faba53b56da + languageName: node + linkType: hard + "emojis-list@npm:^3.0.0": version: 3.0.0 resolution: "emojis-list@npm:3.0.0" @@ -5330,10 +5266,10 @@ __metadata: languageName: node linkType: hard -"emoticon@npm:^3.2.0": - version: 3.2.0 - resolution: "emoticon@npm:3.2.0" - checksum: f30649d18b672ab3139e95cb04f77b2442feb95c99dc59372ff80fbfd639b2bf4018bc68ab0b549bd765aecf8230d7899c43f86cfcc7b6370c06c3232783e24f +"emoticon@npm:^4.0.1": + version: 4.0.1 + resolution: "emoticon@npm:4.0.1" + checksum: 991ab6421927601af4eb44036b60e3125759a4d81f32d2ad96b66e3491e2fdb6a026eeb6bffbfa66724592dca95235570785963607d16961ea73a62ecce715e2 languageName: node linkType: hard @@ -5492,61 +5428,62 @@ __metadata: languageName: node linkType: hard -"estree-util-attach-comments@npm:^2.0.0": - version: 2.1.1 - resolution: "estree-util-attach-comments@npm:2.1.1" +"estree-util-attach-comments@npm:^3.0.0": + version: 3.0.0 + resolution: "estree-util-attach-comments@npm:3.0.0" dependencies: "@types/estree": ^1.0.0 - checksum: c5c2c41c9a55a169fb4fba9627057843f0d2e21e47a2e3e24318a11ffcf6bc704c0f96f405a529bddac7969b7c44f6cf86711505faaf0c5862c2024419b19704 + checksum: 56254eaef39659e6351919ebc2ae53a37a09290a14571c19e373e9d5fad343a3403d9ad0c23ae465d6e7d08c3e572fd56fb8c793efe6434a261bf1489932dbd5 languageName: node linkType: hard -"estree-util-build-jsx@npm:^2.0.0": - version: 2.2.2 - resolution: "estree-util-build-jsx@npm:2.2.2" +"estree-util-build-jsx@npm:^3.0.0": + version: 3.0.1 + resolution: "estree-util-build-jsx@npm:3.0.1" dependencies: "@types/estree-jsx": ^1.0.0 - estree-util-is-identifier-name: ^2.0.0 + devlop: ^1.0.0 + estree-util-is-identifier-name: ^3.0.0 estree-walker: ^3.0.0 - checksum: d008ac36a45d797eadca696f41b4c1ac0587ec0e0b52560cfb0e76d14ef15fc18e526f9023b6e5457dafa9cf3f010c9bb1dfc9c727ebd7cf0ba2ebbaa43919ac + checksum: 185eff060eda2ba32cecd15904db4f5ba0681159fbdf54f0f6586cd9411e77e733861a833d0aee3415e1d1fd4b17edf08bc9e9872cee98e6ec7b0800e1a85064 languageName: node linkType: hard -"estree-util-is-identifier-name@npm:^2.0.0": - version: 2.1.0 - resolution: "estree-util-is-identifier-name@npm:2.1.0" - checksum: cab317a071fafb99cf83b57df7924bccd2e6ab4e252688739e49f00b16cefd168e279c171442b0557c80a1c80ffaa927d670dadea65bb3c9b151efb8e772e89d +"estree-util-is-identifier-name@npm:^3.0.0": + version: 3.0.0 + resolution: "estree-util-is-identifier-name@npm:3.0.0" + checksum: ea3909f0188ea164af0aadeca87c087e3e5da78d76da5ae9c7954ff1340ea3e4679c4653bbf4299ffb70caa9b322218cc1128db2541f3d2976eb9704f9857787 languageName: node linkType: hard -"estree-util-to-js@npm:^1.1.0": - version: 1.2.0 - resolution: "estree-util-to-js@npm:1.2.0" +"estree-util-to-js@npm:^2.0.0": + version: 2.0.0 + resolution: "estree-util-to-js@npm:2.0.0" dependencies: "@types/estree-jsx": ^1.0.0 astring: ^1.8.0 source-map: ^0.7.0 - checksum: 93a75e1051a6a4f5c631597ecd2ed95129fadbc80a58a10475d6d6b1b076a69393ba4a8d2bb71f698401f64ccca47e3f3828dd0042cac81439b988fae0f5f8e0 + checksum: 833edc94ab9978e0918f90261e0a3361bf4564fec4901f326d2237a9235d3f5fc6482da3be5acc545e702c8c7cb8bc5de5c7c71ba3b080eb1975bcfdf3923d79 languageName: node linkType: hard -"estree-util-value-to-estree@npm:^2.1.0": - version: 2.1.0 - resolution: "estree-util-value-to-estree@npm:2.1.0" +"estree-util-value-to-estree@npm:^3.0.1": + version: 3.0.1 + resolution: "estree-util-value-to-estree@npm:3.0.1" dependencies: "@types/estree": ^1.0.0 is-plain-obj: ^4.0.0 - checksum: 6a930e9c8b85c27845ecca010b5b6b64d3b3a4e7d61117d8342fad7b2b595d7052c671616d1c2269e0954a200666834227ce0abb8488ded49700dfde54167ebb + checksum: 7ab89084aa2c5677aeb0d7350ff21e71c9bbc424dc872a55bb4f25f63a7fd99fc7861626dd89b5544db3d3696212154bcf2b12b63ecd5a59dbfd07915c88aee4 languageName: node linkType: hard -"estree-util-visit@npm:^1.0.0": - version: 1.2.1 - resolution: "estree-util-visit@npm:1.2.1" +"estree-util-visit@npm:^2.0.0": + version: 2.0.0 + resolution: "estree-util-visit@npm:2.0.0" dependencies: "@types/estree-jsx": ^1.0.0 - "@types/unist": ^2.0.0 - checksum: 6feea4fdc43b0ba0f79faf1d57cf32373007e146d4810c7c09c13f5a9c1b8600c1ac57a8d949967cedd2a9a91dddd246e19b59bacfc01e417168b4ebf220f691 + "@types/unist": ^3.0.0 + checksum: 6444b38f224322945a6d19ea81a8828a0eec64aefb2bf1ea791fe20df496f7b7c543408d637df899e6a8e318b638f66226f16378a33c4c2b192ba5c3f891121f languageName: node linkType: hard @@ -5739,37 +5676,6 @@ __metadata: languageName: node linkType: hard -"fbemitter@npm:^3.0.0": - version: 3.0.0 - resolution: "fbemitter@npm:3.0.0" - dependencies: - fbjs: ^3.0.0 - checksum: 069690b8cdff3521ade3c9beb92ba0a38d818a86ef36dff8690e66749aef58809db4ac0d6938eb1cacea2dbef5f2a508952d455669590264cdc146bbe839f605 - languageName: node - linkType: hard - -"fbjs-css-vars@npm:^1.0.0": - version: 1.0.2 - resolution: "fbjs-css-vars@npm:1.0.2" - checksum: 72baf6d22c45b75109118b4daecb6c8016d4c83c8c0f23f683f22e9d7c21f32fff6201d288df46eb561e3c7d4bb4489b8ad140b7f56444c453ba407e8bd28511 - languageName: node - linkType: hard - -"fbjs@npm:^3.0.0, fbjs@npm:^3.0.1": - version: 3.0.4 - resolution: "fbjs@npm:3.0.4" - dependencies: - cross-fetch: ^3.1.5 - fbjs-css-vars: ^1.0.0 - loose-envify: ^1.0.0 - object-assign: ^4.1.0 - promise: ^7.1.1 - setimmediate: ^1.0.5 - ua-parser-js: ^0.7.30 - checksum: 8b23a3550fcda8a9109fca9475a3416590c18bb6825ea884192864ed686f67fcd618e308a140c9e5444fbd0168732e1ff3c092ba3d0c0ae1768969f32ba280c7 - languageName: node - linkType: hard - "feed@npm:^4.2.2": version: 4.2.2 resolution: "feed@npm:4.2.2" @@ -5861,19 +5767,7 @@ __metadata: languageName: node linkType: hard -"flux@npm:~4.0.1": - version: 4.0.4 - resolution: "flux@npm:4.0.4" - dependencies: - fbemitter: ^3.0.0 - fbjs: ^3.0.1 - peerDependencies: - react: ^15.0.2 || ^16.0.0 || ^17.0.0 - checksum: 8fa5c2f9322258de3e331f67c6f1078a7f91c4dec9dbe8a54c4b8a80eed19a4f91889028b768668af4a796e8f2ee75e461e1571b8615432a3920ae95cc4ff794 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.0.0, follow-redirects@npm:^1.14.9": +"follow-redirects@npm:^1.0.0": version: 1.15.4 resolution: "follow-redirects@npm:1.15.4" peerDependenciesMeta: @@ -5921,17 +5815,6 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.0": - version: 4.0.0 - resolution: "form-data@npm:4.0.0" - dependencies: - asynckit: ^0.4.0 - combined-stream: ^1.0.8 - mime-types: ^2.1.12 - checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c - languageName: node - linkType: hard - "format@npm:^0.2.0": version: 0.2.2 resolution: "format@npm:0.2.2" @@ -6309,103 +6192,133 @@ __metadata: languageName: node linkType: hard -"hast-util-from-parse5@npm:^7.0.0": - version: 7.1.2 - resolution: "hast-util-from-parse5@npm:7.1.2" +"hast-util-from-parse5@npm:^8.0.0": + version: 8.0.1 + resolution: "hast-util-from-parse5@npm:8.0.1" dependencies: - "@types/hast": ^2.0.0 - "@types/unist": ^2.0.0 - hastscript: ^7.0.0 + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + devlop: ^1.0.0 + hastscript: ^8.0.0 property-information: ^6.0.0 - vfile: ^5.0.0 - vfile-location: ^4.0.0 + vfile: ^6.0.0 + vfile-location: ^5.0.0 web-namespaces: ^2.0.0 - checksum: 7b4ed5b508b1352127c6719f7b0c0880190cf9859fe54ccaf7c9228ecf623d36cef3097910b3874d2fe1aac6bf4cf45d3cc2303daac3135a05e9ade6534ddddb + checksum: fdd1ab8b03af13778ecb94ef9a58b1e3528410cdfceb3d6bb7600508967d0d836b451bc7bc3baf66efb7c730d3d395eea4bb1b30352b0162823d9f0de976774b languageName: node linkType: hard -"hast-util-parse-selector@npm:^3.0.0": - version: 3.1.1 - resolution: "hast-util-parse-selector@npm:3.1.1" +"hast-util-parse-selector@npm:^4.0.0": + version: 4.0.0 + resolution: "hast-util-parse-selector@npm:4.0.0" dependencies: - "@types/hast": ^2.0.0 - checksum: 511d373465f60dd65e924f88bf0954085f4fb6e3a2b062a4b5ac43b93cbfd36a8dce6234b5d1e3e63499d936375687e83fc5da55628b22bd6b581b5ee167d1c4 + "@types/hast": ^3.0.0 + checksum: 76087670d3b0b50b23a6cb70bca53a6176d6608307ccdbb3ed18b650b82e7c3513bfc40348f1389dc0c5ae872b9a768851f4335f44654abd7deafd6974c52402 languageName: node linkType: hard -"hast-util-raw@npm:^7.2.0": - version: 7.2.3 - resolution: "hast-util-raw@npm:7.2.3" +"hast-util-raw@npm:^9.0.0": + version: 9.0.1 + resolution: "hast-util-raw@npm:9.0.1" dependencies: - "@types/hast": ^2.0.0 - "@types/parse5": ^6.0.0 - hast-util-from-parse5: ^7.0.0 - hast-util-to-parse5: ^7.0.0 - html-void-elements: ^2.0.0 - parse5: ^6.0.0 - unist-util-position: ^4.0.0 - unist-util-visit: ^4.0.0 - vfile: ^5.0.0 + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 + "@ungap/structured-clone": ^1.0.0 + hast-util-from-parse5: ^8.0.0 + hast-util-to-parse5: ^8.0.0 + html-void-elements: ^3.0.0 + mdast-util-to-hast: ^13.0.0 + parse5: ^7.0.0 + unist-util-position: ^5.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 web-namespaces: ^2.0.0 zwitch: ^2.0.0 - checksum: 21857eea3ffb8fd92d2d9be7793b56d0b2c40db03c4cfa14828855ae41d7c584917aa83efb7157220b2e41e25e95f81f24679ac342c35145e5f1c1d39015f81f + checksum: 4b486eb4782eafb471ae639d45c14ac8797676518cf5da16adc973f52d7b8e1075a1451558c023b390820bd9fd213213e6248a2dae71b68ac5040b277509b8d9 languageName: node linkType: hard -"hast-util-to-estree@npm:^2.0.0": - version: 2.3.2 - resolution: "hast-util-to-estree@npm:2.3.2" +"hast-util-to-estree@npm:^3.0.0": + version: 3.1.0 + resolution: "hast-util-to-estree@npm:3.1.0" dependencies: "@types/estree": ^1.0.0 "@types/estree-jsx": ^1.0.0 - "@types/hast": ^2.0.0 - "@types/unist": ^2.0.0 + "@types/hast": ^3.0.0 comma-separated-tokens: ^2.0.0 - estree-util-attach-comments: ^2.0.0 - estree-util-is-identifier-name: ^2.0.0 - hast-util-whitespace: ^2.0.0 - mdast-util-mdx-expression: ^1.0.0 - mdast-util-mdxjs-esm: ^1.0.0 + devlop: ^1.0.0 + estree-util-attach-comments: ^3.0.0 + estree-util-is-identifier-name: ^3.0.0 + hast-util-whitespace: ^3.0.0 + mdast-util-mdx-expression: ^2.0.0 + mdast-util-mdx-jsx: ^3.0.0 + mdast-util-mdxjs-esm: ^2.0.0 property-information: ^6.0.0 space-separated-tokens: ^2.0.0 - style-to-object: ^0.4.1 - unist-util-position: ^4.0.0 + style-to-object: ^0.4.0 + unist-util-position: ^5.0.0 zwitch: ^2.0.0 - checksum: 721167e275c1b0b9b1dcb35964a39f6180e22983ee7b56748ecab9f6cc35fe5229fd6e30a8eb4826caeee7eed88014ce4710bd79146c080d4dd281058ba09a39 + checksum: 61272f7c18c9d2a5e34df7cfd2c97cbf12f6e9d05114d60e4dedd64e5576565eb1e35c78b9213c909bb8f984f0f8e9c49b568f04bdb444b83d0bca9159e14f3c languageName: node linkType: hard -"hast-util-to-parse5@npm:^7.0.0": - version: 7.1.0 - resolution: "hast-util-to-parse5@npm:7.1.0" +"hast-util-to-jsx-runtime@npm:^2.0.0": + version: 2.3.0 + resolution: "hast-util-to-jsx-runtime@npm:2.3.0" dependencies: - "@types/hast": ^2.0.0 + "@types/estree": ^1.0.0 + "@types/hast": ^3.0.0 + "@types/unist": ^3.0.0 comma-separated-tokens: ^2.0.0 + devlop: ^1.0.0 + estree-util-is-identifier-name: ^3.0.0 + hast-util-whitespace: ^3.0.0 + mdast-util-mdx-expression: ^2.0.0 + mdast-util-mdx-jsx: ^3.0.0 + mdast-util-mdxjs-esm: ^2.0.0 + property-information: ^6.0.0 + space-separated-tokens: ^2.0.0 + style-to-object: ^1.0.0 + unist-util-position: ^5.0.0 + vfile-message: ^4.0.0 + checksum: 599a97c6ec61c1430776813d7fb42e6f96032bf4a04dfcbb8eceef3bc8d1845ecf242387a4426b9d3f52320dbbfa26450643b81124b3d6a0b9bbb0fff4d0ba83 + languageName: node + linkType: hard + +"hast-util-to-parse5@npm:^8.0.0": + version: 8.0.0 + resolution: "hast-util-to-parse5@npm:8.0.0" + dependencies: + "@types/hast": ^3.0.0 + comma-separated-tokens: ^2.0.0 + devlop: ^1.0.0 property-information: ^6.0.0 space-separated-tokens: ^2.0.0 web-namespaces: ^2.0.0 zwitch: ^2.0.0 - checksum: 3a7f2175a3db599bbae7e49ba73d3e5e688e5efca7590ff50130ba108ad649f728402815d47db49146f6b94c14c934bf119915da9f6964e38802c122bcc8af6b + checksum: 137469209cb2b32b57387928878dc85310fbd5afa4807a8da69529199bb1d19044bfc95b50c3dc68d4fb2b6cb8bf99b899285597ab6ab318f50422eefd5599dd languageName: node linkType: hard -"hast-util-whitespace@npm:^2.0.0": - version: 2.0.1 - resolution: "hast-util-whitespace@npm:2.0.1" - checksum: 431be6b2f35472f951615540d7a53f69f39461e5e080c0190268bdeb2be9ab9b1dddfd1f467dd26c1de7e7952df67beb1307b6ee940baf78b24a71b5e0663868 - languageName: node - linkType: hard - -"hastscript@npm:^7.0.0, hastscript@npm:^7.1.0": - version: 7.2.0 - resolution: "hastscript@npm:7.2.0" +"hast-util-whitespace@npm:^3.0.0": + version: 3.0.0 + resolution: "hast-util-whitespace@npm:3.0.0" dependencies: - "@types/hast": ^2.0.0 + "@types/hast": ^3.0.0 + checksum: 41d93ccce218ba935dc3c12acdf586193c35069489c8c8f50c2aa824c00dec94a3c78b03d1db40fa75381942a189161922e4b7bca700b3a2cc779634c351a1e4 + languageName: node + linkType: hard + +"hastscript@npm:^8.0.0": + version: 8.0.0 + resolution: "hastscript@npm:8.0.0" + dependencies: + "@types/hast": ^3.0.0 comma-separated-tokens: ^2.0.0 - hast-util-parse-selector: ^3.0.0 + hast-util-parse-selector: ^4.0.0 property-information: ^6.0.0 space-separated-tokens: ^2.0.0 - checksum: 928a21576ff7b9a8c945e7940bcbf2d27f770edb4279d4d04b33dc90753e26ca35c1172d626f54afebd377b2afa32331e399feb3eb0f7b91a399dca5927078ae + checksum: ae3c20223e7b847320c0f98b6fb3c763ebe1bf3913c5805fbc176cf84553a9db1117ca34cf842a5235890b4b9ae0e94501bfdc9a9b870a5dbf5fc52426db1097 languageName: node linkType: hard @@ -6501,10 +6414,10 @@ __metadata: languageName: node linkType: hard -"html-void-elements@npm:^2.0.0": - version: 2.0.1 - resolution: "html-void-elements@npm:2.0.1" - checksum: 06d41f13b9d5d6e0f39861c4bec9a9196fa4906d56cd5cf6cf54ad2e52a85bf960cca2bf9600026bde16c8331db171bedba5e5a35e2e43630c8f1d497b2fb658 +"html-void-elements@npm:^3.0.0": + version: 3.0.0 + resolution: "html-void-elements@npm:3.0.0" + checksum: 59be397525465a7489028afa064c55763d9cccd1d7d9f630cca47137317f0e897a9ca26cef7e745e7cff1abc44260cfa407742b243a54261dfacd42230e94fce languageName: node linkType: hard @@ -6818,6 +6731,13 @@ __metadata: languageName: node linkType: hard +"inline-style-parser@npm:0.2.2": + version: 0.2.2 + resolution: "inline-style-parser@npm:0.2.2" + checksum: 698893d6542d4e7c0377936a1c7daec34a197765bd77c5599384756a95ce8804e6b79347b783aa591d5e9c6f3d33dae74c6d4cad3a94647eb05f3a785e927a3f + languageName: node + linkType: hard + "interpret@npm:^1.0.0": version: 1.4.0 resolution: "interpret@npm:1.4.0" @@ -6888,13 +6808,6 @@ __metadata: languageName: node linkType: hard -"is-buffer@npm:^2.0.0": - version: 2.0.5 - resolution: "is-buffer@npm:2.0.5" - checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42 - languageName: node - linkType: hard - "is-ci@npm:^3.0.1": version: 3.0.1 resolution: "is-ci@npm:3.0.1" @@ -7182,16 +7095,16 @@ __metadata: languageName: node linkType: hard -"jiti@npm:^1.18.2": - version: 1.20.0 - resolution: "jiti@npm:1.20.0" +"jiti@npm:^1.18.2, jiti@npm:^1.20.0": + version: 1.21.0 + resolution: "jiti@npm:1.21.0" bin: jiti: bin/jiti.js - checksum: 7924062b5675142e3e272a27735be84b7bfc0a0eb73217fc2dcafa034f37c4f7b4b9ffc07dd98bcff0f739a8811ce1544db205ae7e97b1c86f0df92c65ce3c72 + checksum: a7bd5d63921c170eaec91eecd686388181c7828e1fa0657ab374b9372bfc1f383cf4b039e6b272383d5cb25607509880af814a39abdff967322459cca41f2961 languageName: node linkType: hard -"joi@npm:^17.7.0, joi@npm:^17.9.2": +"joi@npm:^17.9.2": version: 17.11.0 resolution: "joi@npm:17.11.0" dependencies: @@ -7325,13 +7238,6 @@ __metadata: languageName: node linkType: hard -"kleur@npm:^4.0.3": - version: 4.1.5 - resolution: "kleur@npm:4.1.5" - checksum: 1dc476e32741acf0b1b5b0627ffd0d722e342c1b0da14de3e8ae97821327ca08f9fb944542fb3c126d90ac5f27f9d804edbe7c585bf7d12ef495d115e0f22c12 - languageName: node - linkType: hard - "klona@npm:^2.0.4": version: 2.0.6 resolution: "klona@npm:2.0.6" @@ -7432,13 +7338,6 @@ __metadata: languageName: node linkType: hard -"lodash.curry@npm:^4.0.1": - version: 4.1.1 - resolution: "lodash.curry@npm:4.1.1" - checksum: 9192b70fe7df4d1ff780c0260bee271afa9168c93fe4fa24bc861900240531b59781b5fdaadf4644fea8f4fbcd96f0700539ab294b579ffc1022c6c15dcc462a - languageName: node - linkType: hard - "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -7460,13 +7359,6 @@ __metadata: languageName: node linkType: hard -"lodash.flow@npm:^3.3.0": - version: 3.5.0 - resolution: "lodash.flow@npm:3.5.0" - checksum: a9a62ad344e3c5a1f42bc121da20f64dd855aaafecee24b1db640f29b88bd165d81c37ff7e380a7191de6f70b26f5918abcebbee8396624f78f3618a0b18634c - languageName: node - linkType: hard - "lodash.invokemap@npm:^4.6.0": version: 4.6.0 resolution: "lodash.invokemap@npm:4.6.0" @@ -7599,10 +7491,10 @@ __metadata: languageName: node linkType: hard -"markdown-extensions@npm:^1.0.0": - version: 1.1.1 - resolution: "markdown-extensions@npm:1.1.1" - checksum: 8a6dd128be1c524049ea6a41a9193715c2835d3d706af4b8b714ff2043a82786dbcd4a8f1fa9ddd28facbc444426c97515aef2d1f3dd11d5e2d63749ba577b1e +"markdown-extensions@npm:^2.0.0": + version: 2.0.0 + resolution: "markdown-extensions@npm:2.0.0" + checksum: ec4ffcb0768f112e778e7ac74cb8ef22a966c168c3e6c29829f007f015b0a0b5c79c73ee8599a0c72e440e7f5cfdbf19e80e2d77b9a313b8f66e180a330cf1b2 languageName: node linkType: hard @@ -7613,61 +7505,31 @@ __metadata: languageName: node linkType: hard -"mdast-util-definitions@npm:^5.0.0": - version: 5.1.2 - resolution: "mdast-util-definitions@npm:5.1.2" +"mdast-util-directive@npm:^3.0.0": + version: 3.0.0 + resolution: "mdast-util-directive@npm:3.0.0" dependencies: - "@types/mdast": ^3.0.0 - "@types/unist": ^2.0.0 - unist-util-visit: ^4.0.0 - checksum: 2544daccab744ea1ede76045c2577ae4f1cc1b9eb1ea51ab273fe1dca8db5a8d6f50f87759c0ce6484975914b144b7f40316f805cb9c86223a78db8de0b77bae - languageName: node - linkType: hard - -"mdast-util-directive@npm:^2.0.0": - version: 2.2.4 - resolution: "mdast-util-directive@npm:2.2.4" - dependencies: - "@types/mdast": ^3.0.0 - "@types/unist": ^2.0.0 - mdast-util-from-markdown: ^1.3.0 - mdast-util-to-markdown: ^1.5.0 + "@types/mdast": ^4.0.0 + "@types/unist": ^3.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 parse-entities: ^4.0.0 stringify-entities: ^4.0.0 - unist-util-visit-parents: ^5.1.3 - checksum: d04112761659c912b5d7db8f8c035918da52e79ce8cb598c7c270c6f678db0c22f3dea7894b30dec46852409f926306b84ca95683dd1e28731835eb5cb4fbda4 + unist-util-visit-parents: ^6.0.0 + checksum: 593afdc4f39f99bb198f3774bf4648cb546cb99a055e40c82262a7faab10926d2529a725d0d3945300ed0a1f07c6c84215a3f76b899a89b3f410ec7375bbab17 languageName: node linkType: hard -"mdast-util-find-and-replace@npm:^2.0.0": - version: 2.2.2 - resolution: "mdast-util-find-and-replace@npm:2.2.2" +"mdast-util-find-and-replace@npm:^3.0.0, mdast-util-find-and-replace@npm:^3.0.1": + version: 3.0.1 + resolution: "mdast-util-find-and-replace@npm:3.0.1" dependencies: - "@types/mdast": ^3.0.0 + "@types/mdast": ^4.0.0 escape-string-regexp: ^5.0.0 - unist-util-is: ^5.0.0 - unist-util-visit-parents: ^5.0.0 - checksum: b4ce463c43fe6e1c38a53a89703f755c84ab5437f49bff9a0ac751279733332ca11c85ed0262aa6c17481f77b555d26ca6d64e70d6814f5b8d12d34a3e53a60b - languageName: node - linkType: hard - -"mdast-util-from-markdown@npm:^1.0.0, mdast-util-from-markdown@npm:^1.1.0, mdast-util-from-markdown@npm:^1.3.0": - version: 1.3.0 - resolution: "mdast-util-from-markdown@npm:1.3.0" - dependencies: - "@types/mdast": ^3.0.0 - "@types/unist": ^2.0.0 - decode-named-character-reference: ^1.0.0 - mdast-util-to-string: ^3.1.0 - micromark: ^3.0.0 - micromark-util-decode-numeric-character-reference: ^1.0.0 - micromark-util-decode-string: ^1.0.0 - micromark-util-normalize-identifier: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - unist-util-stringify-position: ^3.0.0 - uvu: ^0.5.0 - checksum: cc971d1ad381150f6504fd753fbcffcc64c0abb527540ce343625c2bba76104505262122ef63d14ab66eb47203f323267017c6d09abfa8535ee6a8e14069595f + unist-util-is: ^6.0.0 + unist-util-visit-parents: ^6.0.0 + checksum: 05d5c4ff02e31db2f8a685a13bcb6c3f44e040bd9dfa54c19a232af8de5268334c8755d79cb456ed4cced1300c4fb83e88444c7ae8ee9ff16869a580f29d08cd languageName: node linkType: hard @@ -7705,142 +7567,142 @@ __metadata: languageName: node linkType: hard -"mdast-util-gfm-autolink-literal@npm:^1.0.0": - version: 1.0.3 - resolution: "mdast-util-gfm-autolink-literal@npm:1.0.3" +"mdast-util-gfm-autolink-literal@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-autolink-literal@npm:2.0.0" dependencies: - "@types/mdast": ^3.0.0 + "@types/mdast": ^4.0.0 ccount: ^2.0.0 - mdast-util-find-and-replace: ^2.0.0 - micromark-util-character: ^1.0.0 - checksum: 1748a8727cfc533bac0c287d6e72d571d165bfa77ae0418be4828177a3ec73c02c3f2ee534d87eb75cbaffa00c0866853bbcc60ae2255babb8210f7636ec2ce2 + devlop: ^1.0.0 + mdast-util-find-and-replace: ^3.0.0 + micromark-util-character: ^2.0.0 + checksum: 10322662e5302964bed7c9829c5fd3b0c9899d4f03e63fb8620ab141cf4f3de9e61fcb4b44d46aacc8a23f82bcd5d900980a211825dfe026b1dab5fdbc3e8742 languageName: node linkType: hard -"mdast-util-gfm-footnote@npm:^1.0.0": - version: 1.0.2 - resolution: "mdast-util-gfm-footnote@npm:1.0.2" +"mdast-util-gfm-footnote@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-footnote@npm:2.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-to-markdown: ^1.3.0 - micromark-util-normalize-identifier: ^1.0.0 - checksum: 2d77505f9377ed7e14472ef5e6b8366c3fec2cf5f936bb36f9fbe5b97ccb7cce0464d9313c236fa86fb844206fd585db05707e4fcfb755e4fc1864194845f1f6 + "@types/mdast": ^4.0.0 + devlop: ^1.1.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + micromark-util-normalize-identifier: ^2.0.0 + checksum: 45d26b40e7a093712e023105791129d76e164e2168d5268e113298a22de30c018162683fb7893cdc04ab246dac0087eed708b2a136d1d18ed2b32b3e0cae4a79 languageName: node linkType: hard -"mdast-util-gfm-strikethrough@npm:^1.0.0": - version: 1.0.3 - resolution: "mdast-util-gfm-strikethrough@npm:1.0.3" +"mdast-util-gfm-strikethrough@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-strikethrough@npm:2.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-to-markdown: ^1.3.0 - checksum: 17003340ff1bba643ec4a59fd4370fc6a32885cab2d9750a508afa7225ea71449fb05acaef60faa89c6378b8bcfbd86a9d94b05f3c6651ff27a60e3ddefc2549 + "@types/mdast": ^4.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: fe9b1d0eba9b791ff9001c008744eafe3dd7a81b085f2bf521595ce4a8e8b1b44764ad9361761ad4533af3e5d913d8ad053abec38172031d9ee32a8ebd1c7dbd languageName: node linkType: hard -"mdast-util-gfm-table@npm:^1.0.0": - version: 1.0.7 - resolution: "mdast-util-gfm-table@npm:1.0.7" +"mdast-util-gfm-table@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-table@npm:2.0.0" dependencies: - "@types/mdast": ^3.0.0 + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 markdown-table: ^3.0.0 - mdast-util-from-markdown: ^1.0.0 - mdast-util-to-markdown: ^1.3.0 - checksum: 8b8c401bb4162e53f072a2dff8efbca880fd78d55af30601c791315ab6722cb2918176e8585792469a0c530cebb9df9b4e7fede75fdc4d83df2839e238836692 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 063a627fd0993548fd63ca0c24c437baf91ba7d51d0a38820bd459bc20bf3d13d7365ef8d28dca99176dd5eb26058f7dde51190479c186dfe6af2e11202957c9 languageName: node linkType: hard -"mdast-util-gfm-task-list-item@npm:^1.0.0": - version: 1.0.2 - resolution: "mdast-util-gfm-task-list-item@npm:1.0.2" +"mdast-util-gfm-task-list-item@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-gfm-task-list-item@npm:2.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-to-markdown: ^1.3.0 - checksum: c9b86037d6953b84f11fb2fc3aa23d5b8e14ca0dfcb0eb2fb289200e172bb9d5647bfceb4f86606dc6d935e8d58f6a458c04d3e55e87ff8513c7d4ade976200b + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 37db90c59b15330fc54d790404abf5ef9f2f83e8961c53666fe7de4aab8dd5e6b3c296b6be19797456711a89a27840291d8871ff0438e9b4e15c89d170efe072 languageName: node linkType: hard -"mdast-util-gfm@npm:^2.0.0": - version: 2.0.2 - resolution: "mdast-util-gfm@npm:2.0.2" +"mdast-util-gfm@npm:^3.0.0": + version: 3.0.0 + resolution: "mdast-util-gfm@npm:3.0.0" dependencies: - mdast-util-from-markdown: ^1.0.0 - mdast-util-gfm-autolink-literal: ^1.0.0 - mdast-util-gfm-footnote: ^1.0.0 - mdast-util-gfm-strikethrough: ^1.0.0 - mdast-util-gfm-table: ^1.0.0 - mdast-util-gfm-task-list-item: ^1.0.0 - mdast-util-to-markdown: ^1.0.0 - checksum: 7078cb985255208bcbce94a121906417d38353c6b1a9acbe56ee8888010d3500608b5d51c16b0999ac63ca58848fb13012d55f26930ff6c6f3450f053d56514e + mdast-util-from-markdown: ^2.0.0 + mdast-util-gfm-autolink-literal: ^2.0.0 + mdast-util-gfm-footnote: ^2.0.0 + mdast-util-gfm-strikethrough: ^2.0.0 + mdast-util-gfm-table: ^2.0.0 + mdast-util-gfm-task-list-item: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 62039d2f682ae3821ea1c999454863d31faf94d67eb9b746589c7e136076d7fb35fabc67e02f025c7c26fd7919331a0ee1aabfae24f565d9a6a9ebab3371c626 languageName: node linkType: hard -"mdast-util-mdx-expression@npm:^1.0.0": - version: 1.3.2 - resolution: "mdast-util-mdx-expression@npm:1.3.2" +"mdast-util-mdx-expression@npm:^2.0.0": + version: 2.0.0 + resolution: "mdast-util-mdx-expression@npm:2.0.0" dependencies: "@types/estree-jsx": ^1.0.0 - "@types/hast": ^2.0.0 - "@types/mdast": ^3.0.0 - mdast-util-from-markdown: ^1.0.0 - mdast-util-to-markdown: ^1.0.0 - checksum: e4c90f26deaa5eb6217b0a9af559a80de41da02ab3bcd864c56bed3304b056ae703896e9876bc6ded500f4aff59f4de5cbf6a4b109a5ba408f2342805fe6dc05 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 4e1183000e183e07a7264e192889b4fd57372806103031c71b9318967f85fd50a5dd0f92ef14f42c331e77410808f5de3341d7bc8ad4ee91b7fa8f0a30043a8a languageName: node linkType: hard -"mdast-util-mdx-jsx@npm:^2.0.0": - version: 2.1.2 - resolution: "mdast-util-mdx-jsx@npm:2.1.2" +"mdast-util-mdx-jsx@npm:^3.0.0": + version: 3.0.0 + resolution: "mdast-util-mdx-jsx@npm:3.0.0" dependencies: "@types/estree-jsx": ^1.0.0 - "@types/hast": ^2.0.0 - "@types/mdast": ^3.0.0 - "@types/unist": ^2.0.0 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + "@types/unist": ^3.0.0 ccount: ^2.0.0 - mdast-util-from-markdown: ^1.1.0 - mdast-util-to-markdown: ^1.3.0 + devlop: ^1.1.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 parse-entities: ^4.0.0 stringify-entities: ^4.0.0 - unist-util-remove-position: ^4.0.0 - unist-util-stringify-position: ^3.0.0 - vfile-message: ^3.0.0 - checksum: 637e0bbd97c0c783f6b12bb05ccb1edaec076c5aa6d349147d77b8e6e10677f1be8e2870c05b1896f69095c9bc527f34be72b349b30737ab2e499bfc579b3a28 + unist-util-remove-position: ^5.0.0 + unist-util-stringify-position: ^4.0.0 + vfile-message: ^4.0.0 + checksum: 48fe1ba617205f3776ca2030d195adbdb42bb6c53326534db3f5bdd28abe7895103af8c4dfda7cbe2911e8cd71921bc8a82fe40856565e57af8b4f8a79c8c126 languageName: node linkType: hard -"mdast-util-mdx@npm:^2.0.0": - version: 2.0.1 - resolution: "mdast-util-mdx@npm:2.0.1" +"mdast-util-mdx@npm:^3.0.0": + version: 3.0.0 + resolution: "mdast-util-mdx@npm:3.0.0" dependencies: - mdast-util-from-markdown: ^1.0.0 - mdast-util-mdx-expression: ^1.0.0 - mdast-util-mdx-jsx: ^2.0.0 - mdast-util-mdxjs-esm: ^1.0.0 - mdast-util-to-markdown: ^1.0.0 - checksum: 7303149230a26e524e319833b782bffca94e49cdab012996618701259bd056e014ca22a35d25ffa8880ba9064ee126a2a002f01e5c90a31ca726339ed775875e + mdast-util-from-markdown: ^2.0.0 + mdast-util-mdx-expression: ^2.0.0 + mdast-util-mdx-jsx: ^3.0.0 + mdast-util-mdxjs-esm: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: e2b007d826fcd49fd57ed03e190753c8b0f7d9eff6c7cb26ba609cde15cd3a472c0cd5e4a1ee3e39a40f14be22fdb57de243e093cea0c064d6f3366cff3e3af2 languageName: node linkType: hard -"mdast-util-mdxjs-esm@npm:^1.0.0": - version: 1.3.1 - resolution: "mdast-util-mdxjs-esm@npm:1.3.1" +"mdast-util-mdxjs-esm@npm:^2.0.0": + version: 2.0.1 + resolution: "mdast-util-mdxjs-esm@npm:2.0.1" dependencies: "@types/estree-jsx": ^1.0.0 - "@types/hast": ^2.0.0 - "@types/mdast": ^3.0.0 - mdast-util-from-markdown: ^1.0.0 - mdast-util-to-markdown: ^1.0.0 - checksum: ee78a4f58adfec38723cbc920f05481201ebb001eff3982f2d0e5f5ce5c75685e732e9d361ad4a1be8b936b4e5de0f2599cb96b92ad4bd92698ac0c4a09bbec3 - languageName: node - linkType: hard - -"mdast-util-phrasing@npm:^3.0.0": - version: 3.0.1 - resolution: "mdast-util-phrasing@npm:3.0.1" - dependencies: - "@types/mdast": ^3.0.0 - unist-util-is: ^5.0.0 - checksum: c5b616d9b1eb76a6b351d195d94318494722525a12a89d9c8a3b091af7db3dd1fc55d294f9d29266d8159a8267b0df4a7a133bda8a3909d5331c383e1e1ff328 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + devlop: ^1.0.0 + mdast-util-from-markdown: ^2.0.0 + mdast-util-to-markdown: ^2.0.0 + checksum: 1f9dad04d31d59005332e9157ea9510dc1d03092aadbc607a10475c7eec1c158b475aa0601a3a4f74e13097ca735deb8c2d9d37928ddef25d3029fd7c9e14dc3 languageName: node linkType: hard @@ -7854,35 +7716,20 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-hast@npm:^12.1.0": - version: 12.3.0 - resolution: "mdast-util-to-hast@npm:12.3.0" +"mdast-util-to-hast@npm:^13.0.0": + version: 13.1.0 + resolution: "mdast-util-to-hast@npm:13.1.0" dependencies: - "@types/hast": ^2.0.0 - "@types/mdast": ^3.0.0 - mdast-util-definitions: ^5.0.0 - micromark-util-sanitize-uri: ^1.1.0 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + "@ungap/structured-clone": ^1.0.0 + devlop: ^1.0.0 + micromark-util-sanitize-uri: ^2.0.0 trim-lines: ^3.0.0 - unist-util-generated: ^2.0.0 - unist-util-position: ^4.0.0 - unist-util-visit: ^4.0.0 - checksum: ea40c9f07dd0b731754434e81c913590c611b1fd753fa02550a1492aadfc30fb3adecaf62345ebb03cea2ddd250c15ab6e578fffde69c19955c9b87b10f2a9bb - languageName: node - linkType: hard - -"mdast-util-to-markdown@npm:^1.0.0, mdast-util-to-markdown@npm:^1.3.0, mdast-util-to-markdown@npm:^1.5.0": - version: 1.5.0 - resolution: "mdast-util-to-markdown@npm:1.5.0" - dependencies: - "@types/mdast": ^3.0.0 - "@types/unist": ^2.0.0 - longest-streak: ^3.0.0 - mdast-util-phrasing: ^3.0.0 - mdast-util-to-string: ^3.0.0 - micromark-util-decode-string: ^1.0.0 - unist-util-visit: ^4.0.0 - zwitch: ^2.0.0 - checksum: 64338eb33e49bb0aea417591fd986f72fdd39205052563bb7ce9eb9ecc160824509bfacd740086a05af355c6d5c36353aafe95cab9e6927d674478757cee6259 + unist-util-position: ^5.0.0 + unist-util-visit: ^5.0.0 + vfile: ^6.0.0 + checksum: 640bc897286af8fe760cd477fb04bbf544a5a897cdc2220ce36fe2f892f067b483334610387aeb969511bd78a2d841a54851079cd676ac513d6a5ff75852514e languageName: node linkType: hard @@ -7902,15 +7749,6 @@ __metadata: languageName: node linkType: hard -"mdast-util-to-string@npm:^3.0.0, mdast-util-to-string@npm:^3.1.0, mdast-util-to-string@npm:^3.2.0": - version: 3.2.0 - resolution: "mdast-util-to-string@npm:3.2.0" - dependencies: - "@types/mdast": ^3.0.0 - checksum: dc40b544d54339878ae2c9f2b3198c029e1e07291d2126bd00ca28272ee6616d0d2194eb1c9828a7c34d412a79a7e73b26512a734698d891c710a1e73db1e848 - languageName: node - linkType: hard - "mdast-util-to-string@npm:^4.0.0": version: 4.0.0 resolution: "mdast-util-to-string@npm:4.0.0" @@ -7971,30 +7809,6 @@ __metadata: languageName: node linkType: hard -"micromark-core-commonmark@npm:^1.0.0, micromark-core-commonmark@npm:^1.0.1": - version: 1.0.6 - resolution: "micromark-core-commonmark@npm:1.0.6" - dependencies: - decode-named-character-reference: ^1.0.0 - micromark-factory-destination: ^1.0.0 - micromark-factory-label: ^1.0.0 - micromark-factory-space: ^1.0.0 - micromark-factory-title: ^1.0.0 - micromark-factory-whitespace: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-chunked: ^1.0.0 - micromark-util-classify-character: ^1.0.0 - micromark-util-html-tag-name: ^1.0.0 - micromark-util-normalize-identifier: ^1.0.0 - micromark-util-resolve-all: ^1.0.0 - micromark-util-subtokenize: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.1 - uvu: ^0.5.0 - checksum: 4b483c46077f696ed310f6d709bb9547434c218ceb5c1220fde1707175f6f68b44da15ab8668f9c801e1a123210071e3af883a7d1215122c913fd626f122bfc2 - languageName: node - linkType: hard - "micromark-core-commonmark@npm:^2.0.0": version: 2.0.0 resolution: "micromark-core-commonmark@npm:2.0.0" @@ -8019,18 +7833,18 @@ __metadata: languageName: node linkType: hard -"micromark-extension-directive@npm:^2.0.0": - version: 2.2.0 - resolution: "micromark-extension-directive@npm:2.2.0" +"micromark-extension-directive@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-directive@npm:3.0.0" dependencies: - micromark-factory-space: ^1.0.0 - micromark-factory-whitespace: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 + devlop: ^1.0.0 + micromark-factory-space: ^2.0.0 + micromark-factory-whitespace: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 parse-entities: ^4.0.0 - uvu: ^0.5.0 - checksum: 1256374a1327c2aecd4b3592de2c62ddf4798441d3a953f8414ecde05da2595a47428d94959b0bdb67605d4e195b95eb0b6e18ece44a50cbdf5a9c2c25d21905 + checksum: 8350106bdf039a544cba64cf7932261a710e07d73d43d6c645dd2b16577f30ebd04abf762e8ca74266f5de19938e1eeff6c237d79f8244dea23aef7f90df2c31 languageName: node linkType: hard @@ -8046,181 +7860,172 @@ __metadata: languageName: node linkType: hard -"micromark-extension-gfm-autolink-literal@npm:^1.0.0": - version: 1.0.3 - resolution: "micromark-extension-gfm-autolink-literal@npm:1.0.3" +"micromark-extension-gfm-autolink-literal@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-autolink-literal@npm:2.0.0" dependencies: - micromark-util-character: ^1.0.0 - micromark-util-sanitize-uri: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: bb181972ac346ca73ca1ab0b80b80c9d6509ed149799d2217d5442670f499c38a94edff73d32fa52b390d89640974cfbd7f29e4ad7d599581d5e1cabcae636a2 + micromark-util-character: ^2.0.0 + micromark-util-sanitize-uri: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: fa16d59528239262d6d04d539a052baf1f81275954ec8bfadea40d81bfc25667d5c8e68b225a5358626df5e30a3933173a67fdad2fed011d37810a10b770b0b2 languageName: node linkType: hard -"micromark-extension-gfm-footnote@npm:^1.0.0": - version: 1.1.0 - resolution: "micromark-extension-gfm-footnote@npm:1.1.0" +"micromark-extension-gfm-footnote@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-footnote@npm:2.0.0" dependencies: - micromark-core-commonmark: ^1.0.0 - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-normalize-identifier: ^1.0.0 - micromark-util-sanitize-uri: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: 7a5408625ef2cca5cc18e6591c2522a8a409f466a6fbc0ed938950aafe5fc9bf1eada65e1a4dd4e36ec3e7b24920de1f4b3e2c365d8f5cd2d6ccb1f8c2377c49 + devlop: ^1.0.0 + micromark-core-commonmark: ^2.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-normalize-identifier: ^2.0.0 + micromark-util-sanitize-uri: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: a426fddecfac6144fc622b845cd2dc09d46faa75be5b76ff022cb76a03301b1d4929a5e5e41e071491787936be65e03d0b03c7aebc0e0136b3cdbfadadd6632c languageName: node linkType: hard -"micromark-extension-gfm-strikethrough@npm:^1.0.0": - version: 1.0.5 - resolution: "micromark-extension-gfm-strikethrough@npm:1.0.5" +"micromark-extension-gfm-strikethrough@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-strikethrough@npm:2.0.0" dependencies: - micromark-util-chunked: ^1.0.0 - micromark-util-classify-character: ^1.0.0 - micromark-util-resolve-all: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: 548c0f257753d735c741533411957f04253da53db31e1f398dc5dc1de9f398c45586baad5223dce8f3b55f9433c255e6eb695fc3104256b8c332dd8737136882 + devlop: ^1.0.0 + micromark-util-chunked: ^2.0.0 + micromark-util-classify-character: ^2.0.0 + micromark-util-resolve-all: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 4e35fbbf364bfce08066b70acd94b9d393a8fd09a5afbe0bae70d0c8a174640b1ba86ab6b78ee38f411a813e2a718b07959216cf0063d823ba1c569a7694e5ad languageName: node linkType: hard -"micromark-extension-gfm-table@npm:^1.0.0": - version: 1.0.5 - resolution: "micromark-extension-gfm-table@npm:1.0.5" +"micromark-extension-gfm-table@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-table@npm:2.0.0" dependencies: - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: f0aab3b4333cc24b1534b08dc4cce986dd606df8b7ed913e5a1de9fe2d3ae67b2435663c0bc271b528874af4928e580e1ad540ea9117d7f2d74edb28859c97ef + devlop: ^1.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 71484dcf8db7b189da0528f472cc81e4d6d1a64ae43bbe7fcb7e2e1dba758a0a4f785f9f1afb9459fe5b4a02bbe023d78c95c05204414a14083052eb8219e5eb languageName: node linkType: hard -"micromark-extension-gfm-tagfilter@npm:^1.0.0": - version: 1.0.2 - resolution: "micromark-extension-gfm-tagfilter@npm:1.0.2" +"micromark-extension-gfm-tagfilter@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-gfm-tagfilter@npm:2.0.0" dependencies: - micromark-util-types: ^1.0.0 - checksum: 7d2441df51f890c86f8e7cf7d331a570b69c8105fa1c2fc5b737cb739502c16c8ee01cf35550a8a78f89497c5dfacc97cf82d55de6274e8320f3aec25e2b0dd2 + micromark-util-types: ^2.0.0 + checksum: cf21552f4a63592bfd6c96ae5d64a5f22bda4e77814e3f0501bfe80e7a49378ad140f827007f36044666f176b3a0d5fea7c2e8e7973ce4b4579b77789f01ae95 languageName: node linkType: hard -"micromark-extension-gfm-task-list-item@npm:^1.0.0": - version: 1.0.4 - resolution: "micromark-extension-gfm-task-list-item@npm:1.0.4" - dependencies: - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: 2575bb47b320f2479d3cc2492ba7cf79d6baa9cd0200c0ed120fd0e318e64e8ebab4a93a056a3781cb5107193f3b36ebd2d86a5928308bef45fc121291f97eb5 - languageName: node - linkType: hard - -"micromark-extension-gfm@npm:^2.0.0": +"micromark-extension-gfm-task-list-item@npm:^2.0.0": version: 2.0.1 - resolution: "micromark-extension-gfm@npm:2.0.1" + resolution: "micromark-extension-gfm-task-list-item@npm:2.0.1" dependencies: - micromark-extension-gfm-autolink-literal: ^1.0.0 - micromark-extension-gfm-footnote: ^1.0.0 - micromark-extension-gfm-strikethrough: ^1.0.0 - micromark-extension-gfm-table: ^1.0.0 - micromark-extension-gfm-tagfilter: ^1.0.0 - micromark-extension-gfm-task-list-item: ^1.0.0 - micromark-util-combine-extensions: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: b181479c87be38d5ae8d28e6dc52fab73c894fd2706876746f27a91fb186644ce03532a9c35dca2186327a0e2285cd5242ad0361dc89adedd4a50376ffd94e22 + devlop: ^1.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 80e569ab1a1d1f89d86af91482e9629e24b7e3f019c9d7989190f36a9367c6de723b2af48e908c1b73479f35b2215d3d38c1fdbf02ab01eb2fc90a59d1cf4465 languageName: node linkType: hard -"micromark-extension-mdx-expression@npm:^1.0.0": - version: 1.0.4 - resolution: "micromark-extension-mdx-expression@npm:1.0.4" +"micromark-extension-gfm@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-gfm@npm:3.0.0" dependencies: - micromark-factory-mdx-expression: ^1.0.0 - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-events-to-acorn: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: d19a31f9813dd5d4ad96b99e35b7c48067e69d75f92ec670dad5242857fb7688ba8b7c6a15616797b5df25dd89fd3b54916f93cb60ce2cfe97aca84739b45954 + micromark-extension-gfm-autolink-literal: ^2.0.0 + micromark-extension-gfm-footnote: ^2.0.0 + micromark-extension-gfm-strikethrough: ^2.0.0 + micromark-extension-gfm-table: ^2.0.0 + micromark-extension-gfm-tagfilter: ^2.0.0 + micromark-extension-gfm-task-list-item: ^2.0.0 + micromark-util-combine-extensions: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 2060fa62666a09532d6b3a272d413bc1b25bbb262f921d7402795ac021e1362c8913727e33d7528d5b4ccaf26922ec51208c43f795a702964817bc986de886c9 languageName: node linkType: hard -"micromark-extension-mdx-jsx@npm:^1.0.0": - version: 1.0.3 - resolution: "micromark-extension-mdx-jsx@npm:1.0.3" +"micromark-extension-mdx-expression@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdx-expression@npm:3.0.0" + dependencies: + "@types/estree": ^1.0.0 + devlop: ^1.0.0 + micromark-factory-mdx-expression: ^2.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-events-to-acorn: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: abd6ba0acdebc03bc0836c51a1ec4ca28e0be86f10420dd8cfbcd6c10dd37cd3f31e7c8b9792e9276e7526748883f4a30d0803d72b6285dae47d4e5348c23a10 + languageName: node + linkType: hard + +"micromark-extension-mdx-jsx@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdx-jsx@npm:3.0.0" dependencies: "@types/acorn": ^4.0.0 - estree-util-is-identifier-name: ^2.0.0 - micromark-factory-mdx-expression: ^1.0.0 - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - vfile-message: ^3.0.0 - checksum: 1a5566890aabc52fe96b78e3a3a507dee03a2232e44b9360b00617734e156f934e85bc6a477fbb856c793fe33c9fb7d2207a4f50e680168c0d04ba9c9336d960 + "@types/estree": ^1.0.0 + devlop: ^1.0.0 + estree-util-is-identifier-name: ^3.0.0 + micromark-factory-mdx-expression: ^2.0.0 + micromark-factory-space: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + vfile-message: ^4.0.0 + checksum: 5e2f45d381d1ce43afadc5376427b42ef8cd2a574ca3658473254eabe84db99ef1abc03055b3d86728fac7f1edfb1076e6f2f322ed8bfb1f2f14cafc2c8f0d0e languageName: node linkType: hard -"micromark-extension-mdx-md@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-extension-mdx-md@npm:1.0.0" +"micromark-extension-mdx-md@npm:^2.0.0": + version: 2.0.0 + resolution: "micromark-extension-mdx-md@npm:2.0.0" dependencies: - micromark-util-types: ^1.0.0 - checksum: b4f205e1d5f0946b4755541ef44ffd0b3be8c7ecfc08d8b139b6a21fbd3ff62d8fdb6b7e6d17bd9a3b610450267f43a41703dc48b341da9addd743a28cdefa64 + micromark-util-types: ^2.0.0 + checksum: 7daf03372fd7faddf3f0ac87bdb0debb0bb770f33b586f72251e1072b222ceee75400ab6194c0e130dbf1e077369a5b627be6e9130d7a2e9e6b849f0d18ff246 languageName: node linkType: hard -"micromark-extension-mdxjs-esm@npm:^1.0.0": - version: 1.0.3 - resolution: "micromark-extension-mdxjs-esm@npm:1.0.3" +"micromark-extension-mdxjs-esm@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdxjs-esm@npm:3.0.0" dependencies: - micromark-core-commonmark: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-events-to-acorn: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - unist-util-position-from-estree: ^1.1.0 - uvu: ^0.5.0 - vfile-message: ^3.0.0 - checksum: 756074656391a5e5bb96bc8a0e9c1df7d9f7be5299847c9719e6a90552e1c76a11876aa89986ad5da89ab485f776a4a43a61ea3acddd4f865a5cee43ac523ffd + "@types/estree": ^1.0.0 + devlop: ^1.0.0 + micromark-core-commonmark: ^2.0.0 + micromark-util-character: ^2.0.0 + micromark-util-events-to-acorn: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + unist-util-position-from-estree: ^2.0.0 + vfile-message: ^4.0.0 + checksum: fb33d850200afce567b95c90f2f7d42259bd33eea16154349e4fa77c3ec934f46c8e5c111acea16321dce3d9f85aaa4c49afe8b810e31b34effc11617aeee8f6 languageName: node linkType: hard -"micromark-extension-mdxjs@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-extension-mdxjs@npm:1.0.0" +"micromark-extension-mdxjs@npm:^3.0.0": + version: 3.0.0 + resolution: "micromark-extension-mdxjs@npm:3.0.0" dependencies: acorn: ^8.0.0 acorn-jsx: ^5.0.0 - micromark-extension-mdx-expression: ^1.0.0 - micromark-extension-mdx-jsx: ^1.0.0 - micromark-extension-mdx-md: ^1.0.0 - micromark-extension-mdxjs-esm: ^1.0.0 - micromark-util-combine-extensions: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: ba836c6d2dfc67597886e88f533ffa02f2029dbe216a0651f1066e70f8529a700bcc7fa2bc4201ee12fd3d1cd7da7093d5a442442daeb84b27df96aaffb7699c - languageName: node - linkType: hard - -"micromark-factory-destination@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-factory-destination@npm:1.0.0" - dependencies: - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: 8e733ae9c1c2342f14ff290bf09946e20f6f540117d80342377a765cac48df2ea5e748f33c8b07501ad7a43414b1a6597c8510ede2052b6bf1251fab89748e20 + micromark-extension-mdx-expression: ^3.0.0 + micromark-extension-mdx-jsx: ^3.0.0 + micromark-extension-mdx-md: ^2.0.0 + micromark-extension-mdxjs-esm: ^3.0.0 + micromark-util-combine-extensions: ^2.0.0 + micromark-util-types: ^2.0.0 + checksum: 7da6f0fb0e1e0270a2f5ad257e7422cc16e68efa7b8214c63c9d55bc264cb872e9ca4ac9a71b9dfd13daf52e010f730bac316086f4340e4fcc6569ec699915bf languageName: node linkType: hard @@ -8235,18 +8040,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-label@npm:^1.0.0": - version: 1.0.2 - resolution: "micromark-factory-label@npm:1.0.2" - dependencies: - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: 957e9366bdc8dbc1437c0706ff96972fa985ab4b1274abcae12f6094f527cbf5c69e7f2304c23c7f4b96e311ff7911d226563b8b43dcfcd4091e8c985fb97ce6 - languageName: node - linkType: hard - "micromark-factory-label@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-label@npm:2.0.0" @@ -8259,19 +8052,19 @@ __metadata: languageName: node linkType: hard -"micromark-factory-mdx-expression@npm:^1.0.0": - version: 1.0.7 - resolution: "micromark-factory-mdx-expression@npm:1.0.7" +"micromark-factory-mdx-expression@npm:^2.0.0": + version: 2.0.1 + resolution: "micromark-factory-mdx-expression@npm:2.0.1" dependencies: - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-events-to-acorn: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - unist-util-position-from-estree: ^1.0.0 - uvu: ^0.5.0 - vfile-message: ^3.0.0 - checksum: e7893f21576bcb7755d341e45d3ff202ba466fa2278c6f31ae4db4002a28d6d13a4efad331ef46223372ec2010d9bc2ff27e2cd57a4580be6491e59ca21ba59d + "@types/estree": ^1.0.0 + devlop: ^1.0.0 + micromark-util-character: ^2.0.0 + micromark-util-events-to-acorn: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + unist-util-position-from-estree: ^2.0.0 + vfile-message: ^4.0.0 + checksum: 2ba0ae939d0174a5e5331b1a4c203b96862ccf06e8903d6bdcc2d51f75515e52d407cd394afcd182f9ff0e877dc2a14e3fa430ced0131e156650d45104de8311 languageName: node linkType: hard @@ -8295,19 +8088,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-title@npm:^1.0.0": - version: 1.0.2 - resolution: "micromark-factory-title@npm:1.0.2" - dependencies: - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: 9a9cf66babde0bad1e25d6c1087082bfde6dfc319a36cab67c89651cc1a53d0e21cdec83262b5a4c33bff49f0e3c8dc2a7bd464e991d40dbea166a8f9b37e5b2 - languageName: node - linkType: hard - "micromark-factory-title@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-title@npm:2.0.0" @@ -8320,18 +8100,6 @@ __metadata: languageName: node linkType: hard -"micromark-factory-whitespace@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-factory-whitespace@npm:1.0.0" - dependencies: - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: 0888386e6ea2dd665a5182c570d9b3d0a172d3f11694ca5a2a84e552149c9f1429f5b975ec26e1f0fa4388c55a656c9f359ce5e0603aff6175ba3e255076f20b - languageName: node - linkType: hard - "micromark-factory-whitespace@npm:^2.0.0": version: 2.0.0 resolution: "micromark-factory-whitespace@npm:2.0.0" @@ -8364,15 +8132,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-chunked@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-chunked@npm:1.0.0" - dependencies: - micromark-util-symbol: ^1.0.0 - checksum: c1efd56e8c4217bcf1c6f1a9fb9912b4a2a5503b00d031da902be922fb3fee60409ac53f11739991291357b2784fb0647ddfc74c94753a068646c0cb0fd71421 - languageName: node - linkType: hard - "micromark-util-chunked@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-chunked@npm:2.0.0" @@ -8382,17 +8141,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-classify-character@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-classify-character@npm:1.0.0" - dependencies: - micromark-util-character: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: 180446e6a1dec653f625ded028f244784e1db8d10ad05c5d70f08af9de393b4a03dc6cf6fa5ed8ccc9c24bbece7837abf3bf66681c0b4adf159364b7d5236dfd - languageName: node - linkType: hard - "micromark-util-classify-character@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-classify-character@npm:2.0.0" @@ -8404,16 +8152,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-combine-extensions@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-combine-extensions@npm:1.0.0" - dependencies: - micromark-util-chunked: ^1.0.0 - micromark-util-types: ^1.0.0 - checksum: 5304a820ef75340e1be69d6ad167055b6ba9a3bafe8171e5945a935752f462415a9dd61eb3490220c055a8a11167209a45bfa73f278338b7d3d61fa1464d3f35 - languageName: node - linkType: hard - "micromark-util-combine-extensions@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-combine-extensions@npm:2.0.0" @@ -8424,15 +8162,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-decode-numeric-character-reference@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-decode-numeric-character-reference@npm:1.0.0" - dependencies: - micromark-util-symbol: ^1.0.0 - checksum: f3ae2bb582a80f1e9d3face026f585c0c472335c064bd850bde152376f0394cb2831746749b6be6e0160f7d73626f67d10716026c04c87f402c0dd45a1a28633 - languageName: node - linkType: hard - "micromark-util-decode-numeric-character-reference@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-decode-numeric-character-reference@npm:2.0.0" @@ -8442,18 +8171,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-decode-string@npm:^1.0.0": - version: 1.0.2 - resolution: "micromark-util-decode-string@npm:1.0.2" - dependencies: - decode-named-character-reference: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-decode-numeric-character-reference: ^1.0.0 - micromark-util-symbol: ^1.0.0 - checksum: 2dbb41c9691cc71505d39706405139fb7d6699429d577a524c7c248ac0cfd09d3dd212ad8e91c143a00b2896f26f81136edc67c5bda32d20446f0834d261b17a - languageName: node - linkType: hard - "micromark-util-decode-string@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-decode-string@npm:2.0.0" @@ -8466,13 +8183,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-encode@npm:^1.0.0": - version: 1.0.1 - resolution: "micromark-util-encode@npm:1.0.1" - checksum: 9290583abfdc79ea3e7eb92c012c47a0e14327888f8aaa6f57ff79b3058d8e7743716b9d91abca3646f15ab3d78fdad9779fdb4ccf13349cd53309dfc845253a - languageName: node - linkType: hard - "micromark-util-encode@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-encode@npm:2.0.0" @@ -8480,25 +8190,19 @@ __metadata: languageName: node linkType: hard -"micromark-util-events-to-acorn@npm:^1.0.0": - version: 1.2.1 - resolution: "micromark-util-events-to-acorn@npm:1.2.1" +"micromark-util-events-to-acorn@npm:^2.0.0": + version: 2.0.2 + resolution: "micromark-util-events-to-acorn@npm:2.0.2" dependencies: "@types/acorn": ^4.0.0 "@types/estree": ^1.0.0 - estree-util-visit: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - vfile-location: ^4.0.0 - vfile-message: ^3.0.0 - checksum: baf1cad66d860980cf20963f641c48c434e5be5802beabefdda21be136ae037845dd236b5e9ce5cf9409bf1b9ba8b4131a396d3a5bfa12098dae13e4a9724f2b - languageName: node - linkType: hard - -"micromark-util-html-tag-name@npm:^1.0.0": - version: 1.1.0 - resolution: "micromark-util-html-tag-name@npm:1.1.0" - checksum: a9b783cec89ec813648d59799464c1950fe281ae797b2a965f98ad0167d7fa1a247718eff023b4c015f47211a172f9446b8e6b98aad50e3cd44a3337317dad2c + "@types/unist": ^3.0.0 + devlop: ^1.0.0 + estree-util-visit: ^2.0.0 + micromark-util-symbol: ^2.0.0 + micromark-util-types: ^2.0.0 + vfile-message: ^4.0.0 + checksum: bcb3eeac52a4ae5c3ca3d8cff514de3a7d1f272d9a94cce26a08c578bef64df4d61820874c01207e92fcace9eae5c9a7ecdddef0c6e10014b255a07b7880bf94 languageName: node linkType: hard @@ -8509,15 +8213,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-normalize-identifier@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-normalize-identifier@npm:1.0.0" - dependencies: - micromark-util-symbol: ^1.0.0 - checksum: d7c09d5e8318fb72f194af72664bd84a48a2928e3550b2b21c8fbc0ec22524f2a72e0f6663d2b95dc189a6957d3d7759b60716e888909710767cd557be821f8b - languageName: node - linkType: hard - "micromark-util-normalize-identifier@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-normalize-identifier@npm:2.0.0" @@ -8527,15 +8222,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-resolve-all@npm:^1.0.0": - version: 1.0.0 - resolution: "micromark-util-resolve-all@npm:1.0.0" - dependencies: - micromark-util-types: ^1.0.0 - checksum: 409667f2bd126ef8acce009270d2aecaaa5584c5807672bc657b09e50aa91bd2e552cf41e5be1e6469244a83349cbb71daf6059b746b1c44e3f35446fef63e50 - languageName: node - linkType: hard - "micromark-util-resolve-all@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-resolve-all@npm:2.0.0" @@ -8545,17 +8231,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-sanitize-uri@npm:^1.0.0, micromark-util-sanitize-uri@npm:^1.1.0": - version: 1.1.0 - resolution: "micromark-util-sanitize-uri@npm:1.1.0" - dependencies: - micromark-util-character: ^1.0.0 - micromark-util-encode: ^1.0.0 - micromark-util-symbol: ^1.0.0 - checksum: fe6093faa0adeb8fad606184d927ce37f207dcc2ec7256438e7f273c8829686245dd6161b597913ef25a3c4fb61863d3612a40cb04cf15f83ba1b4087099996b - languageName: node - linkType: hard - "micromark-util-sanitize-uri@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-sanitize-uri@npm:2.0.0" @@ -8567,18 +8242,6 @@ __metadata: languageName: node linkType: hard -"micromark-util-subtokenize@npm:^1.0.0": - version: 1.0.2 - resolution: "micromark-util-subtokenize@npm:1.0.2" - dependencies: - micromark-util-chunked: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.0 - uvu: ^0.5.0 - checksum: c32ee58a7e1384ab1161a9ee02fbb04ad7b6e96d0b8c93dba9803c329a53d07f22ab394c7a96b2e30d6b8fbe3585b85817dba07277b1317111fc234e166bd2d1 - languageName: node - linkType: hard - "micromark-util-subtokenize@npm:^2.0.0": version: 2.0.0 resolution: "micromark-util-subtokenize@npm:2.0.0" @@ -8605,7 +8268,7 @@ __metadata: languageName: node linkType: hard -"micromark-util-types@npm:^1.0.0, micromark-util-types@npm:^1.0.1": +"micromark-util-types@npm:^1.0.0": version: 1.0.2 resolution: "micromark-util-types@npm:1.0.2" checksum: 08dc901b7c06ee3dfeb54befca05cbdab9525c1cf1c1080967c3878c9e72cb9856c7e8ff6112816e18ead36ce6f99d55aaa91560768f2f6417b415dcba1244df @@ -8619,31 +8282,6 @@ __metadata: languageName: node linkType: hard -"micromark@npm:^3.0.0": - version: 3.1.0 - resolution: "micromark@npm:3.1.0" - dependencies: - "@types/debug": ^4.0.0 - debug: ^4.0.0 - decode-named-character-reference: ^1.0.0 - micromark-core-commonmark: ^1.0.1 - micromark-factory-space: ^1.0.0 - micromark-util-character: ^1.0.0 - micromark-util-chunked: ^1.0.0 - micromark-util-combine-extensions: ^1.0.0 - micromark-util-decode-numeric-character-reference: ^1.0.0 - micromark-util-encode: ^1.0.0 - micromark-util-normalize-identifier: ^1.0.0 - micromark-util-resolve-all: ^1.0.0 - micromark-util-sanitize-uri: ^1.0.0 - micromark-util-subtokenize: ^1.0.0 - micromark-util-symbol: ^1.0.0 - micromark-util-types: ^1.0.1 - uvu: ^0.5.0 - checksum: 5fe5bc3bf92e2ddd37b5f0034080fc3a4d4b3c1130dd5e435bb96ec75e9453091272852e71a4d74906a8fcf992d6f79d794607657c534bda49941e9950a92e28 - languageName: node - linkType: hard - "micromark@npm:^4.0.0": version: 4.0.0 resolution: "micromark@npm:4.0.0" @@ -8702,7 +8340,7 @@ __metadata: languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.27, mime-types@npm:^2.1.31, mime-types@npm:~2.1.17, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -8777,7 +8415,7 @@ __metadata: languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.7": +"minimist@npm:^1.2.0": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 @@ -8872,13 +8510,6 @@ __metadata: languageName: node linkType: hard -"mri@npm:^1.1.0": - version: 1.2.0 - resolution: "mri@npm:1.2.0" - checksum: 83f515abbcff60150873e424894a2f65d68037e5a7fcde8a9e2b285ee9c13ac581b63cfc1e6826c4732de3aeb84902f7c1e16b7aff46cd3f897a0f757a894e85 - languageName: node - linkType: hard - "mrmime@npm:^1.0.0": version: 1.0.1 resolution: "mrmime@npm:1.0.1" @@ -8952,26 +8583,15 @@ __metadata: languageName: node linkType: hard -"node-emoji@npm:^1.10.0": - version: 1.11.0 - resolution: "node-emoji@npm:1.11.0" +"node-emoji@npm:^2.1.0": + version: 2.1.3 + resolution: "node-emoji@npm:2.1.3" dependencies: - lodash: ^4.17.21 - checksum: e8c856c04a1645062112a72e59a98b203505ed5111ff84a3a5f40611afa229b578c7d50f1e6a7f17aa62baeea4a640d2e2f61f63afc05423aa267af10977fb2b - languageName: node - linkType: hard - -"node-fetch@npm:2.6.7": - version: 2.6.7 - resolution: "node-fetch@npm:2.6.7" - dependencies: - whatwg-url: ^5.0.0 - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b + "@sindresorhus/is": ^4.6.0 + char-regex: ^1.0.2 + emojilib: ^2.4.0 + skin-tone: ^2.0.0 + checksum: 9ae5a1fb12fd5ce6885f251f345986115de4bb82e7d06fdc943845fb19260d89d0aaaccbaf85cae39fe7aaa1fc391640558865ba690c9bb8a7236c3ac10bbab0 languageName: node linkType: hard @@ -9002,10 +8622,10 @@ __metadata: languageName: node linkType: hard -"node-releases@npm:^2.0.13": - version: 2.0.13 - resolution: "node-releases@npm:2.0.13" - checksum: 17ec8f315dba62710cae71a8dad3cd0288ba943d2ece43504b3b1aa8625bf138637798ab470b1d9035b0545996f63000a8a926e0f6d35d0996424f8b6d36dda3 +"node-releases@npm:^2.0.14": + version: 2.0.14 + resolution: "node-releases@npm:2.0.14" + checksum: 59443a2f77acac854c42d321bf1b43dea0aef55cd544c6a686e9816a697300458d4e82239e2d794ea05f7bbbc8a94500332e2d3ac3f11f52e4b16cbe638b3c41 languageName: node linkType: hard @@ -9085,7 +8705,7 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": +"object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f @@ -9342,13 +8962,6 @@ __metadata: languageName: node linkType: hard -"parse5@npm:^6.0.0": - version: 6.0.1 - resolution: "parse5@npm:6.0.1" - checksum: 7d569a176c5460897f7c8f3377eff640d54132b9be51ae8a8fa4979af940830b2b0c296ce75e5bd8f4041520aadde13170dbdec44889975f906098ea0002f4bd - languageName: node - linkType: hard - "parse5@npm:^7.0.0": version: 7.1.2 resolution: "parse5@npm:7.1.2" @@ -9977,15 +9590,15 @@ __metadata: languageName: node linkType: hard -"prism-react-renderer@npm:^2.1.0": - version: 2.1.0 - resolution: "prism-react-renderer@npm:2.1.0" +"prism-react-renderer@npm:^2.3.0": + version: 2.3.1 + resolution: "prism-react-renderer@npm:2.3.1" dependencies: "@types/prismjs": ^1.26.0 - clsx: ^1.2.1 + clsx: ^2.0.0 peerDependencies: react: ">=16.0.0" - checksum: 61b4eb22bdbf01005a0d7ec2a24a27b69e28f124a1fbbfc2adb4d7d41a7929ea94d5ce506a361dd5a230728402f02595d521d9a5286d74ec9b34be0896c513a5 + checksum: b12a7d502c1e764d94f7d3c84aee9cd6fccc676bb7e21dee94d37eb2e7e62e097a343999e1979887cb83a57cbdea48d2046aa74d07bce05caa25f4c296df30b6 languageName: node linkType: hard @@ -10020,15 +9633,6 @@ __metadata: languageName: node linkType: hard -"promise@npm:^7.1.1": - version: 7.3.1 - resolution: "promise@npm:7.3.1" - dependencies: - asap: ~2.0.3 - checksum: 475bb069130179fbd27ed2ab45f26d8862376a137a57314cf53310bdd85cc986a826fd585829be97ebc0aaf10e9d8e68be1bfe5a4a0364144b1f9eedfa940cf1 - languageName: node - linkType: hard - "prompts@npm:^2.4.2": version: 2.4.2 resolution: "prompts@npm:2.4.2" @@ -10097,13 +9701,6 @@ __metadata: languageName: node linkType: hard -"pure-color@npm:^1.2.0": - version: 1.3.0 - resolution: "pure-color@npm:1.3.0" - checksum: 646d8bed6e6eab89affdd5e2c11f607a85b631a7fb03c061dfa658eb4dc4806881a15feed2ac5fd8c0bad8c00c632c640d5b1cb8b9a972e6e947393a1329371b - languageName: node - linkType: hard - "qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" @@ -10185,18 +9782,6 @@ __metadata: languageName: node linkType: hard -"react-base16-styling@npm:~0.6.0": - version: 0.6.0 - resolution: "react-base16-styling@npm:0.6.0" - dependencies: - base16: ^1.0.0 - lodash.curry: ^4.0.1 - lodash.flow: ^3.3.0 - pure-color: ^1.2.0 - checksum: 00a12dddafc8a9025cca933b0dcb65fca41c81fa176d1fc3a6a9d0242127042e2c0a604f4c724a3254dd2c6aeb5ef55095522ff22f5462e419641c1341a658e4 - languageName: node - linkType: hard - "react-dev-utils@npm:^12.0.1": version: 12.0.1 resolution: "react-dev-utils@npm:12.0.1" @@ -10278,10 +9863,12 @@ __metadata: languageName: node linkType: hard -"react-lifecycles-compat@npm:~3.0.4": - version: 3.0.4 - resolution: "react-lifecycles-compat@npm:3.0.4" - checksum: a904b0fc0a8eeb15a148c9feb7bc17cec7ef96e71188280061fc340043fd6d8ee3ff233381f0e8f95c1cf926210b2c4a31f38182c8f35ac55057e453d6df204f +"react-json-view-lite@npm:^1.2.0": + version: 1.2.1 + resolution: "react-json-view-lite@npm:1.2.1" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + checksum: 9441260033ec07991b0121281d846f9d3e8db9061ea0dc3938f7003630f515d47870a465d90f1f9300ebe406679986657a4062c009c2a2084e2e145e6fa05a47 languageName: node linkType: hard @@ -10345,19 +9932,6 @@ __metadata: languageName: node linkType: hard -"react-textarea-autosize@npm:~8.3.2": - version: 8.3.4 - resolution: "react-textarea-autosize@npm:8.3.4" - dependencies: - "@babel/runtime": ^7.10.2 - use-composed-ref: ^1.3.0 - use-latest: ^1.2.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: 87360d4392276d4e87511a73be9b0634b8bcce8f4f648cf659334d993f25ad3d4062f468f1e1944fc614123acae4299580aad00b760c6a96cec190e076f847f5 - languageName: node - linkType: hard - "react@npm:^18.0.0": version: 18.2.0 resolution: "react@npm:18.2.0" @@ -10502,14 +10076,14 @@ __metadata: languageName: node linkType: hard -"rehype-raw@npm:^6.1.1": - version: 6.1.1 - resolution: "rehype-raw@npm:6.1.1" +"rehype-raw@npm:^7.0.0": + version: 7.0.0 + resolution: "rehype-raw@npm:7.0.0" dependencies: - "@types/hast": ^2.0.0 - hast-util-raw: ^7.2.0 - unified: ^10.0.0 - checksum: a1f9d309e609f49fb1f1e06e722705f4dd2e569653a89f756eaccb33b612cf1bb511216a81d10a619d11d047afc161e4b3cb99b957df05a8ba8fdbd5843f949a + "@types/hast": ^3.0.0 + hast-util-raw: ^9.0.0 + vfile: ^6.0.0 + checksum: f9e28dcbf4c6c7d91a97c10a840310f18ef3268aa45abb3e0428b6b191ff3c4fa8f753b910d768588a2dac5c7da7e557b4ddc3f1b6cd252e8d20cb62d60c65ed languageName: node linkType: hard @@ -10520,26 +10094,28 @@ __metadata: languageName: node linkType: hard -"remark-directive@npm:^2.0.1": - version: 2.0.1 - resolution: "remark-directive@npm:2.0.1" +"remark-directive@npm:^3.0.0": + version: 3.0.0 + resolution: "remark-directive@npm:3.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-directive: ^2.0.0 - micromark-extension-directive: ^2.0.0 - unified: ^10.0.0 - checksum: 61539926813654011431cc34e3d6c5ba702a6b610a0e8210b4748828809e86614bbb3e2de5fdee59eed24501f95c41665b175b852cc26544079789db10ac6cd7 + "@types/mdast": ^4.0.0 + mdast-util-directive: ^3.0.0 + micromark-extension-directive: ^3.0.0 + unified: ^11.0.0 + checksum: 744d12bbe924bd0492a2481cbaf9250aa6622c0d2cc090bb7bc39975e355c8a46ae13cc4793204ada39f0af64c953f6b730a55420a50375e0f74a5dd5d201089 languageName: node linkType: hard -"remark-emoji@npm:^2.2.0": - version: 2.2.0 - resolution: "remark-emoji@npm:2.2.0" +"remark-emoji@npm:^4.0.0": + version: 4.0.1 + resolution: "remark-emoji@npm:4.0.1" dependencies: - emoticon: ^3.2.0 - node-emoji: ^1.10.0 - unist-util-visit: ^2.0.3 - checksum: 638d4be72eb4110a447f389d4b8c454921f188c0acabf1b6579f3ddaa301ee91010173d6eebd975ea622ae3de7ed4531c0315a4ffd4f9653d80c599ef9ec21a8 + "@types/mdast": ^4.0.2 + emoticon: ^4.0.1 + mdast-util-find-and-replace: ^3.0.1 + node-emoji: ^2.1.0 + unified: ^11.0.4 + checksum: 2c02d8c0b694535a9f0c4fe39180cb89a8fbd07eb873c94842c34dfde566b8a6703df9d28fe175a8c28584f96252121de722862baa756f2d875f2f1a4352c1f4 languageName: node linkType: hard @@ -10555,48 +10131,63 @@ __metadata: languageName: node linkType: hard -"remark-gfm@npm:^3.0.1": - version: 3.0.1 - resolution: "remark-gfm@npm:3.0.1" +"remark-gfm@npm:^4.0.0": + version: 4.0.0 + resolution: "remark-gfm@npm:4.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-gfm: ^2.0.0 - micromark-extension-gfm: ^2.0.0 - unified: ^10.0.0 - checksum: 02254f74d67b3419c2c9cf62d799ec35f6c6cd74db25c001361751991552a7ce86049a972107bff8122d85d15ae4a8d1a0618f3bc01a7df837af021ae9b2a04e + "@types/mdast": ^4.0.0 + mdast-util-gfm: ^3.0.0 + micromark-extension-gfm: ^3.0.0 + remark-parse: ^11.0.0 + remark-stringify: ^11.0.0 + unified: ^11.0.0 + checksum: 84bea84e388061fbbb697b4b666089f5c328aa04d19dc544c229b607446bc10902e46b67b9594415a1017bbbd7c811c1f0c30d36682c6d1a6718b66a1558261b languageName: node linkType: hard -"remark-mdx@npm:^2.0.0": - version: 2.3.0 - resolution: "remark-mdx@npm:2.3.0" +"remark-mdx@npm:^3.0.0": + version: 3.0.0 + resolution: "remark-mdx@npm:3.0.0" dependencies: - mdast-util-mdx: ^2.0.0 - micromark-extension-mdxjs: ^1.0.0 - checksum: 98486986c5b6f6a8321eb2f3b13c70fcd5644821428c77b7bfeb5ee5d4605b9761b322b2f6b531e83883cd2d5bc7bc4623427149aee00e1eba012f538b3d5627 + mdast-util-mdx: ^3.0.0 + micromark-extension-mdxjs: ^3.0.0 + checksum: 8b9b3e5297e5cb4c312553f42c3720280ada96ae60ede880606924a0aad2e773106aba1ef45a0c179c218f8da6f58dac3c789a9c4f791649ca7a183706cde5b8 languageName: node linkType: hard -"remark-parse@npm:^10.0.0": - version: 10.0.1 - resolution: "remark-parse@npm:10.0.1" +"remark-parse@npm:^11.0.0": + version: 11.0.0 + resolution: "remark-parse@npm:11.0.0" dependencies: - "@types/mdast": ^3.0.0 - mdast-util-from-markdown: ^1.0.0 - unified: ^10.0.0 - checksum: 505088e564ab53ff054433368adbb7b551f69240c7d9768975529837a86f1d0f085e72d6211929c5c42db315273df4afc94f3d3a8662ffdb69468534c6643d29 + "@types/mdast": ^4.0.0 + mdast-util-from-markdown: ^2.0.0 + micromark-util-types: ^2.0.0 + unified: ^11.0.0 + checksum: d83d245290fa84bb04fb3e78111f09c74f7417e7c012a64dd8dc04fccc3699036d828fbd8eeec8944f774b6c30cc1d925c98f8c46495ebcee7c595496342ab7f languageName: node linkType: hard -"remark-rehype@npm:^10.0.0": - version: 10.1.0 - resolution: "remark-rehype@npm:10.1.0" +"remark-rehype@npm:^11.0.0": + version: 11.1.0 + resolution: "remark-rehype@npm:11.1.0" dependencies: - "@types/hast": ^2.0.0 - "@types/mdast": ^3.0.0 - mdast-util-to-hast: ^12.1.0 - unified: ^10.0.0 - checksum: b9ac8acff3383b204dfdc2599d0bdf86e6ca7e837033209584af2e6aaa6a9013e519a379afa3201299798cab7298c8f4b388de118c312c67234c133318aec084 + "@types/hast": ^3.0.0 + "@types/mdast": ^4.0.0 + mdast-util-to-hast: ^13.0.0 + unified: ^11.0.0 + vfile: ^6.0.0 + checksum: f0c731f0ab92a122e7f9c9bcbd10d6a31fdb99f0ea3595d232ddd9f9d11a308c4ec0aff4d56e1d0d256042dfad7df23b9941e50b5038da29786959a5926814e1 + languageName: node + linkType: hard + +"remark-stringify@npm:^11.0.0": + version: 11.0.0 + resolution: "remark-stringify@npm:11.0.0" + dependencies: + "@types/mdast": ^4.0.0 + mdast-util-to-markdown: ^2.0.0 + unified: ^11.0.0 + checksum: 59e07460eb629d6c3b3c0f438b0b236e7e6858fd5ab770303078f5a556ec00354d9c7fb9ef6d5f745a4617ac7da1ab618b170fbb4dac120e183fecd9cc86bce6 languageName: node linkType: hard @@ -10752,24 +10343,6 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^7.8.0": - version: 7.8.1 - resolution: "rxjs@npm:7.8.1" - dependencies: - tslib: ^2.1.0 - checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 - languageName: node - linkType: hard - -"sade@npm:^1.7.3": - version: 1.8.1 - resolution: "sade@npm:1.8.1" - dependencies: - mri: ^1.1.0 - checksum: 0756e5b04c51ccdc8221ebffd1548d0ce5a783a44a0fa9017a026659b97d632913e78f7dca59f2496aa996a0be0b0c322afd87ca72ccd909406f49dbffa0f45d - languageName: node - linkType: hard - "safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": version: 5.1.2 resolution: "safe-buffer@npm:5.1.2" @@ -11014,13 +10587,6 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:^1.0.5": - version: 1.0.5 - resolution: "setimmediate@npm:1.0.5" - checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd - languageName: node - linkType: hard - "setprototypeof@npm:1.1.0": version: 1.1.0 resolution: "setprototypeof@npm:1.1.0" @@ -11137,6 +10703,15 @@ __metadata: languageName: node linkType: hard +"skin-tone@npm:^2.0.0": + version: 2.0.0 + resolution: "skin-tone@npm:2.0.0" + dependencies: + unicode-emoji-modifier-base: ^1.0.0 + checksum: 19de157586b8019cacc55eb25d9d640f00fc02415761f3e41a4527142970fd4e7f6af0333bc90e879858766c20a976107bb386ffd4c812289c01d51f2c8d182c + languageName: node + linkType: hard + "slash@npm:^3.0.0": version: 3.0.0 resolution: "slash@npm:3.0.0" @@ -11420,12 +10995,21 @@ __metadata: languageName: node linkType: hard -"style-to-object@npm:^0.4.1": - version: 0.4.1 - resolution: "style-to-object@npm:0.4.1" +"style-to-object@npm:^0.4.0": + version: 0.4.4 + resolution: "style-to-object@npm:0.4.4" dependencies: inline-style-parser: 0.1.1 - checksum: 2ea213e98eed21764ae1d1dc9359231a9f2d480d6ba55344c4c15eb275f0809f1845786e66d4caf62414a5cc8f112ce9425a58d251c77224060373e0db48f8c2 + checksum: 41656c06f93ac0a7ac260ebc2f9d09a8bd74b8ec1836f358cc58e169235835a3a356977891d2ebbd76f0e08a53616929069199f9cce543214d3dc98346e19c9a + languageName: node + linkType: hard + +"style-to-object@npm:^1.0.0": + version: 1.0.5 + resolution: "style-to-object@npm:1.0.5" + dependencies: + inline-style-parser: 0.2.2 + checksum: 6201063204b6a94645f81b189452b2ca3e63d61867ec48523f4d52609c81e96176739fa12020d97fbbf023efb57a6f7ec3a15fb3a7fb7eb3ffea0b52b9dd6b8c languageName: node linkType: hard @@ -11631,13 +11215,6 @@ __metadata: languageName: node linkType: hard -"tr46@npm:~0.0.3": - version: 0.0.3 - resolution: "tr46@npm:0.0.3" - checksum: 726321c5eaf41b5002e17ffbd1fb7245999a073e8979085dacd47c4b4e8068ff5777142fc6726d6ca1fd2ff16921b48788b87225cbc57c72636f6efa8efbffe3 - languageName: node - linkType: hard - "trim-lines@npm:^3.0.0": version: 3.0.1 resolution: "trim-lines@npm:3.0.1" @@ -11652,7 +11229,7 @@ __metadata: languageName: node linkType: hard -"tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.6.0": +"tslib@npm:^2.0.3, tslib@npm:^2.6.0": version: 2.6.2 resolution: "tslib@npm:2.6.2" checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad @@ -11712,13 +11289,6 @@ __metadata: languageName: node linkType: hard -"ua-parser-js@npm:^0.7.30": - version: 0.7.33 - resolution: "ua-parser-js@npm:0.7.33" - checksum: 1510e9ec26fcaf0d8c6ae8f1078a8230e8816f083e1b5f453ea19d06b8ef2b8a596601c92148fd41899e8b3e5f83fa69c42332bd5729b931a721040339831696 - languageName: node - linkType: hard - "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -11726,6 +11296,13 @@ __metadata: languageName: node linkType: hard +"unicode-emoji-modifier-base@npm:^1.0.0": + version: 1.0.0 + resolution: "unicode-emoji-modifier-base@npm:1.0.0" + checksum: 6e1521d35fa69493207eb8b41f8edb95985d8b3faf07c01d820a1830b5e8403e20002563e2f84683e8e962a49beccae789f0879356bf92a4ec7a4dd8e2d16fdb + languageName: node + linkType: hard + "unicode-match-property-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-match-property-ecmascript@npm:2.0.0" @@ -11750,24 +11327,9 @@ __metadata: languageName: node linkType: hard -"unified@npm:^10.0.0, unified@npm:^10.1.2": - version: 10.1.2 - resolution: "unified@npm:10.1.2" - dependencies: - "@types/unist": ^2.0.0 - bail: ^2.0.0 - extend: ^3.0.0 - is-buffer: ^2.0.0 - is-plain-obj: ^4.0.0 - trough: ^2.0.0 - vfile: ^5.0.0 - checksum: 053e7c65ede644607f87bd625a299e4b709869d2f76ec8138569e6e886903b6988b21cd9699e471eda42bee189527be0a9dac05936f1d069a5e65d0125d5d756 - languageName: node - linkType: hard - -"unified@npm:^11.0.0": - version: 11.0.3 - resolution: "unified@npm:11.0.3" +"unified@npm:^11.0.0, unified@npm:^11.0.3, unified@npm:^11.0.4": + version: 11.0.4 + resolution: "unified@npm:11.0.4" dependencies: "@types/unist": ^3.0.0 bail: ^2.0.0 @@ -11776,7 +11338,7 @@ __metadata: is-plain-obj: ^4.0.0 trough: ^2.0.0 vfile: ^6.0.0 - checksum: 402d6b397b98f8966993faca0e1480f5f1825cc44df6a5236b75ab099f14b10ed808e578155c3f535e60676c12ee3f52346ca08d64343a72181d7a6b4d32011d + checksum: cfb023913480ac2bd5e787ffb8c27782c43e6be4a55f8f1c288233fce46a7ebe7718ccc5adb80bf8d56b7ef85f5fc32239c7bfccda006f9f2382e0cc2e2a77e4 languageName: node linkType: hard @@ -11807,29 +11369,6 @@ __metadata: languageName: node linkType: hard -"unist-util-generated@npm:^2.0.0": - version: 2.0.1 - resolution: "unist-util-generated@npm:2.0.1" - checksum: 6221ad0571dcc9c8964d6b054f39ef6571ed59cc0ce3e88ae97ea1c70afe76b46412a5ffaa91f96814644ac8477e23fb1b477d71f8d70e625728c5258f5c0d99 - languageName: node - linkType: hard - -"unist-util-is@npm:^4.0.0": - version: 4.1.0 - resolution: "unist-util-is@npm:4.1.0" - checksum: 726484cd2adc9be75a939aeedd48720f88294899c2e4a3143da413ae593f2b28037570730d5cf5fd910ff41f3bc1501e3d636b6814c478d71126581ef695f7ea - languageName: node - linkType: hard - -"unist-util-is@npm:^5.0.0": - version: 5.2.1 - resolution: "unist-util-is@npm:5.2.1" - dependencies: - "@types/unist": ^2.0.0 - checksum: ae76fdc3d35352cd92f1bedc3a0d407c3b9c42599a52ab9141fe89bdd786b51f0ec5a2ab68b93fb532e239457cae62f7e39eaa80229e1cb94875da2eafcbe5c4 - languageName: node - linkType: hard - "unist-util-is@npm:^6.0.0": version: 6.0.0 resolution: "unist-util-is@npm:6.0.0" @@ -11839,40 +11378,31 @@ __metadata: languageName: node linkType: hard -"unist-util-position-from-estree@npm:^1.0.0, unist-util-position-from-estree@npm:^1.1.0": - version: 1.1.2 - resolution: "unist-util-position-from-estree@npm:1.1.2" +"unist-util-position-from-estree@npm:^2.0.0": + version: 2.0.0 + resolution: "unist-util-position-from-estree@npm:2.0.0" dependencies: - "@types/unist": ^2.0.0 - checksum: e3f4060e2a9e894c6ed63489c5a7cb58ff282e5dae9497cbc2073033ca74d6e412af4d4d342c97aea08d997c908b8bce2fe43a2062aafc2bb3f266533016588b + "@types/unist": ^3.0.0 + checksum: d3b3048a5727c2367f64ef6dcc5b20c4717215ef8b1372ff9a7c426297c5d1e5776409938acd01531213e2cd2543218d16e73f9f862f318e9496e2c73bb18354 languageName: node linkType: hard -"unist-util-position@npm:^4.0.0": - version: 4.0.4 - resolution: "unist-util-position@npm:4.0.4" +"unist-util-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-position@npm:5.0.0" dependencies: - "@types/unist": ^2.0.0 - checksum: e7487b6cec9365299695e3379ded270a1717074fa11fd2407c9b934fb08db6fe1d9077ddeaf877ecf1813665f8ccded5171693d3d9a7a01a125ec5cdd5e88691 + "@types/unist": ^3.0.0 + checksum: f89b27989b19f07878de9579cd8db2aa0194c8360db69e2c99bd2124a480d79c08f04b73a64daf01a8fb3af7cba65ff4b45a0b978ca243226084ad5f5d441dde languageName: node linkType: hard -"unist-util-remove-position@npm:^4.0.0": - version: 4.0.2 - resolution: "unist-util-remove-position@npm:4.0.2" +"unist-util-remove-position@npm:^5.0.0": + version: 5.0.0 + resolution: "unist-util-remove-position@npm:5.0.0" dependencies: - "@types/unist": ^2.0.0 - unist-util-visit: ^4.0.0 - checksum: 989831da913d09a82a99ed9b47b78471b6409bde95942cde47e09da54b7736516f17e3c7e026af468684c1efcec5fb52df363381b2f9dc7fd96ce791c5a2fa4a - languageName: node - linkType: hard - -"unist-util-stringify-position@npm:^3.0.0": - version: 3.0.3 - resolution: "unist-util-stringify-position@npm:3.0.3" - dependencies: - "@types/unist": ^2.0.0 - checksum: dbd66c15183607ca942a2b1b7a9f6a5996f91c0d30cf8966fb88955a02349d9eefd3974e9010ee67e71175d784c5a9fea915b0aa0b0df99dcb921b95c4c9e124 + "@types/unist": ^3.0.0 + unist-util-visit: ^5.0.0 + checksum: 8aabdb9d0e3e744141bc123d8f87b90835d521209ad3c6c4619d403b324537152f0b8f20dda839b40c3aa0abfbf1828b3635a7a8bb159c3ed469e743023510ee languageName: node linkType: hard @@ -11885,26 +11415,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit-parents@npm:^3.0.0": - version: 3.1.1 - resolution: "unist-util-visit-parents@npm:3.1.1" - dependencies: - "@types/unist": ^2.0.0 - unist-util-is: ^4.0.0 - checksum: 1170e397dff88fab01e76d5154981666eb0291019d2462cff7a2961a3e76d3533b42eaa16b5b7e2d41ad42a5ea7d112301458283d255993e660511387bf67bc3 - languageName: node - linkType: hard - -"unist-util-visit-parents@npm:^5.0.0, unist-util-visit-parents@npm:^5.1.1, unist-util-visit-parents@npm:^5.1.3": - version: 5.1.3 - resolution: "unist-util-visit-parents@npm:5.1.3" - dependencies: - "@types/unist": ^2.0.0 - unist-util-is: ^5.0.0 - checksum: 8ecada5978994f846b64658cf13b4092cd78dea39e1ba2f5090a5de842ba4852712c02351a8ae95250c64f864635e7b02aedf3b4a093552bb30cf1bd160efbaa - languageName: node - linkType: hard - "unist-util-visit-parents@npm:^6.0.0": version: 6.0.1 resolution: "unist-util-visit-parents@npm:6.0.1" @@ -11915,28 +11425,6 @@ __metadata: languageName: node linkType: hard -"unist-util-visit@npm:^2.0.3": - version: 2.0.3 - resolution: "unist-util-visit@npm:2.0.3" - dependencies: - "@types/unist": ^2.0.0 - unist-util-is: ^4.0.0 - unist-util-visit-parents: ^3.0.0 - checksum: 1fe19d500e212128f96d8c3cfa3312846e586b797748a1fd195fe6479f06bc90a6f6904deb08eefc00dd58e83a1c8a32fb8677252d2273ad7a5e624525b69b8f - languageName: node - linkType: hard - -"unist-util-visit@npm:^4.0.0": - version: 4.1.2 - resolution: "unist-util-visit@npm:4.1.2" - dependencies: - "@types/unist": ^2.0.0 - unist-util-is: ^5.0.0 - unist-util-visit-parents: ^5.1.1 - checksum: 95a34e3f7b5b2d4b68fd722b6229972099eb97b6df18913eda44a5c11df8b1e27efe7206dd7b88c4ed244a48c474a5b2e2629ab79558ff9eb936840295549cee - languageName: node - linkType: hard - "unist-util-visit@npm:^5.0.0": version: 5.0.0 resolution: "unist-util-visit@npm:5.0.0" @@ -12024,41 +11512,6 @@ __metadata: languageName: node linkType: hard -"use-composed-ref@npm:^1.3.0": - version: 1.3.0 - resolution: "use-composed-ref@npm:1.3.0" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - checksum: f771cbadfdc91e03b7ab9eb32d0fc0cc647755711801bf507e891ad38c4bbc5f02b2509acadf9c965ec9c5f2f642fd33bdfdfb17b0873c4ad0a9b1f5e5e724bf - languageName: node - linkType: hard - -"use-isomorphic-layout-effect@npm:^1.1.1": - version: 1.1.2 - resolution: "use-isomorphic-layout-effect@npm:1.1.2" - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: a6532f7fc9ae222c3725ff0308aaf1f1ddbd3c00d685ef9eee6714fd0684de5cb9741b432fbf51e61a784e2955424864f7ea9f99734a02f237b17ad3e18ea5cb - languageName: node - linkType: hard - -"use-latest@npm:^1.2.1": - version: 1.2.1 - resolution: "use-latest@npm:1.2.1" - dependencies: - use-isomorphic-layout-effect: ^1.1.1 - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: ed3f2ddddf6f21825e2ede4c2e0f0db8dcce5129802b69d1f0575fc1b42380436e8c76a6cd885d4e9aa8e292e60fb8b959c955f33c6a9123b83814a1a1875367 - languageName: node - linkType: hard - "util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" @@ -12096,20 +11549,6 @@ __metadata: languageName: node linkType: hard -"uvu@npm:^0.5.0": - version: 0.5.6 - resolution: "uvu@npm:0.5.6" - dependencies: - dequal: ^2.0.0 - diff: ^5.0.0 - kleur: ^4.0.3 - sade: ^1.7.3 - bin: - uvu: bin.js - checksum: 09460a37975627de9fcad396e5078fb844d01aaf64a6399ebfcfd9e55f1c2037539b47611e8631f89be07656962af0cf48c334993db82b9ae9c3d25ce3862168 - languageName: node - linkType: hard - "value-equal@npm:^1.0.1": version: 1.0.1 resolution: "value-equal@npm:1.0.1" @@ -12124,23 +11563,13 @@ __metadata: languageName: node linkType: hard -"vfile-location@npm:^4.0.0": - version: 4.1.0 - resolution: "vfile-location@npm:4.1.0" +"vfile-location@npm:^5.0.0": + version: 5.0.2 + resolution: "vfile-location@npm:5.0.2" dependencies: - "@types/unist": ^2.0.0 - vfile: ^5.0.0 - checksum: c894e8e5224170d1f85288f4a1d1ebcee0780823ea2b49d881648ab360ebf01b37ecb09b1c4439a75f9a51f31a9f9742cd045e987763e367c352a1ef7c50d446 - languageName: node - linkType: hard - -"vfile-message@npm:^3.0.0": - version: 3.1.4 - resolution: "vfile-message@npm:3.1.4" - dependencies: - "@types/unist": ^2.0.0 - unist-util-stringify-position: ^3.0.0 - checksum: d0ee7da1973ad76513c274e7912adbed4d08d180eaa34e6bd40bc82459f4b7bc50fcaff41556135e3339995575eac5f6f709aba9332b80f775618ea4880a1367 + "@types/unist": ^3.0.0 + vfile: ^6.0.0 + checksum: b61c048cedad3555b4f007f390412c6503f58a6a130b58badf4ee340c87e0d7421e9c86bbc1494c57dedfccadb60f5176cc60ba3098209d99fb3a3d8804e4c38 languageName: node linkType: hard @@ -12154,19 +11583,7 @@ __metadata: languageName: node linkType: hard -"vfile@npm:^5.0.0, vfile@npm:^5.3.7": - version: 5.3.7 - resolution: "vfile@npm:5.3.7" - dependencies: - "@types/unist": ^2.0.0 - is-buffer: ^2.0.0 - unist-util-stringify-position: ^3.0.0 - vfile-message: ^3.0.0 - checksum: 642cce703afc186dbe7cabf698dc954c70146e853491086f5da39e1ce850676fc96b169fcf7898aa3ff245e9313aeec40da93acd1e1fcc0c146dc4f6308b4ef9 - languageName: node - linkType: hard - -"vfile@npm:^6.0.0": +"vfile@npm:^6.0.0, vfile@npm:^6.0.1": version: 6.0.1 resolution: "vfile@npm:6.0.1" dependencies: @@ -12177,21 +11594,6 @@ __metadata: languageName: node linkType: hard -"wait-on@npm:^7.0.1": - version: 7.0.1 - resolution: "wait-on@npm:7.0.1" - dependencies: - axios: ^0.27.2 - joi: ^17.7.0 - lodash: ^4.17.21 - minimist: ^1.2.7 - rxjs: ^7.8.0 - bin: - wait-on: bin/wait-on - checksum: 1e8a17d8ee6436f71d3ab82781ce31267481fcd7bbccde49b0f8124871e6e40a1acac3401f04f775ba6203853a5813352fa131620fc139914351f3b2894d573f - languageName: node - linkType: hard - "watchpack@npm:^2.4.0": version: 2.4.0 resolution: "watchpack@npm:2.4.0" @@ -12218,13 +11620,6 @@ __metadata: languageName: node linkType: hard -"webidl-conversions@npm:^3.0.0": - version: 3.0.1 - resolution: "webidl-conversions@npm:3.0.1" - checksum: c92a0a6ab95314bde9c32e1d0a6dfac83b578f8fa5f21e675bc2706ed6981bc26b7eb7e6a1fab158e5ce4adf9caa4a0aee49a52505d4d13c7be545f15021b17c - languageName: node - linkType: hard - "webpack-bundle-analyzer@npm:^4.9.0": version: 4.9.1 resolution: "webpack-bundle-analyzer@npm:4.9.1" @@ -12400,16 +11795,6 @@ __metadata: languageName: node linkType: hard -"whatwg-url@npm:^5.0.0": - version: 5.0.0 - resolution: "whatwg-url@npm:5.0.0" - dependencies: - tr46: ~0.0.3 - webidl-conversions: ^3.0.0 - checksum: b8daed4ad3356cc4899048a15b2c143a9aed0dfae1f611ebd55073310c7b910f522ad75d727346ad64203d7e6c79ef25eafd465f4d12775ca44b90fa82ed9e2c - languageName: node - linkType: hard - "which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" From 6ce5e30a23e7f527768c89d9613656026d14edf8 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 19 Jan 2024 18:08:39 -0500 Subject: [PATCH 124/152] sort labeler config hopefully alphabetical order makes future changes more straightforward Signed-off-by: Jamie Klassen --- .github/labeler.yml | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index add66d7b28..7da0b72572 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -2,34 +2,34 @@ area:catalog: - plugins/catalog/**/* - plugins/catalog-*/**/* - packages/catalog-*/**/* -area:scaffolder: - - plugins/scaffolder/**/* - - plugins/scaffolder-*/**/* -search: - - plugins/search/**/* - - plugins/search-*/**/* - - packages/search-*/**/* -homepage: - - plugins/home/**/* area:discoverability: # search + home - plugins/search/**/* - plugins/search-*/**/* - packages/search-*/**/* - plugins/home/**/* +area:permission: + - plugins/permission-*/**/* +area:scaffolder: + - plugins/scaffolder/**/* + - plugins/scaffolder-*/**/* area:techdocs: - plugins/techdocs/**/* - plugins/techdocs-*/**/* - packages/techdocs-*/**/* -documentation: - - docs/**/* -microsite: - - microsite/**/* -storybook: - - storybook/**/* auth: - plugins/auth-backend/**/* - packages/core-app-api/src/apis/implementations/auth/**/* - packages/core-app-api/src/lib/Auth*/**/* - packages/core-plugin-api/src/apis/definitions/auth.ts -area:permission: - - plugins/permission-*/**/* +documentation: + - docs/**/* +homepage: + - plugins/home/**/* +microsite: + - microsite/**/* +search: + - plugins/search/**/* + - plugins/search-*/**/* + - packages/search-*/**/* +storybook: + - storybook/**/* From 1691d8d8f83bdb88369b6529addf9302b14061f6 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 19 Jan 2024 18:10:53 -0500 Subject: [PATCH 125/152] label kubernetes-related PRs Signed-off-by: Jamie Klassen --- .github/labeler.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/labeler.yml b/.github/labeler.yml index 7da0b72572..3e0eac8ad7 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -7,6 +7,9 @@ area:discoverability: # search + home - plugins/search-*/**/* - packages/search-*/**/* - plugins/home/**/* +area:kubernetes: + - plugins/kubernetes/**/* + - plugins/kubernetes-*/**/* area:permission: - plugins/permission-*/**/* area:scaffolder: From 10e80ade7ca42b9dd9a05ae35ea8cfdc8f1cc7d1 Mon Sep 17 00:00:00 2001 From: Jamie Klassen Date: Fri, 19 Jan 2024 18:12:38 -0500 Subject: [PATCH 126/152] capture new auth packages with labeler This includes stuff like auth-node and the various auth provider modules. Signed-off-by: Jamie Klassen --- .github/labeler.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index 3e0eac8ad7..5066ebaf05 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -20,7 +20,7 @@ area:techdocs: - plugins/techdocs-*/**/* - packages/techdocs-*/**/* auth: - - plugins/auth-backend/**/* + - plugins/auth-*/**/* - packages/core-app-api/src/apis/implementations/auth/**/* - packages/core-app-api/src/lib/Auth*/**/* - packages/core-plugin-api/src/apis/definitions/auth.ts From c7259dc12dc8743570c62b55cfbfdc6c485f20ea Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Jan 2024 10:25:48 +0100 Subject: [PATCH 127/152] changesets: added changeset for cli backend module template update Signed-off-by: Patrik Oldsberg --- .changeset/two-coats-smile.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/two-coats-smile.md diff --git a/.changeset/two-coats-smile.md b/.changeset/two-coats-smile.md new file mode 100644 index 0000000000..65abb7a1eb --- /dev/null +++ b/.changeset/two-coats-smile.md @@ -0,0 +1,5 @@ +--- +'@backstage/cli': patch +--- + +Updated the backend module template to make the module instance the package default export. From 86346c2c11dbe15fd2be2bd3f6e924b13239e622 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 20 Jan 2024 11:16:25 +0100 Subject: [PATCH 128/152] simplify route bindings in config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/silent-hotels-knock.md | 25 +++++++++++++++++++ .../frontend-system/architecture/07-routes.md | 4 +-- packages/app-next/app-config.yaml | 6 ++--- .../src/routing/collectRouteIds.test.ts | 10 +++----- .../src/routing/collectRouteIds.ts | 4 +-- 5 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 .changeset/silent-hotels-knock.md diff --git a/.changeset/silent-hotels-knock.md b/.changeset/silent-hotels-knock.md new file mode 100644 index 0000000000..7ff5d978d2 --- /dev/null +++ b/.changeset/silent-hotels-knock.md @@ -0,0 +1,25 @@ +--- +'@backstage/frontend-app-api': minor +--- + +**BREAKING**: The `app.routes.bindings` app-config mapping has been simplified. You now only need to specify the plugin ID and route ID on both sides of the mapping. + +Old form: + +```yaml +app: + routes: + bindings: + plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot + plugin.catalog.externalRoutes.createComponent: plugin.catalog-import.routes.importPage +``` + +New form: + +```yaml +app: + routes: + bindings: + catalog.viewTechDoc: techdocs.docRoot + catalog.createComponent: catalog-import.importPage +``` diff --git a/docs/frontend-system/architecture/07-routes.md b/docs/frontend-system/architecture/07-routes.md index 8351a864ba..11586573be 100644 --- a/docs/frontend-system/architecture/07-routes.md +++ b/docs/frontend-system/architecture/07-routes.md @@ -241,10 +241,10 @@ app: bindings: # point to the Scaffolder create component page when the Catalog create component ref is used # highlight-next-line - plugin.catalog.externalRoutes.createComponent: plugin.scaffolder.routes.index + catalog.createComponent: scaffolder.index # point to the Catalog details page when the Scaffolder component details ref is used # highlight-next-line - plugin.scaffolder.externalRoutes.componentDetails: plugin.catalog.routes.details + scaffolder.componentDetails: catalog.details ``` We also have the ability to express this in code as an option to `createApp`, but you of course only need to use one of these two methods: diff --git a/packages/app-next/app-config.yaml b/packages/app-next/app-config.yaml index 1b77205d5e..9cc3507fa4 100644 --- a/packages/app-next/app-config.yaml +++ b/packages/app-next/app-config.yaml @@ -3,9 +3,9 @@ app: packages: 'all' # ✨ routes: bindings: - plugin.pages.externalRoutes.pageX: plugin.pages.routes.pageX - plugin.catalog.externalRoutes.viewTechDoc: plugin.techdocs.routes.docRoot - plugin.catalog.externalRoutes.createComponent: plugin.catalog-import.routes.importPage + pages.pageX: pages.pageX + catalog.viewTechDoc: techdocs.docRoot + catalog.createComponent: catalog-import.importPage extensions: # - apis.plugin.graphiql.browse.gitlab: true diff --git a/packages/frontend-app-api/src/routing/collectRouteIds.test.ts b/packages/frontend-app-api/src/routing/collectRouteIds.test.ts index 95a5afd8cd..6b9fdecf70 100644 --- a/packages/frontend-app-api/src/routing/collectRouteIds.test.ts +++ b/packages/frontend-app-api/src/routing/collectRouteIds.test.ts @@ -37,15 +37,13 @@ describe('collectRouteIds', () => { createPlugin({ id: 'test', routes: { ref }, externalRoutes: { extRef } }), ]); expect(Object.fromEntries(collected.routes)).toEqual({ - 'plugin.test.routes.ref': ref, + 'test.ref': ref, }); expect(Object.fromEntries(collected.externalRoutes)).toEqual({ - 'plugin.test.externalRoutes.extRef': extRef, + 'test.extRef': extRef, }); - expect(String(ref)).toBe('RouteRef{plugin.test.routes.ref}'); - expect(String(extRef)).toBe( - 'ExternalRouteRef{plugin.test.externalRoutes.extRef}', - ); + expect(String(ref)).toBe('RouteRef{test.ref}'); + expect(String(extRef)).toBe('ExternalRouteRef{test.extRef}'); }); }); diff --git a/packages/frontend-app-api/src/routing/collectRouteIds.ts b/packages/frontend-app-api/src/routing/collectRouteIds.ts index 2fbb8b6791..d45871ae89 100644 --- a/packages/frontend-app-api/src/routing/collectRouteIds.ts +++ b/packages/frontend-app-api/src/routing/collectRouteIds.ts @@ -47,7 +47,7 @@ export function collectRouteIds(features: FrontendFeature[]): RouteRefsById { } for (const [name, ref] of Object.entries(feature.routes)) { - const refId = `plugin.${feature.id}.routes.${name}`; + const refId = `${feature.id}.${name}`; if (routesById.has(refId)) { throw new Error(`Unexpected duplicate route '${refId}'`); } @@ -62,7 +62,7 @@ export function collectRouteIds(features: FrontendFeature[]): RouteRefsById { } } for (const [name, ref] of Object.entries(feature.externalRoutes)) { - const refId = `plugin.${feature.id}.externalRoutes.${name}`; + const refId = `${feature.id}.${name}`; if (externalRoutesById.has(refId)) { throw new Error(`Unexpected duplicate external route '${refId}'`); } From e0a4dd1a2ffcc90a41a0a1e76d983d7396bc2954 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 20 Jan 2024 10:58:33 +0100 Subject: [PATCH 129/152] improve resolveInputData error messaging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/silent-poets-grab.md | 5 +++++ .../src/tree/instantiateAppNodeTree.test.ts | 4 ++-- .../src/tree/instantiateAppNodeTree.ts | 11 ++++++++++- .../src/app/createExtensionTester.test.tsx | 4 ++-- 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 .changeset/silent-poets-grab.md diff --git a/.changeset/silent-poets-grab.md b/.changeset/silent-poets-grab.md new file mode 100644 index 0000000000..0d76e263d2 --- /dev/null +++ b/.changeset/silent-poets-grab.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-app-api': patch +--- + +Improved the error message when data input/output shapes do not match diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts index 766cb4804e..3330b9add8 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.test.ts @@ -615,8 +615,8 @@ describe('createAppNodeInstance', () => { ), ), }), - ).toThrow( - "Failed to instantiate extension 'app/test', input 'singleton' did not receive required extension data 'other' from extension 'app/test'", + ).toThrowErrorMatchingInlineSnapshot( + `"Failed to instantiate extension 'app/test', extension 'app/test' could not be attached because its output data ('test', 'other') does not match what the input 'singleton' requires ('other')"`, ); }); }); diff --git a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts index 90a256fea6..63eca749b7 100644 --- a/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts +++ b/packages/frontend-app-api/src/tree/instantiateAppNodeTree.ts @@ -37,8 +37,17 @@ function resolveInputData( return mapValues(dataMap, ref => { const value = attachment.instance?.getData(ref); if (value === undefined && !ref.config.optional) { + const expected = Object.values(dataMap) + .filter(r => !r.config.optional) + .map(r => `'${r.id}'`) + .join(', '); + + const provided = [...(attachment.instance?.getDataRefs() ?? [])] + .map(r => `'${r.id}'`) + .join(', '); + throw new Error( - `input '${inputName}' did not receive required extension data '${ref.id}' from extension '${attachment.spec.id}'`, + `extension '${attachment.spec.id}' could not be attached because its output data (${provided}) does not match what the input '${inputName}' requires (${expected})`, ); } return value; diff --git a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx index d53344f7fd..f59d44f6d7 100644 --- a/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx +++ b/packages/frontend-test-utils/src/app/createExtensionTester.test.tsx @@ -63,8 +63,8 @@ describe('createExtensionTester', () => { factory: () => ({ path: '/foo' }), }); const tester = createExtensionTester(extension); - expect(() => tester.render()).toThrow( - "Failed to instantiate extension 'app/routes', input 'routes' did not receive required extension data 'core.reactElement' from extension 'test'", + expect(() => tester.render()).toThrowErrorMatchingInlineSnapshot( + `"Failed to instantiate extension 'app/routes', extension 'test' could not be attached because its output data ('core.routing.path') does not match what the input 'routes' requires ('core.routing.path', 'core.reactElement')"`, ); }); From e34401c3c69c24257582fbfe46a3c83dc22f5abb Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sat, 20 Jan 2024 16:23:58 +0100 Subject: [PATCH 130/152] Update microsite/data/plugins/github-codespaces.yaml Signed-off-by: Patrik Oldsberg --- microsite/data/plugins/github-codespaces.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/github-codespaces.yaml b/microsite/data/plugins/github-codespaces.yaml index e4480cf4e2..de2659c1e1 100644 --- a/microsite/data/plugins/github-codespaces.yaml +++ b/microsite/data/plugins/github-codespaces.yaml @@ -12,4 +12,4 @@ tags: - codespaces - development - devcontainers -addedDate: '2023-12-30' \ No newline at end of file +addedDate: '2023-12-30' From da059d74886da474130b9dc9310356a5c0a74d03 Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 20 Jan 2024 10:29:55 -0600 Subject: [PATCH 131/152] Removed alpha symbol from Task List header Signed-off-by: Andre Wanlin --- .changeset/lovely-starfishes-remain.md | 5 +++++ .../src/components/ListTasksPage/ListTasksPage.tsx | 7 +------ 2 files changed, 6 insertions(+), 6 deletions(-) create mode 100644 .changeset/lovely-starfishes-remain.md diff --git a/.changeset/lovely-starfishes-remain.md b/.changeset/lovely-starfishes-remain.md new file mode 100644 index 0000000000..c57bfa0aaa --- /dev/null +++ b/.changeset/lovely-starfishes-remain.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-scaffolder': patch +--- + +Removed alpha symbol from Task List header diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index 1edac508d8..8f8c4e7644 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -18,7 +18,6 @@ import { EmptyState, ErrorPanel, Header, - Lifecycle, Link, Page, Progress, @@ -139,11 +138,7 @@ export const ListTasksPage = (props: MyTaskPageProps) => {
- List template tasks - - } + title={<>List template tasks} subtitle="All tasks that have been started" /> From 80107196317f1d4b0e07a117e8b2ad5248d4f9cb Mon Sep 17 00:00:00 2001 From: Andre Wanlin Date: Sat, 20 Jan 2024 10:32:14 -0600 Subject: [PATCH 132/152] Fixed formatting Signed-off-by: Andre Wanlin --- .../scaffolder/src/components/ListTasksPage/ListTasksPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx index 8f8c4e7644..aac0ed5c79 100644 --- a/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx +++ b/plugins/scaffolder/src/components/ListTasksPage/ListTasksPage.tsx @@ -138,7 +138,7 @@ export const ListTasksPage = (props: MyTaskPageProps) => {
List template tasks} + title="List template tasks" subtitle="All tasks that have been started" /> From 103143d3df95671f243231e50cd6492a8641cab5 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Sat, 20 Jan 2024 22:13:56 +0530 Subject: [PATCH 133/152] added suggested changes Signed-off-by: npiyush97 --- microsite/sidebars.json | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 81bd0daec0..68628f1490 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -473,13 +473,8 @@ "architecture-decisions/adrs-adr013" ], "FAQ": [ - "faq/FAQ", "faq/Product FAQs", - "faq/Technical FAQs", - "faq/Adoption FAQs", - "faq/DEPLOYMENT FAQs", - "faq/FRAMEWORK FAQs", - "faq/CORE FEATURES FAQs" + "faq/Technical FAQs" ], "Accessibility": ["accessibility/index"] } From d004e65f54dc6f9c08c361088f1994f070d9a844 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Sat, 20 Jan 2024 22:16:56 +0530 Subject: [PATCH 134/152] added suggested changes Signed-off-by: npiyush97 --- microsite/sidebars.json | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 68628f1490..8272f5edfb 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -472,10 +472,7 @@ "architecture-decisions/adrs-adr012", "architecture-decisions/adrs-adr013" ], - "FAQ": [ - "faq/Product FAQs", - "faq/Technical FAQs" - ], + "FAQ": ["faq/Product FAQs", "faq/Technical FAQs"], "Accessibility": ["accessibility/index"] } } From 5a8b6ccda3d14ed8a08e9ef3595fb4fcacd0a6e2 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Sat, 20 Jan 2024 22:24:29 +0530 Subject: [PATCH 135/152] removed files Signed-off-by: npiyush97 --- docs/faq/FAQ.md | 19 ------------------- docs/faq/adoption.md | 7 ------- docs/faq/corefeatures.md | 7 ------- docs/faq/framework.md | 7 ------- 4 files changed, 40 deletions(-) delete mode 100644 docs/faq/FAQ.md delete mode 100644 docs/faq/adoption.md delete mode 100644 docs/faq/corefeatures.md delete mode 100644 docs/faq/framework.md diff --git a/docs/faq/FAQ.md b/docs/faq/FAQ.md deleted file mode 100644 index 4b50a38818..0000000000 --- a/docs/faq/FAQ.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -id: FAQ -title: FAQ -description: All FAQ related to Questions ---- - -## FAQS - -[Product FAQS](../faq/product-faq.md) - -[Technical FAQS](../faq/technical.md) - -[Adoption FAQS](../faq/adoption.md) - -[Deployment FAQS](../faq/deployment.md) - -[Framework FAQS](../faq/framework.md) - -[Core features FAQS](../faq/corefeatures.md) diff --git a/docs/faq/adoption.md b/docs/faq/adoption.md deleted file mode 100644 index 0939fef069..0000000000 --- a/docs/faq/adoption.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -id: Adoption FAQs -title: Adoption FAQs -description: All FAQs related to Adoption ---- - -## ADOPTION FAQ diff --git a/docs/faq/corefeatures.md b/docs/faq/corefeatures.md deleted file mode 100644 index 8a46929797..0000000000 --- a/docs/faq/corefeatures.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -id: CORE FEATURES FAQs -title: Core Features FAQs -description: All FAQs related to Core Features ---- - -## Core Features FAQs diff --git a/docs/faq/framework.md b/docs/faq/framework.md deleted file mode 100644 index 409a273bfb..0000000000 --- a/docs/faq/framework.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -id: FRAMEWORK FAQs -title: Framework FAQs -description: All FAQ related to FRAMEWORK ---- - -## FRAMEWORK FAQs From 58a83daffcc07b4e6085dd2643f21e963354d570 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Sat, 20 Jan 2024 22:26:46 +0530 Subject: [PATCH 136/152] fix Signed-off-by: npiyush97 --- docs/faq/deployment.md | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 docs/faq/deployment.md diff --git a/docs/faq/deployment.md b/docs/faq/deployment.md deleted file mode 100644 index a67586bc2a..0000000000 --- a/docs/faq/deployment.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -id: DEPLOYMENT FAQs -title: Deployment FAQs -description: All FAQs related to DEPLOYMENT ---- - -## Deployment FAQs From 7ad5b8146e7d8b06ec4c3be1d383f2b425152c24 Mon Sep 17 00:00:00 2001 From: npiyush97 Date: Sat, 20 Jan 2024 22:35:50 +0530 Subject: [PATCH 137/152] added suggested fix Signed-off-by: npiyush97 --- docs/faq/product-faq.md | 2 -- docs/faq/technical.md | 2 -- 2 files changed, 4 deletions(-) diff --git a/docs/faq/product-faq.md b/docs/faq/product-faq.md index 68d8918ad0..29cd22992b 100644 --- a/docs/faq/product-faq.md +++ b/docs/faq/product-faq.md @@ -4,8 +4,6 @@ title: Product FAQs description: All FAQs related to Product --- -## Product FAQs - ### Can we call Backstage something different? So that it fits our company better? Yes, Backstage is just a platform for building your own developer portal. We diff --git a/docs/faq/technical.md b/docs/faq/technical.md index a79c673799..1573c14c59 100644 --- a/docs/faq/technical.md +++ b/docs/faq/technical.md @@ -4,8 +4,6 @@ title: Technical FAQs description: All FAQs related to Technical --- -## Technical FAQs - ### What technology does Backstage use? Backstage is a large scale [TypeScript](https://www.typescriptlang.org/) From f8a20c560cb1acaa03c4d38f1d40b3c396d5ab0e Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 13:53:52 +0100 Subject: [PATCH 138/152] Update package.json Signed-off-by: Patrik Oldsberg --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 4edc266ba1..483e02fdb5 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "dependencies": { "@backstage/errors": "workspace:^", "@manypkg/get-packages": "^1.1.3", - "@useoptic/optic": "^0.50.10", - "lodash": "^4.17.21" + "@useoptic/optic": "^0.50.10" }, "devDependencies": { "@backstage/cli": "workspace:*", From 87773788c8b2c8408ff8d140d94811a1d5293fd0 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:03:21 +0100 Subject: [PATCH 139/152] yarn.lock: update Signed-off-by: Patrik Oldsberg --- yarn.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/yarn.lock b/yarn.lock index 3ba535682c..6182ed543f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -39723,7 +39723,6 @@ __metadata: fs-extra: 10.1.0 husky: ^8.0.0 lint-staged: ^15.0.0 - lodash: ^4.17.21 minimist: ^1.2.5 node-gyp: ^10.0.0 prettier: ^2.2.1 From aede0b73b00696a88a7d726d54741b7c46cf24a1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:38:02 +0100 Subject: [PATCH 140/152] docs/faq: add back index + frontmatter tweaks Signed-off-by: Patrik Oldsberg --- docs/faq/index.md | 15 +++++++++++++++ docs/faq/{product-faq.md => product.md} | 6 +++--- docs/faq/technical.md | 6 +++--- microsite/sidebars.json | 2 +- 4 files changed, 22 insertions(+), 7 deletions(-) create mode 100644 docs/faq/index.md rename docs/faq/{product-faq.md => product.md} (97%) diff --git a/docs/faq/index.md b/docs/faq/index.md new file mode 100644 index 0000000000..93e3e90fbe --- /dev/null +++ b/docs/faq/index.md @@ -0,0 +1,15 @@ +--- +id: index +title: Overview +description: FAQ Overview +--- + +This section contains answers to frequently asked questions about Backstage. + +### [Product FAQ](../faq/product.md) + +Questions related to product and design. + +### [Technical FAQ](../faq/technical.md) + +General technical questions about Backstage. diff --git a/docs/faq/product-faq.md b/docs/faq/product.md similarity index 97% rename from docs/faq/product-faq.md rename to docs/faq/product.md index 29cd22992b..6d97206907 100644 --- a/docs/faq/product-faq.md +++ b/docs/faq/product.md @@ -1,7 +1,7 @@ --- -id: Product FAQs -title: Product FAQs -description: All FAQs related to Product +id: product +title: Product FAQ +description: Product FAQ --- ### Can we call Backstage something different? So that it fits our company better? diff --git a/docs/faq/technical.md b/docs/faq/technical.md index 1573c14c59..140b4c0893 100644 --- a/docs/faq/technical.md +++ b/docs/faq/technical.md @@ -1,7 +1,7 @@ --- -id: Technical FAQs -title: Technical FAQs -description: All FAQs related to Technical +id: technical +title: Technical FAQ +description: Technical FAQ --- ### What technology does Backstage use? diff --git a/microsite/sidebars.json b/microsite/sidebars.json index 8272f5edfb..f0f924411b 100644 --- a/microsite/sidebars.json +++ b/microsite/sidebars.json @@ -472,7 +472,7 @@ "architecture-decisions/adrs-adr012", "architecture-decisions/adrs-adr013" ], - "FAQ": ["faq/Product FAQs", "faq/Technical FAQs"], + "FAQ": ["faq/index", "faq/product", "faq/technical"], "Accessibility": ["accessibility/index"] } } From f2c5d430d4bc26bb087353931fb2c85e5e76efba Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:39:51 +0100 Subject: [PATCH 141/152] docs/faq: update descriptions Signed-off-by: Patrik Oldsberg --- docs/faq/product.md | 2 +- docs/faq/technical.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/faq/product.md b/docs/faq/product.md index 6d97206907..30793ba045 100644 --- a/docs/faq/product.md +++ b/docs/faq/product.md @@ -1,7 +1,7 @@ --- id: product title: Product FAQ -description: Product FAQ +description: Questions related to product and design. --- ### Can we call Backstage something different? So that it fits our company better? diff --git a/docs/faq/technical.md b/docs/faq/technical.md index 140b4c0893..040317b998 100644 --- a/docs/faq/technical.md +++ b/docs/faq/technical.md @@ -1,7 +1,7 @@ --- id: technical title: Technical FAQ -description: Technical FAQ +description: General technical questions about Backstage. --- ### What technology does Backstage use? From d84a93b4a34c9ebd42b6acee08d1522535e33d94 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:40:57 +0100 Subject: [PATCH 142/152] docs/overview: update link to FAQ Signed-off-by: Patrik Oldsberg --- docs/overview/support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/support.md b/docs/overview/support.md index c968405a99..38bb60784f 100644 --- a/docs/overview/support.md +++ b/docs/overview/support.md @@ -11,7 +11,7 @@ description: Support and Community Details and Links here if you want to contribute. - [RFCs](https://github.com/backstage/backstage/labels/rfc) - Help shape the technical direction by reviewing _Request for Comments_ issues. -- [FAQ](../FAQ.md) - Frequently Asked Questions. +- [FAQ](../faq/index.md) - Frequently Asked Questions. - [Code of Conduct](https://github.com/backstage/backstage/blob/master/CODE_OF_CONDUCT.md) - This is how we roll. - [Blog](https://backstage.io/blog/) - Announcements and updates. From 01515668d2d957d17f64c382460416d301df22a4 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:50:35 +0100 Subject: [PATCH 143/152] yarn.lock: restore jose and openid-client versions Signed-off-by: Patrik Oldsberg --- yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index eb6ade926f..fcb1ece0b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -31269,10 +31269,10 @@ __metadata: languageName: node linkType: hard -"jose@npm:^4.14.6, jose@npm:^4.15.1, jose@npm:^4.6.0": - version: 4.15.3 - resolution: "jose@npm:4.15.3" - checksum: b76eeccc1d40d0eaf26dfaadc0f88fc15802c9105ab66a24ee223bd84369f7cb217f4a2cb852f5080ff6996170b3a73db2b2d26878b8905d99c36ca432628134 +"jose@npm:^4.14.6, jose@npm:^4.15.4, jose@npm:^4.6.0": + version: 4.15.4 + resolution: "jose@npm:4.15.4" + checksum: dccad91cb3357f36423774a0b89ad830dd84b31090de65cd139b85488439f16a00f8c59c0773825e8a1adb0dd9d13ad725ad66e6ea33880ecb3959bb99e1ea5b languageName: node linkType: hard @@ -35649,14 +35649,14 @@ __metadata: linkType: hard "openid-client@npm:^5.2.1, openid-client@npm:^5.3.0, openid-client@npm:^5.4.3, openid-client@npm:^5.5.0": - version: 5.6.1 - resolution: "openid-client@npm:5.6.1" + version: 5.6.4 + resolution: "openid-client@npm:5.6.4" dependencies: - jose: ^4.15.1 + jose: ^4.15.4 lru-cache: ^6.0.0 object-hash: ^2.2.0 oidc-token-hash: ^5.0.3 - checksum: 9d939cec57540e6dd3f67e9a248ec5ecec3b439b7ab5bd2e9fb4481bd03e8d030deedd87a447348194be7f3e93e84085841b0414033caf86479870f526cdbc2f + checksum: 69843f078dacbbc6bad6d65ca6689414ac73f095dfe2f8e606822e6cfc9d9cd7d0dfaf2649352eda604653806f0ea65326ad2d6266da897e4740ec93d26d21f6 languageName: node linkType: hard From b5d4c7b882271abb905c6564e5b7baddeb9c2c86 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 14:57:40 +0100 Subject: [PATCH 144/152] auth-backend: add back deprecated `OidcAuthResult` Signed-off-by: Patrik Oldsberg --- plugins/auth-backend/api-report.md | 9 ++++++--- plugins/auth-backend/src/providers/index.ts | 1 + plugins/auth-backend/src/providers/oidc/index.ts | 8 ++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/auth-backend/api-report.md b/plugins/auth-backend/api-report.md index 16f257d17d..9f68d8085c 100644 --- a/plugins/auth-backend/api-report.md +++ b/plugins/auth-backend/api-report.md @@ -25,7 +25,7 @@ import { LoggerService } from '@backstage/backend-plugin-api'; import { OAuth2ProxyResult as OAuth2ProxyResult_2 } from '@backstage/plugin-auth-backend-module-oauth2-proxy-provider'; import { OAuthEnvironmentHandler as OAuthEnvironmentHandler_2 } from '@backstage/plugin-auth-node'; import { OAuthState as OAuthState_2 } from '@backstage/plugin-auth-node'; -import { OidcAuthResult } from '@backstage/plugin-auth-backend-module-oidc-provider'; +import { OidcAuthResult as OidcAuthResult_2 } from '@backstage/plugin-auth-backend-module-oidc-provider'; import { PluginDatabaseManager } from '@backstage/backend-common'; import { PluginEndpointDiscovery } from '@backstage/backend-common'; import { prepareBackstageIdentityResponse as prepareBackstageIdentityResponse_2 } from '@backstage/plugin-auth-node'; @@ -339,6 +339,9 @@ export type OAuthStartResponse = { // @public @deprecated (undocumented) export type OAuthState = OAuthState_2; +// @public @deprecated (undocumented) +export type OidcAuthResult = OidcAuthResult_2; + // @public @deprecated (undocumented) export const postMessageResponse: ( res: express.Response, @@ -557,10 +560,10 @@ export const providers: Readonly<{ create: ( options?: | { - authHandler?: AuthHandler | undefined; + authHandler?: AuthHandler | undefined; signIn?: | { - resolver: SignInResolver; + resolver: SignInResolver; } | undefined; } diff --git a/plugins/auth-backend/src/providers/index.ts b/plugins/auth-backend/src/providers/index.ts index c8140f864e..8173a7fbc3 100644 --- a/plugins/auth-backend/src/providers/index.ts +++ b/plugins/auth-backend/src/providers/index.ts @@ -29,6 +29,7 @@ export type { } from './cloudflare-access'; export type { GithubOAuthResult } from './github'; export type { OAuth2ProxyResult } from './oauth2-proxy'; +export type { OidcAuthResult } from './oidc'; export type { SamlAuthResult } from './saml'; export type { GcpIapResult, GcpIapTokenInfo } from './gcp-iap'; diff --git a/plugins/auth-backend/src/providers/oidc/index.ts b/plugins/auth-backend/src/providers/oidc/index.ts index c4343b1547..501f223fb3 100644 --- a/plugins/auth-backend/src/providers/oidc/index.ts +++ b/plugins/auth-backend/src/providers/oidc/index.ts @@ -15,3 +15,11 @@ */ export { oidc } from './provider'; + +import { OidcAuthResult as OidcAuthResult_ } from '@backstage/plugin-auth-backend-module-oidc-provider'; + +/** + * @public + * @deprecated Use OidcAuthResult from `@backstage/plugin-auth-backend-module-oidc-provider` instead + */ +export type OidcAuthResult = OidcAuthResult_; From 636f43b76e17c955aaf55a0be30d0e8e25919ef1 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 15:01:35 +0100 Subject: [PATCH 145/152] docs/faq: update relative links Signed-off-by: Patrik Oldsberg --- docs/faq/product.md | 2 +- docs/faq/technical.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/faq/product.md b/docs/faq/product.md index 30793ba045..9d2a32f33e 100644 --- a/docs/faq/product.md +++ b/docs/faq/product.md @@ -46,7 +46,7 @@ source candidates. (And we'll probably end up writing some brand new ones, too.) ### What's the roadmap for Backstage? We envision three phases, which you can learn about in -[our project roadmap](overview/roadmap.md). Even though the open source version +[our project roadmap](../overview/roadmap.md). Even though the open source version of Backstage is relatively new compared to our internal version, we have already begun work on various aspects of all three phases. Looking at the [milestones for active issues](https://github.com/backstage/backstage/milestones) diff --git a/docs/faq/technical.md b/docs/faq/technical.md index 040317b998..55bb1af615 100644 --- a/docs/faq/technical.md +++ b/docs/faq/technical.md @@ -51,7 +51,7 @@ type of content. Plugins all use a common set of platform APIs and reusable UI components. Plugins can fetch data either from the backend or an API exposed through the proxy. -Learn more about [the different components](overview/what-is-backstage.md) that +Learn more about [the different components](../overview/what-is-backstage.md) that make up Backstage. ### Why can't I dynamically install plugins without modifications to the app? @@ -188,7 +188,7 @@ data is shared with. Yes. The core frontend framework could be used for building any large-scale web application where (1) multiple teams are building separate parts of the app, and (2) you want the overall experience to be consistent. That being said, in -[Phase 2](overview/roadmap.md) of the project we will add features that are +[Phase 2](../overview/roadmap.md) of the project we will add features that are needed for developer portals and systems for managing software ecosystems. Our ambition will be to keep Backstage modular. From 96a715636fea9a169d74e3ff73ab00813464625e Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 14:22:19 +0000 Subject: [PATCH 146/152] Update dependency @playwright/test to v1.41.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/yarn.lock b/yarn.lock index dc76ef0101..63e6903880 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14105,13 +14105,13 @@ __metadata: linkType: hard "@playwright/test@npm:^1.32.3": - version: 1.41.0 - resolution: "@playwright/test@npm:1.41.0" + version: 1.41.1 + resolution: "@playwright/test@npm:1.41.1" dependencies: - playwright: 1.41.0 + playwright: 1.41.1 bin: playwright: cli.js - checksum: 3a7039f8cd14dd242154255417c100a99c3254a3c1fd26df2a11be24c10b06ef77d2736336d7743dedc5e1a6a52748e58ced730b6048f8bd75d8867ce81661e0 + checksum: d9877e777a1a7f60f097df57b6abc2478e2ae342930a409c8546c8aa40d6e206cbc16bf1c71b23414ac3fbad36dcae1ad79635d7f4eb705ab54d3c705e82ea04 languageName: node linkType: hard @@ -36854,27 +36854,27 @@ __metadata: languageName: node linkType: hard -"playwright-core@npm:1.41.0": - version: 1.41.0 - resolution: "playwright-core@npm:1.41.0" +"playwright-core@npm:1.41.1": + version: 1.41.1 + resolution: "playwright-core@npm:1.41.1" bin: playwright-core: cli.js - checksum: 14671265916a1fd0c71d94640de19c48bcce3f7dec35530f10e349e97030ea44ffa8ee518cbf811501e3ab2b74874aecf917e46bf40fea0570db1d4bea1fe7ac + checksum: c83446a560c6bd85f6f0cd586ff8c643b77e2005567386e12f85890936cc370673114b94cd883246018797cc1580e93b0296ade7d07275bb611b8962f5bb9693 languageName: node linkType: hard -"playwright@npm:1.41.0": - version: 1.41.0 - resolution: "playwright@npm:1.41.0" +"playwright@npm:1.41.1": + version: 1.41.1 + resolution: "playwright@npm:1.41.1" dependencies: fsevents: 2.3.2 - playwright-core: 1.41.0 + playwright-core: 1.41.1 dependenciesMeta: fsevents: optional: true bin: playwright: cli.js - checksum: e7c32136911c58e06b964fe7d33f8b3d8f6a067ae5218662a0811dd6c90e007db1774eb7e161f4aa748d760f404f4c066b7b7303c2b617f7448b6ee4b86c9999 + checksum: 3da7fb929abdec6adbdd8829f840580f5f210713214a8d230b130127f2270403eb2113c6c1418012221149707250fff896794c7c22c260dd09a92bf800227f31 languageName: node linkType: hard From a1a2ca5b52707ff418993fcd1d5fcd9938525a01 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 15:56:35 +0100 Subject: [PATCH 147/152] Apply suggestions from code review Signed-off-by: Patrik Oldsberg --- .changeset/long-suns-bow.md | 2 +- .changeset/nine-bulldogs-camp.md | 2 +- plugins/catalog-backend/src/schema/openapi.generated.ts | 2 +- plugins/search-backend/src/schema/openapi.generated.ts | 2 +- plugins/todo-backend/src/schema/openapi.generated.ts | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.changeset/long-suns-bow.md b/.changeset/long-suns-bow.md index 837482500f..81fafa8dae 100644 --- a/.changeset/long-suns-bow.md +++ b/.changeset/long-suns-bow.md @@ -1,7 +1,7 @@ --- '@backstage/plugin-catalog-backend': minor '@backstage/plugin-search-backend': minor -'@backstage/plugin-todo-backend': minor +'@backstage/plugin-todo-backend': patch --- Updates the OpenAPI spec to use plugin as `info.title` instead of package name. diff --git a/.changeset/nine-bulldogs-camp.md b/.changeset/nine-bulldogs-camp.md index f397bb9ab7..b6612b9948 100644 --- a/.changeset/nine-bulldogs-camp.md +++ b/.changeset/nine-bulldogs-camp.md @@ -2,7 +2,7 @@ '@backstage/catalog-client': minor '@backstage/plugin-catalog-backend': minor '@backstage/plugin-search-backend': minor -'@backstage/plugin-todo-backend': minor +'@backstage/plugin-todo-backend': patch --- Updates the OpenAPI specification title to plugin ID instead of package name. diff --git a/plugins/catalog-backend/src/schema/openapi.generated.ts b/plugins/catalog-backend/src/schema/openapi.generated.ts index 20dfa2c978..c29dd78ec6 100644 --- a/plugins/catalog-backend/src/schema/openapi.generated.ts +++ b/plugins/catalog-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/search-backend/src/schema/openapi.generated.ts b/plugins/search-backend/src/schema/openapi.generated.ts index cc38a6b293..06b576ba70 100644 --- a/plugins/search-backend/src/schema/openapi.generated.ts +++ b/plugins/search-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/plugins/todo-backend/src/schema/openapi.generated.ts b/plugins/todo-backend/src/schema/openapi.generated.ts index 2308ec0ab5..238be2fe5b 100644 --- a/plugins/todo-backend/src/schema/openapi.generated.ts +++ b/plugins/todo-backend/src/schema/openapi.generated.ts @@ -1,5 +1,5 @@ /* - * Copyright 2024 The Backstage Authors + * Copyright 2023 The Backstage Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. From 32f74203d0d5d38112624866b32f0cdaf29a297d Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 15:06:24 +0000 Subject: [PATCH 148/152] Update dependency @testing-library/dom to v9.3.4 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 6f5a1449ac..90c97f09cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -17177,8 +17177,8 @@ __metadata: linkType: soft "@testing-library/dom@npm:^9.0.0": - version: 9.3.3 - resolution: "@testing-library/dom@npm:9.3.3" + version: 9.3.4 + resolution: "@testing-library/dom@npm:9.3.4" dependencies: "@babel/code-frame": ^7.10.4 "@babel/runtime": ^7.12.5 @@ -17188,7 +17188,7 @@ __metadata: dom-accessibility-api: ^0.5.9 lz-string: ^1.5.0 pretty-format: ^27.0.2 - checksum: 34e0a564da7beb92aa9cc44a9080221e2412b1a132eb37be3d513fe6c58027674868deb9f86195756d98d15ba969a30fe00632a4e26e25df2a5a4f6ac0686e37 + checksum: dfd6fb0d6c7b4dd716ba3c47309bc9541b4a55772cb61758b4f396b3785efe2dbc75dc63423545c039078c7ffcc5e4b8c67c2db1b6af4799580466036f70026f languageName: node linkType: hard From 69d52450b8c7d9115ac6a3e92408ee372502e84a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 16:45:00 +0100 Subject: [PATCH 149/152] mkdocs.yml: update FAQ Signed-off-by: Patrik Oldsberg --- mkdocs.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index bf4024759b..8c9f84ba81 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -214,4 +214,7 @@ nav: - ADR011 - Plugin Package Structure: 'architecture-decisions/adr011-plugin-package-structure.md' - ADR012 - Plugin Package Structure: 'architecture-decisions/adr012-use-luxon-locale-and-date-presets.md' - ADR013 - Plugin Package Structure: 'architecture-decisions/adr013-use-node-fetch.md' - - FAQ: FAQ.md + - FAQ: + - Overview: 'faq/index.md' + - Product FAQ: 'faq/product.md' + - Technical FAQ: 'faq/technical.md' From c63d46de76f5b4b83a0c0660c6f9ee39724fe4c3 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 18:23:32 +0000 Subject: [PATCH 150/152] Update dependency @types/node to v16.18.74 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 62ceb0e964..ed48a1cced 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18451,9 +18451,9 @@ __metadata: linkType: hard "@types/node@npm:^16.11.26, @types/node@npm:^16.7.10, @types/node@npm:^16.9.2": - version: 16.18.72 - resolution: "@types/node@npm:16.18.72" - checksum: c0f1748b20566417d3b777d11962f84500b4cb9c77ebb59c51f2882c3e5141bd4636709dc63fe2e45d6d6799fc4c5a98931abcb2dd3a5b107e448d76ae848320 + version: 16.18.74 + resolution: "@types/node@npm:16.18.74" + checksum: 33d8f157b51cb2f6907a7019e0bef0733f6ebd025f208863e6e6ea4721348902f2c7ac9eeaadff9f1e138927921e620c7f0a655475a298569d7551f2d28e35f3 languageName: node linkType: hard From d1a5219e9a4c497daf078aa02e18c91f4a4431a6 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Sun, 21 Jan 2024 21:11:51 +0100 Subject: [PATCH 151/152] auth-backend: doc fix Signed-off-by: Patrik Oldsberg --- plugins/auth-backend/src/providers/microsoft/provider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/auth-backend/src/providers/microsoft/provider.ts b/plugins/auth-backend/src/providers/microsoft/provider.ts index 8947c7432b..b004cb9651 100644 --- a/plugins/auth-backend/src/providers/microsoft/provider.ts +++ b/plugins/auth-backend/src/providers/microsoft/provider.ts @@ -32,7 +32,7 @@ import { } from '@backstage/plugin-auth-backend-module-microsoft-provider'; /** - * Auth provider integration for GitLab auth + * Auth provider integration for Microsoft auth * * @public */ From b764082bace71df9386d420f680685b6e3b955a2 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sun, 21 Jan 2024 20:20:48 +0000 Subject: [PATCH 152/152] Update dependency core-js to v3.35.1 Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 800aa6702e..b556638d4b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -23309,9 +23309,9 @@ __metadata: linkType: hard "core-js@npm:^3.26.0, core-js@npm:^3.6.5": - version: 3.35.0 - resolution: "core-js@npm:3.35.0" - checksum: 25c224aca3df012b98f08f13ccbd8171ef5852acd33fd5e58e106d27f5f0c97de2fdbc520f0b4364d26253caf2deb3e5d265310f57d2a66ae6cc922850e649f0 + version: 3.35.1 + resolution: "core-js@npm:3.35.1" + checksum: e246af6b634be3763ffe3ce6ac4601b4dc5b928006fb6c95e5d08ecd82a2413bf36f00ffe178b89c9a8e94000288933a78a9881b2c9498e6cf312b031013b952 languageName: node linkType: hard