Merge branch 'master' of https://github.com/backstage/backstage into pr-draft

This commit is contained in:
Lykke Axlin
2021-09-18 10:32:01 +02:00
62 changed files with 1911 additions and 444 deletions
+45
View File
@@ -0,0 +1,45 @@
---
'@backstage/plugin-catalog-import': minor
---
Add initial support for customizing the catalog import page.
It is now possible to pass a custom layout to the import page, as it's already
supported by the search page. If no custom layout is passed, the default layout
is used.
```typescript
<Route path="/catalog-import" element={<CatalogImportPage />}>
<Page themeId="home">
<Header title="Register an existing component" />
<Content>
<ContentHeader title="Start tracking your components">
<SupportButton>
Start tracking your component in Backstage by adding it to the
software catalog.
</SupportButton>
</ContentHeader>
<Grid container spacing={2} direction="row-reverse">
<Grid item xs={12} md={4} lg={6} xl={8}>
Hello World
</Grid>
<Grid item xs={12} md={8} lg={6} xl={4}>
<ImportStepper />
</Grid>
</Grid>
</Content>
</Page>
</Route>
```
Previously it was possible to disable and customize the automatic pull request
feature by passing options to `<CatalogImportPage>` (`pullRequest.disable` and
`pullRequest.preparePullRequest`). This functionality is moved to the
`CatalogImportApi` which now provides an optional `preparePullRequest()`
function. The function can either be overridden to generate a different content
for the pull request, or removed to disable this feature.
The export of the long term deprecated legacy `<Router>` is removed, migrate to
`<CatalogImportPage>` instead.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-ldap': patch
---
Alters LDAP processor to handle one SearchEntry at a time
+19
View File
@@ -0,0 +1,19 @@
---
'@backstage/backend-common': patch
'@backstage/catalog-model': patch
'@backstage/cli': patch
'@backstage/config': patch
'@backstage/core-components': patch
'@backstage/plugin-catalog-backend': patch
'@backstage/plugin-catalog-backend-module-ldap': patch
'@backstage/plugin-catalog-backend-module-msgraph': patch
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-circleci': patch
'@backstage/plugin-kafka-backend': patch
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-rollbar': patch
'@backstage/plugin-rollbar-backend': patch
'@backstage/plugin-search-backend-module-pg': patch
---
Bump `lodash` to remediate `SNYK-JS-LODASH-590103` security vulnerability
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-techdocs': patch
---
Make techdocs context search bar width adjust on smaller screens.
+1 -1
View File
@@ -19,7 +19,7 @@
"@spotify/prettier-config": "^11.0.0",
"docusaurus": "^2.0.0-alpha.70",
"js-yaml": "^4.1.0",
"prettier": "^2.4.0",
"prettier": "^2.4.1",
"yarn-lock-check": "^1.0.5"
},
"prettier": "@spotify/prettier-config"
+9 -1
View File
@@ -306,7 +306,15 @@ class Index extends React.Component {
src={`${baseUrl}animations/backstage-techdocs-icon-1.gif`}
/>
<Block.Subtitle>Backstage TechDocs</Block.Subtitle>
<Block.Subtitle>
Backstage TechDocs{' '}
<a
title="Submit feedback for this feature. Click to learn more about this release."
href="https://backstage.io/blog/2021/09/16/the-techdocs-beta-has-landed"
>
(beta)
</a>
</Block.Subtitle>
<Block.Title small>Docs like code</Block.Title>
</Block.TextBox>
<Breakpoint
+4 -4
View File
@@ -5207,10 +5207,10 @@ prepend-http@^2.0.0:
resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
prettier@^2.4.0:
version "2.4.0"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.0.tgz#85bdfe0f70c3e777cf13a4ffff39713ca6f64cba"
integrity sha512-DsEPLY1dE5HF3BxCRBmD4uYZ+5DCbvatnolqTqcxEgKVZnL2kUfyu7b8pPQ5+hTBkdhU9SLUmK0/pHb07RE4WQ==
prettier@^2.4.1:
version "2.4.1"
resolved "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz#671e11c89c14a4cfc876ce564106c4a6726c9f5c"
integrity sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==
prismjs@^1.22.0:
version "1.23.0"
+1 -1
View File
@@ -55,7 +55,7 @@
"keyv": "^4.0.3",
"keyv-memcache": "^1.2.5",
"knex": "^0.95.1",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"logform": "^2.1.1",
"minimatch": "^3.0.4",
"minimist": "^1.2.5",
+1 -1
View File
@@ -36,7 +36,7 @@
"@types/yup": "^0.29.8",
"ajv": "^7.0.3",
"json-schema": "^0.3.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"uuid": "^8.0.0",
"yup": "^0.29.3"
},
+1 -1
View File
@@ -82,7 +82,7 @@
"jest": "^26.0.1",
"jest-css-modules": "^2.1.0",
"json-schema": "^0.3.0",
"lodash": "^4.17.19",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^1.4.1",
"node-libs-browser": "^2.2.1",
"ora": "^5.3.0",
+1 -1
View File
@@ -30,7 +30,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"lodash": "^4.17.15"
"lodash": "^4.17.21"
},
"devDependencies": {
"@types/jest": "^26.0.7",
+1 -1
View File
@@ -50,7 +50,7 @@
"d3-zoom": "^2.0.0",
"dagre": "^0.8.5",
"immer": "^9.0.1",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"pluralize": "^8.0.0",
"prop-types": "^15.7.2",
"qs": "^6.9.4",
@@ -103,6 +103,15 @@ export class LdapClient {
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
search(dn: string, options: SearchOptions): Promise<SearchEntry[]>;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (ae-forgotten-export) The symbol "SearchCallback" needs to be exported by the entry point index.d.ts
searchStreaming(
dn: string,
options: SearchOptions,
f: SearchCallback,
): Promise<void>;
}
// Warning: (ae-missing-release-tag) "LdapOrgEntityProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -34,7 +34,7 @@
"@backstage/plugin-catalog-backend": "^0.13.3",
"@types/ldapjs": "^2.2.0",
"ldapjs": "^2.2.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"winston": "^3.2.1"
},
"devDependencies": {
@@ -24,6 +24,10 @@ import {
LdapVendor,
} from './vendors';
export interface SearchCallback {
(entry: SearchEntry): void;
}
/**
* Basic wrapper for the ldapjs library.
*
@@ -120,6 +124,53 @@ export class LdapClient {
}
}
/**
* Performs an LDAP search operation, calls a function on each entry to limit memory usage
*
* @param dn The fully qualified base DN to search within
* @param options The search options
* @param f The callback to call on each search entry
*/
async searchStreaming(
dn: string,
options: SearchOptions,
f: SearchCallback,
): Promise<void> {
try {
return await new Promise<void>((resolve, reject) => {
this.client.search(dn, options, (err, res) => {
if (err) {
reject(new Error(errorString(err)));
}
res.on('searchReference', () => {
reject(new Error('Unable to handle referral'));
});
res.on('searchEntry', entry => {
f(entry);
});
res.on('error', e => {
reject(new Error(errorString(e)));
});
res.on('end', r => {
if (!r) {
throw new Error('Null response');
} else if (r.status !== 0) {
throw new Error(`Got status ${r.status}: ${r.errorMessage}`);
} else {
resolve();
}
});
});
});
} catch (e) {
throw new Error(`LDAP search at DN "${dn}" failed, ${e.message}`);
}
}
/**
* Get the Server Vendor.
* Currently only detects Microsoft Active Directory Servers.
@@ -67,7 +67,7 @@ function searchEntry(
describe('readLdapUsers', () => {
const client: jest.Mocked<LdapClient> = {
search: jest.fn(),
searchStreaming: jest.fn(),
getVendor: jest.fn(),
} as any;
@@ -75,18 +75,20 @@ describe('readLdapUsers', () => {
it('transfers all attributes from a default ldap vendor', async () => {
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.search.mockResolvedValue([
searchEntry({
uid: ['uid-value'],
description: ['description-value'],
cn: ['cn-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
entryDN: ['dn-value'],
entryUUID: ['uuid-value'],
}),
]);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
uid: ['uid-value'],
description: ['description-value'],
cn: ['cn-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
entryDN: ['dn-value'],
entryUUID: ['uuid-value'],
}),
);
});
const config: UserConfig = {
dn: 'ddd',
options: {},
@@ -129,23 +131,25 @@ describe('readLdapUsers', () => {
it('transfers all attributes from Microsoft Active Directory', async () => {
client.getVendor.mockResolvedValue(ActiveDirectoryVendor);
client.search.mockResolvedValue([
searchEntry({
uid: ['uid-value'],
description: ['description-value'],
cn: ['cn-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
distinguishedName: ['dn-value'],
objectGUID: [
Buffer.from([
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
152,
]),
],
}),
]);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
uid: ['uid-value'],
description: ['description-value'],
cn: ['cn-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
distinguishedName: ['dn-value'],
objectGUID: [
Buffer.from([
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
152,
]),
],
}),
);
});
const config: UserConfig = {
dn: 'ddd',
options: {},
@@ -189,7 +193,7 @@ describe('readLdapUsers', () => {
describe('readLdapGroups', () => {
const client: jest.Mocked<LdapClient> = {
search: jest.fn(),
searchStreaming: jest.fn(),
getVendor: jest.fn(),
} as any;
@@ -197,19 +201,21 @@ describe('readLdapGroups', () => {
it('transfers all attributes from a default ldap vendor', async () => {
client.getVendor.mockResolvedValue(DefaultLdapVendor);
client.search.mockResolvedValue([
searchEntry({
cn: ['cn-value'],
description: ['description-value'],
tt: ['type-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
member: ['e', 'f', 'g'],
entryDN: ['dn-value'],
entryUUID: ['uuid-value'],
}),
]);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
cn: ['cn-value'],
description: ['description-value'],
tt: ['type-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
member: ['e', 'f', 'g'],
entryDN: ['dn-value'],
entryUUID: ['uuid-value'],
}),
);
});
const config: GroupConfig = {
dn: 'ddd',
options: {},
@@ -260,24 +266,26 @@ describe('readLdapGroups', () => {
});
it('transfers all attributes from Microsoft Active Directory', async () => {
client.getVendor.mockResolvedValue(ActiveDirectoryVendor);
client.search.mockResolvedValue([
searchEntry({
cn: ['cn-value'],
description: ['description-value'],
tt: ['type-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
member: ['e', 'f', 'g'],
distinguishedName: ['dn-value'],
objectGUID: [
Buffer.from([
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
152,
]),
],
}),
]);
client.searchStreaming.mockImplementation(async (_dn, _opts, fn) => {
await fn(
searchEntry({
cn: ['cn-value'],
description: ['description-value'],
tt: ['type-value'],
mail: ['mail-value'],
avatarUrl: ['avatarUrl-value'],
memberOf: ['x', 'y', 'z'],
member: ['e', 'f', 'g'],
distinguishedName: ['dn-value'],
objectGUID: [
Buffer.from([
68, 2, 125, 190, 209, 0, 94, 73, 133, 33, 230, 174, 234, 195, 160,
152,
]),
],
}),
);
});
const config: GroupConfig = {
dn: 'ddd',
options: {},
@@ -107,13 +107,11 @@ export async function readLdapUsers(
const transformer = opts?.transformer ?? defaultUserTransformer;
const entries = await client.search(dn, options);
for (const user of entries) {
await client.searchStreaming(dn, options, async user => {
const entity = await transformer(vendor, config, user);
if (!entity) {
continue;
return;
}
mapReferencesAttr(user, vendor, map.memberOf, (myDn, vs) => {
@@ -121,7 +119,7 @@ export async function readLdapUsers(
});
entities.push(entity);
}
});
return { users: entities, userMemberOf };
}
@@ -210,24 +208,26 @@ export async function readLdapGroups(
const transformer = opts?.transformer ?? defaultGroupTransformer;
const entries = await client.search(dn, options);
for (const group of entries) {
const entity = await transformer(vendor, config, group);
if (!entity) {
continue;
await client.searchStreaming(dn, options, async entry => {
if (!entry) {
return;
}
mapReferencesAttr(group, vendor, map.memberOf, (myDn, vs) => {
const entity = await transformer(vendor, config, entry);
if (!entity) {
return;
}
mapReferencesAttr(entry, vendor, map.memberOf, (myDn, vs) => {
ensureItems(groupMemberOf, myDn, vs);
});
mapReferencesAttr(group, vendor, map.members, (myDn, vs) => {
mapReferencesAttr(entry, vendor, map.members, (myDn, vs) => {
ensureItems(groupMember, myDn, vs);
});
groups.push(entity);
}
});
return {
groups,
@@ -35,7 +35,7 @@
"@backstage/plugin-catalog-backend": "^0.13.5",
"@microsoft/microsoft-graph-types": "^1.25.0",
"cross-fetch": "^3.0.6",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"p-limit": "^3.0.2",
"winston": "^3.2.1",
"qs": "^6.9.4"
+1 -1
View File
@@ -51,7 +51,7 @@
"git-url-parse": "^11.6.0",
"glob": "^7.1.6",
"knex": "^0.95.1",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"morgan": "^1.10.0",
"p-limit": "^3.0.2",
+24 -12
View File
@@ -68,6 +68,11 @@ export interface CatalogImportApi {
// (undocumented)
analyzeUrl(url: string): Promise<AnalyzeResult>;
// (undocumented)
preparePullRequest?(): Promise<{
title: string;
body: string;
}>;
// (undocumented)
submitPullRequest(options: {
repositoryUrl: string;
fileContent: string;
@@ -94,10 +99,16 @@ export class CatalogImportClient implements CatalogImportApi {
identityApi: IdentityApi;
scmIntegrationsApi: ScmIntegrationRegistry;
catalogApi: CatalogApi;
configApi: ConfigApi;
});
// (undocumented)
analyzeUrl(url: string): Promise<AnalyzeResult>;
// (undocumented)
preparePullRequest(): Promise<{
title: string;
body: string;
}>;
// (undocumented)
submitPullRequest({
repositoryUrl,
fileContent,
@@ -114,11 +125,10 @@ export class CatalogImportClient implements CatalogImportApi {
}>;
}
// Warning: (ae-forgotten-export) The symbol "StepperProviderOpts" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "CatalogImportPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const CatalogImportPage: (opts: StepperProviderOpts) => JSX.Element;
export const CatalogImportPage: () => JSX.Element;
// Warning: (ae-missing-release-tag) "catalogImportPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -144,6 +154,11 @@ export function defaultGenerateStepper(
defaults: StepperProvider,
): StepperProvider;
// Warning: (ae-missing-release-tag) "DefaultImportPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const DefaultImportPage: () => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "EntityListComponent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -155,7 +170,12 @@ export const EntityListComponent: ({
onItemClick,
firstListItem,
withLinks,
}: Props_2) => JSX.Element;
}: Props) => JSX.Element;
// Warning: (ae-missing-release-tag) "ImportInfoCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const ImportInfoCard: () => JSX.Element;
// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "ImportStepper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
@@ -165,8 +185,7 @@ export const ImportStepper: ({
initialUrl,
generateStepper,
variant,
opts,
}: Props) => JSX.Element;
}: Props_2) => JSX.Element;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
@@ -203,11 +222,6 @@ export const PreviewPullRequestComponent: ({
classes,
}: Props_7) => JSX.Element;
// Warning: (ae-missing-release-tag) "Router" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export const Router: (opts: StepperProviderOpts) => JSX.Element;
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
// Warning: (tsdoc-param-tag-missing-hyphen) The @param block should be followed by a parameter name and then a hyphen
@@ -230,8 +244,6 @@ export const StepPrepareCreatePullRequest: ({
onPrepare,
onGoBack,
renderFormFields,
defaultTitle,
defaultBody,
}: Props_8) => JSX.Element;
// Warnings were encountered during analysis:
+2 -2
View File
@@ -29,7 +29,7 @@ import {
EntityListComponent,
ImportStepper,
} from '../src';
import { ImportComponentPage } from '../src/components/ImportComponentPage';
import { ImportPage } from '../src/components/ImportPage';
import { Content, Header, InfoCard, Page } from '@backstage/core-components';
const getEntityNames = (url: string): EntityName[] => [
@@ -252,7 +252,7 @@ createDevApp()
})
.addPage({
title: 'Catalog Import',
element: <ImportComponentPage />,
element: <ImportPage />,
})
.addPage({
title: 'Catalog Import 2',
@@ -15,8 +15,8 @@
*/
import { EntityName } from '@backstage/catalog-model';
import { PartialEntity } from '../types';
import { createApiRef } from '@backstage/core-plugin-api';
import { PartialEntity } from '../types';
export const catalogImportApiRef = createApiRef<CatalogImportApi>({
id: 'plugin.catalog-import.service',
@@ -42,6 +42,10 @@ export type AnalyzeResult =
export interface CatalogImportApi {
analyzeUrl(url: string): Promise<AnalyzeResult>;
preparePullRequest?(): Promise<{
title: string;
body: string;
}>;
submitPullRequest(options: {
repositoryUrl: string;
fileContent: string;
@@ -115,6 +115,11 @@ describe('CatalogImportClient', () => {
scmIntegrationsApi,
identityApi,
catalogApi,
configApi: new ConfigReader({
app: {
baseUrl: 'https://demo.backstage.io/',
},
}),
});
});
@@ -443,4 +448,13 @@ describe('CatalogImportClient', () => {
});
});
});
describe('preparePullRequest', () => {
test('should prepare pull request details', async () => {
await expect(catalogImportClient.preparePullRequest()).resolves.toEqual({
title: 'Add catalog-info.yaml config file',
body: expect.any(String),
});
});
});
});
@@ -17,6 +17,7 @@
import { CatalogApi } from '@backstage/catalog-client';
import { EntityName } from '@backstage/catalog-model';
import {
ConfigApi,
DiscoveryApi,
IdentityApi,
OAuthApi,
@@ -37,6 +38,7 @@ export class CatalogImportClient implements CatalogImportApi {
private readonly githubAuthApi: OAuthApi;
private readonly scmIntegrationsApi: ScmIntegrationRegistry;
private readonly catalogApi: CatalogApi;
private readonly configApi: ConfigApi;
constructor(options: {
discoveryApi: DiscoveryApi;
@@ -44,12 +46,14 @@ export class CatalogImportClient implements CatalogImportApi {
identityApi: IdentityApi;
scmIntegrationsApi: ScmIntegrationRegistry;
catalogApi: CatalogApi;
configApi: ConfigApi;
}) {
this.discoveryApi = options.discoveryApi;
this.githubAuthApi = options.githubAuthApi;
this.identityApi = options.identityApi;
this.scmIntegrationsApi = options.scmIntegrationsApi;
this.catalogApi = options.catalogApi;
this.configApi = options.configApi;
}
async analyzeUrl(url: string): Promise<AnalyzeResult> {
@@ -114,6 +118,24 @@ export class CatalogImportClient implements CatalogImportApi {
};
}
async preparePullRequest(): Promise<{
title: string;
body: string;
}> {
const appTitle =
this.configApi.getOptionalString('app.title') ?? 'Backstage';
const appBaseUrl = this.configApi.getString('app.baseUrl');
return {
title: 'Add catalog-info.yaml config file',
body: `This pull request adds a **Backstage entity metadata file** \
to this repository so that the component can be added to the \
[${appTitle} software catalog](${appBaseUrl}).\n\nAfter this pull request is merged, \
the component will become available.\n\nFor more information, read an \
[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`,
};
}
async submitPullRequest({
repositoryUrl,
fileContent,
@@ -15,21 +15,19 @@
*/
import { CatalogClient } from '@backstage/catalog-client';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { wrapInTestApp } from '@backstage/test-utils';
import { act, render } from '@testing-library/react';
import React from 'react';
import { catalogImportApiRef, CatalogImportClient } from '../api';
import { ImportComponentPage } from './ImportComponentPage';
import {
ApiProvider,
ApiRegistry,
ConfigReader,
} from '@backstage/core-app-api';
import { configApiRef } from '@backstage/core-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
import { catalogImportApiRef, CatalogImportClient } from '../../api';
import { DefaultImportPage } from './DefaultImportPage';
describe('<ImportComponentPage />', () => {
describe('<DefaultImportPage />', () => {
const identityApi = {
getUserId: () => {
return 'user';
@@ -63,23 +61,20 @@ describe('<ImportComponentPage />', () => {
identityApi,
scmIntegrationsApi: {} as any,
catalogApi: {} as any,
configApi: {} as any,
}),
);
});
it('renders without exploding', async () => {
await act(async () => {
const { getByText } = render(
wrapInTestApp(
<ApiProvider apis={apis}>
<ImportComponentPage />
</ApiProvider>,
),
);
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<DefaultImportPage />
</ApiProvider>,
);
expect(
await getByText('Start tracking your component in Backstage'),
).toBeInTheDocument();
});
expect(
getByText('Start tracking your component in Backstage'),
).toBeInTheDocument();
});
});
@@ -0,0 +1,57 @@
/*
* 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 {
Content,
ContentHeader,
Header,
Page,
SupportButton,
} from '@backstage/core-components';
import { configApiRef, useApi } from '@backstage/core-plugin-api';
import { Grid } from '@material-ui/core';
import React from 'react';
import { ImportInfoCard } from '../ImportInfoCard';
import { ImportStepper } from '../ImportStepper';
export const DefaultImportPage = () => {
const configApi = useApi(configApiRef);
const appTitle = configApi.getOptional('app.title') || 'Backstage';
return (
<Page themeId="home">
<Header title="Register an existing component" />
<Content>
<ContentHeader title={`Start tracking your component in ${appTitle}`}>
<SupportButton>
Start tracking your component in {appTitle} by adding it to the
software catalog.
</SupportButton>
</ContentHeader>
<Grid container spacing={2} direction="row-reverse">
<Grid item xs={12} md={4} lg={6} xl={8}>
<ImportInfoCard />
</Grid>
<Grid item xs={12} md={8} lg={6} xl={4}>
<ImportStepper />
</Grid>
</Grid>
</Content>
</Page>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { DefaultImportPage } from './DefaultImportPage';
@@ -1,115 +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 { Chip, Grid, Typography } from '@material-ui/core';
import React from 'react';
import { ImportStepper } from './ImportStepper';
import { StepperProviderOpts } from './ImportStepper/defaults';
import { configApiRef, useApi } from '@backstage/core-plugin-api';
import {
Content,
ContentHeader,
Header,
InfoCard,
Page,
SupportButton,
} from '@backstage/core-components';
export const ImportComponentPage = (opts: StepperProviderOpts) => {
const configApi = useApi(configApiRef);
const appTitle = configApi.getOptional('app.title') || 'Backstage';
const integrations = configApi.getConfig('integrations');
const hasGithubIntegration = integrations.has('github');
return (
<Page themeId="home">
<Header title="Register an existing component" />
<Content>
<ContentHeader title={`Start tracking your component in ${appTitle}`}>
<SupportButton>
Start tracking your component in {appTitle} by adding it to the
software catalog.
</SupportButton>
</ContentHeader>
<Grid container spacing={2} direction="row-reverse">
<Grid item xs={12} md={4} lg={6} xl={8}>
<InfoCard
title="Register an existing component"
deepLink={{
title: 'Learn more about the Software Catalog',
link: 'https://backstage.io/docs/features/software-catalog/software-catalog-overview',
}}
>
<Typography variant="body2" paragraph>
Enter the URL to your source code repository to add it to{' '}
{appTitle}.
</Typography>
<Typography variant="h6">
Link to an existing entity file
</Typography>
<Typography variant="subtitle2" color="textSecondary" paragraph>
Example:{' '}
<code>
https://github.com/backstage/backstage/blob/master/catalog-info.yaml
</code>
</Typography>
<Typography variant="body2" paragraph>
The wizard analyzes the file, previews the entities, and adds
them to the {appTitle} catalog.
</Typography>
{hasGithubIntegration && (
<>
<Typography variant="h6">
Link to a repository{' '}
<Chip label="GitHub only" variant="outlined" size="small" />
</Typography>
<Typography
variant="subtitle2"
color="textSecondary"
paragraph
>
Example: <code>https://github.com/backstage/backstage</code>
</Typography>
<Typography variant="body2" paragraph>
The wizard discovers all <code>catalog-info.yaml</code>{' '}
files in the repository, previews the entities, and adds
them to the {appTitle} catalog.
</Typography>
{!opts?.pullRequest?.disable && (
<Typography variant="body2" paragraph>
If no entities are found, the wizard will prepare a Pull
Request that adds an example{' '}
<code>catalog-info.yaml</code> and prepares the {appTitle}{' '}
catalog to load all entities as soon as the Pull Request
is merged.
</Typography>
)}
</>
)}
</InfoCard>
</Grid>
<Grid item xs={12} md={8} lg={6} xl={4}>
<ImportStepper opts={opts} />
</Grid>
</Grid>
</Content>
</Page>
);
};
@@ -0,0 +1,88 @@
/*
* 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 {
ApiProvider,
ApiRegistry,
ConfigReader,
} from '@backstage/core-app-api';
import { configApiRef } from '@backstage/core-plugin-api';
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
import { CatalogImportApi, catalogImportApiRef } from '../../api';
import { ImportInfoCard } from './ImportInfoCard';
describe('<ImportInfoCard />', () => {
let apis: ApiRegistry;
let catalogImportApi: jest.Mocked<CatalogImportApi>;
beforeEach(() => {
catalogImportApi = {
analyzeUrl: jest.fn(),
submitPullRequest: jest.fn(),
};
apis = ApiRegistry.with(
configApiRef,
new ConfigReader({
integrations: {
github: [{ token: 'my-token' }],
},
}),
).with(catalogImportApiRef, catalogImportApi);
});
it('renders without exploding', async () => {
apis = ApiRegistry.with(
configApiRef,
new ConfigReader({ integrations: {} }),
).with(catalogImportApiRef, catalogImportApi);
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<ImportInfoCard />
</ApiProvider>,
);
expect(getByText('Register an existing component')).toBeInTheDocument();
});
it('renders section on GitHub discovery if supported', async () => {
catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' });
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<ImportInfoCard />
</ApiProvider>,
);
expect(getByText(/The wizard discovers all/)).toBeInTheDocument();
});
it('renders section on pull requests if supported', async () => {
catalogImportApi.preparePullRequest = async () => ({ title: '', body: '' });
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<ImportInfoCard />
</ApiProvider>,
);
expect(
getByText(/the wizard will prepare a Pull Request/),
).toBeInTheDocument();
});
});
@@ -0,0 +1,79 @@
/*
* 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 { InfoCard } from '@backstage/core-components';
import { configApiRef, useApi } from '@backstage/core-plugin-api';
import { Chip, Typography } from '@material-ui/core';
import React from 'react';
import { catalogImportApiRef } from '../../api';
export const ImportInfoCard = () => {
const configApi = useApi(configApiRef);
const appTitle = configApi.getOptional('app.title') || 'Backstage';
const catalogImportApi = useApi(catalogImportApiRef);
const integrations = configApi.getConfig('integrations');
const hasGithubIntegration = integrations.has('github');
return (
<InfoCard
title="Register an existing component"
deepLink={{
title: 'Learn more about the Software Catalog',
link: 'https://backstage.io/docs/features/software-catalog/software-catalog-overview',
}}
>
<Typography variant="body2" paragraph>
Enter the URL to your source code repository to add it to {appTitle}.
</Typography>
<Typography variant="h6">Link to an existing entity file</Typography>
<Typography variant="subtitle2" color="textSecondary" paragraph>
Example:{' '}
<code>
https://github.com/backstage/backstage/blob/master/catalog-info.yaml
</code>
</Typography>
<Typography variant="body2" paragraph>
The wizard analyzes the file, previews the entities, and adds them to
the {appTitle} catalog.
</Typography>
{hasGithubIntegration && (
<>
<Typography variant="h6">
Link to a repository{' '}
<Chip label="GitHub only" variant="outlined" size="small" />
</Typography>
<Typography variant="subtitle2" color="textSecondary" paragraph>
Example: <code>https://github.com/backstage/backstage</code>
</Typography>
<Typography variant="body2" paragraph>
The wizard discovers all <code>catalog-info.yaml</code> files in the
repository, previews the entities, and adds them to the {appTitle}{' '}
catalog.
</Typography>
{catalogImportApi.preparePullRequest && (
<Typography variant="body2" paragraph>
If no entities are found, the wizard will prepare a Pull Request
that adds an example <code>catalog-info.yaml</code> and prepares
the {appTitle} catalog to load all entities as soon as the Pull
Request is merged.
</Typography>
)}
</>
)}
</InfoCard>
);
};
@@ -0,0 +1,17 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ImportInfoCard } from './ImportInfoCard';
@@ -0,0 +1,100 @@
/*
* 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 { CatalogClient } from '@backstage/catalog-client';
import {
ApiProvider,
ApiRegistry,
ConfigReader,
} from '@backstage/core-app-api';
import { configApiRef } from '@backstage/core-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { renderInTestApp } from '@backstage/test-utils';
import React from 'react';
import { useOutlet } from 'react-router';
import { catalogImportApiRef, CatalogImportClient } from '../../api';
import { ImportPage } from './ImportPage';
jest.mock('react-router', () => ({
...jest.requireActual('react-router'),
useOutlet: jest.fn(),
}));
describe('<ImportPage />', () => {
const identityApi = {
getUserId: () => {
return 'user';
},
getProfile: () => {
return {};
},
getIdToken: () => {
return Promise.resolve('token');
},
signOut: () => {
return Promise.resolve();
},
};
let apis: ApiRegistry;
beforeEach(() => {
apis = ApiRegistry.with(
configApiRef,
new ConfigReader({ integrations: {} }),
)
.with(catalogApiRef, new CatalogClient({ discoveryApi: {} as any }))
.with(
catalogImportApiRef,
new CatalogImportClient({
discoveryApi: {} as any,
githubAuthApi: {
getAccessToken: async () => 'token',
},
identityApi,
scmIntegrationsApi: {} as any,
catalogApi: {} as any,
configApi: new ConfigReader({}),
}),
);
});
afterEach(() => jest.resetAllMocks());
it('renders without exploding', async () => {
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<ImportPage />
</ApiProvider>,
);
expect(
getByText('Start tracking your component in Backstage'),
).toBeInTheDocument();
});
it('renders with custom children', async () => {
(useOutlet as jest.Mock).mockReturnValue(<div>Hello World</div>);
const { getByText } = await renderInTestApp(
<ApiProvider apis={apis}>
<ImportPage />
</ApiProvider>,
);
expect(getByText('Hello World')).toBeInTheDocument();
});
});
@@ -15,12 +15,11 @@
*/
import React from 'react';
import { Route, Routes } from 'react-router-dom';
import { ImportComponentPage } from './ImportComponentPage';
import { StepperProviderOpts } from './ImportStepper/defaults';
import { useOutlet } from 'react-router';
import { DefaultImportPage } from '../DefaultImportPage';
export const Router = (opts: StepperProviderOpts) => (
<Routes>
<Route element={<ImportComponentPage {...opts} />} />
</Routes>
);
export const ImportPage = () => {
const outlet = useOutlet();
return outlet || <DefaultImportPage />;
};
@@ -0,0 +1,17 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { ImportPage } from './ImportPage';
@@ -14,21 +14,20 @@
* limitations under the License.
*/
import { InfoCard, InfoCardVariants } from '@backstage/core-components';
import { useApi } from '@backstage/core-plugin-api';
import { Step, StepContent, Stepper } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import React, { useMemo } from 'react';
import { catalogImportApiRef } from '../../api';
import { ImportFlows, ImportState, useImportState } from '../useImportState';
import {
defaultGenerateStepper,
defaultStepper,
StepConfiguration,
StepperProvider,
StepperProviderOpts,
} from './defaults';
import { configApiRef, useApi } from '@backstage/core-plugin-api';
import { InfoCard, InfoCardVariants } from '@backstage/core-components';
const useStyles = makeStyles(() => ({
stepperRoot: {
padding: 0,
@@ -42,16 +41,14 @@ type Props = {
defaults: StepperProvider,
) => StepperProvider;
variant?: InfoCardVariants;
opts?: StepperProviderOpts;
};
export const ImportStepper = ({
initialUrl,
generateStepper = defaultGenerateStepper,
variant,
opts,
}: Props) => {
const configApi = useApi(configApiRef);
const catalogImportApi = useApi(catalogImportApiRef);
const classes = useStyles();
const state = useImportState({ initialUrl });
@@ -79,25 +76,25 @@ export const ImportStepper = ({
{render(
states.analyze(
state as Extract<ImportState, { activeState: 'analyze' }>,
{ apis: { configApi }, opts },
{ apis: { catalogImportApi } },
),
)}
{render(
states.prepare(
state as Extract<ImportState, { activeState: 'prepare' }>,
{ apis: { configApi }, opts },
{ apis: { catalogImportApi } },
),
)}
{render(
states.review(
state as Extract<ImportState, { activeState: 'review' }>,
{ apis: { configApi }, opts },
{ apis: { catalogImportApi } },
),
)}
{render(
states.finish(
state as Extract<ImportState, { activeState: 'finish' }>,
{ apis: { configApi }, opts },
{ apis: { catalogImportApi } },
),
)}
</Stepper>
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import { ConfigApi } from '@backstage/core-plugin-api';
import {
Box,
Checkbox,
@@ -35,22 +34,9 @@ import {
} from '../StepPrepareCreatePullRequest';
import { StepPrepareSelectLocations } from '../StepPrepareSelectLocations';
import { StepReviewLocation } from '../StepReviewLocation';
import { StepperApis } from '../types';
import { ImportFlows, ImportState } from '../useImportState';
export type StepperProviderOpts = {
pullRequest?: {
disable?: boolean;
preparePullRequest?: (apis: StepperApis) => {
title?: string;
body?: string;
};
};
};
type StepperApis = {
configApi: ConfigApi;
};
export type StepConfiguration = {
stepLabel: React.ReactElement;
content: React.ReactElement;
@@ -59,41 +45,22 @@ export type StepConfiguration = {
export type StepperProvider = {
analyze: (
s: Extract<ImportState, { activeState: 'analyze' }>,
opts: { apis: StepperApis; opts?: StepperProviderOpts },
opts: { apis: StepperApis },
) => StepConfiguration;
prepare: (
s: Extract<ImportState, { activeState: 'prepare' }>,
opts: { apis: StepperApis; opts?: StepperProviderOpts },
opts: { apis: StepperApis },
) => StepConfiguration;
review: (
s: Extract<ImportState, { activeState: 'review' }>,
opts: { apis: StepperApis; opts?: StepperProviderOpts },
opts: { apis: StepperApis },
) => StepConfiguration;
finish: (
s: Extract<ImportState, { activeState: 'finish' }>,
opts: { apis: StepperApis; opts?: StepperProviderOpts },
opts: { apis: StepperApis },
) => StepConfiguration;
};
function defaultPreparePullRequest(
apis: StepperApis,
{ title, body }: { title?: string; body?: string } = {},
) {
const appTitle = apis.configApi.getOptionalString('app.title') ?? 'Backstage';
const appBaseUrl = apis.configApi.getString('app.baseUrl');
return {
title: title ?? 'Add catalog-info.yaml config file',
body:
body ??
`This pull request adds a **Backstage entity metadata file** \
to this repository so that the component can be added to the \
[${appTitle} software catalog](${appBaseUrl}).\n\nAfter this pull request is merged, \
the component will become available.\n\nFor more information, read an \
[overview of the Backstage software catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview).`,
};
}
/**
* The default stepper generation function.
*
@@ -169,13 +136,6 @@ export function defaultGenerateStepper(
return defaults.prepare(state, opts);
}
const preparePullRequest =
opts?.opts?.pullRequest?.preparePullRequest;
const { title, body } = defaultPreparePullRequest(
opts.apis,
preparePullRequest ? preparePullRequest(opts.apis) : {},
);
return {
stepLabel: <StepLabel>Create Pull Request</StepLabel>,
content: (
@@ -183,8 +143,6 @@ export function defaultGenerateStepper(
analyzeResult={state.analyzeResult}
onPrepare={state.onPrepare}
onGoBack={state.onGoBack}
defaultTitle={title}
defaultBody={body}
renderFormFields={({
values,
setValue,
@@ -299,14 +257,14 @@ export function defaultGenerateStepper(
}
export const defaultStepper: StepperProvider = {
analyze: (state, { opts }) => ({
analyze: (state, { apis }) => ({
stepLabel: <StepLabel>Select URL</StepLabel>,
content: (
<StepInitAnalyzeUrl
key="analyze"
analysisUrl={state.analysisUrl}
onAnalysis={state.onAnalysis}
disablePullRequest={opts?.pullRequest?.disable}
disablePullRequest={!apis.catalogImportApi.preparePullRequest}
/>
),
}),
@@ -14,15 +14,14 @@
* limitations under the License.
*/
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
import { errorApiRef } from '@backstage/core-plugin-api';
import { act, render } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { AnalyzeResult, catalogImportApiRef } from '../../api/';
import { StepInitAnalyzeUrl } from './StepInitAnalyzeUrl';
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
import { errorApiRef } from '@backstage/core-plugin-api';
describe('<StepInitAnalyzeUrl />', () => {
const catalogImportApi: jest.Mocked<typeof catalogImportApiRef.T> = {
analyzeUrl: jest.fn(),
@@ -14,6 +14,8 @@
* limitations under the License.
*/
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
import { errorApiRef } from '@backstage/core-plugin-api';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { TextField } from '@material-ui/core';
import { act, render, screen } from '@testing-library/react';
@@ -25,12 +27,12 @@ import {
generateEntities,
StepPrepareCreatePullRequest,
} from './StepPrepareCreatePullRequest';
import { ApiProvider, ApiRegistry } from '@backstage/core-app-api';
describe('<StepPrepareCreatePullRequest />', () => {
const catalogImportApi: jest.Mocked<typeof catalogImportApiRef.T> = {
analyzeUrl: jest.fn(),
submitPullRequest: jest.fn(),
preparePullRequest: jest.fn(),
};
const catalogApi: jest.Mocked<typeof catalogApiRef.T> = {
@@ -44,12 +46,16 @@ describe('<StepPrepareCreatePullRequest />', () => {
removeEntityByUid: jest.fn(),
};
const errorApi: jest.Mocked<typeof errorApiRef.T> = {
error$: jest.fn(),
post: jest.fn(),
};
const Wrapper = ({ children }: { children?: React.ReactNode }) => (
<ApiProvider
apis={ApiRegistry.with(catalogImportApiRef, catalogImportApi).with(
catalogApiRef,
catalogApi,
)}
apis={ApiRegistry.with(catalogImportApiRef, catalogImportApi)
.with(catalogApiRef, catalogApi)
.with(errorApiRef, errorApi)}
>
{children}
</ApiProvider>
@@ -77,16 +83,19 @@ describe('<StepPrepareCreatePullRequest />', () => {
beforeEach(() => {
jest.resetAllMocks();
(catalogImportApi.preparePullRequest! as jest.Mock).mockResolvedValue({
title: 'My title',
body: 'My **body**',
});
});
it('renders without exploding', async () => {
catalogApi.getEntities.mockReturnValue(Promise.resolve({ items: [] }));
await act(async () => {
const { getByText } = render(
const { findByText } = render(
<StepPrepareCreatePullRequest
defaultTitle="My title"
defaultBody="My **body**"
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
@@ -105,8 +114,8 @@ describe('<StepPrepareCreatePullRequest />', () => {
},
);
const title = getByText('My title');
const description = getByText('body', { selector: 'strong' });
const title = await findByText('My title');
const description = await findByText('body', { selector: 'strong' });
expect(title).toBeInTheDocument();
expect(title).toBeVisible();
expect(description).toBeInTheDocument();
@@ -124,10 +133,8 @@ describe('<StepPrepareCreatePullRequest />', () => {
);
await act(async () => {
await render(
render(
<StepPrepareCreatePullRequest
defaultTitle="My title"
defaultBody="My **body**"
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
@@ -154,11 +161,9 @@ describe('<StepPrepareCreatePullRequest />', () => {
},
);
await userEvent.type(await screen.getByLabelText('name'), '-changed');
await userEvent.type(await screen.getByLabelText('owner'), '-changed');
await userEvent.click(
await screen.getByRole('button', { name: /Create PR/i }),
);
userEvent.type(await screen.findByLabelText('name'), '-changed');
userEvent.type(await screen.findByLabelText('owner'), '-changed');
userEvent.click(screen.getByRole('button', { name: /Create PR/i }));
});
expect(catalogImportApi.submitPullRequest).toBeCalledTimes(1);
@@ -212,10 +217,8 @@ spec:
);
await act(async () => {
await render(
render(
<StepPrepareCreatePullRequest
defaultTitle="My title"
defaultBody="My **body**"
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={({ register }) => {
@@ -234,8 +237,8 @@ spec:
},
);
await userEvent.click(
await screen.getByRole('button', { name: /Create PR/i }),
userEvent.click(
await screen.findByRole('button', { name: /Create PR/i }),
);
});
@@ -261,10 +264,8 @@ spec:
);
await act(async () => {
await render(
render(
<StepPrepareCreatePullRequest
defaultTitle="My title"
defaultBody="My **body**"
analyzeResult={analyzeResult}
onPrepare={onPrepareFn}
renderFormFields={renderFormFieldsFn}
@@ -15,14 +15,14 @@
*/
import { Entity } from '@backstage/catalog-model';
import { useApi } from '@backstage/core-plugin-api';
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import {
catalogApiRef,
formatEntityRefTitle,
} from '@backstage/plugin-catalog-react';
import { Box, FormHelperText, Grid, Typography } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import React, { useCallback, useState } from 'react';
import React, { useCallback, useEffect, useState } from 'react';
import { UnpackNestedValue, UseFormReturn } from 'react-hook-form';
import { useAsync } from 'react-use';
import YAML from 'yaml';
@@ -59,9 +59,6 @@ type Props = {
) => void;
onGoBack?: () => void;
defaultTitle: string;
defaultBody: string;
renderFormFields: (
props: Pick<
UseFormReturn<FormData>,
@@ -99,16 +96,30 @@ export const StepPrepareCreatePullRequest = ({
onPrepare,
onGoBack,
renderFormFields,
defaultTitle,
defaultBody,
}: Props) => {
const classes = useStyles();
const catalogApi = useApi(catalogApiRef);
const catalogInfoApi = useApi(catalogImportApiRef);
const catalogImportApi = useApi(catalogImportApiRef);
const errorApi = useApi(errorApiRef);
const [submitted, setSubmitted] = useState(false);
const [error, setError] = useState<string>();
const {
loading: prDefaultsLoading,
value: prDefaults,
error: prDefaultsError,
} = useAsync(
() => catalogImportApi.preparePullRequest!(),
[catalogImportApi.preparePullRequest],
);
useEffect(() => {
if (prDefaultsError) {
errorApi.post(prDefaultsError);
}
}, [prDefaultsError, errorApi]);
const { loading: groupsLoading, value: groups } = useAsync(async () => {
const groupEntities = await catalogApi.getEntities({
filter: { kind: 'group' },
@@ -124,7 +135,7 @@ export const StepPrepareCreatePullRequest = ({
setSubmitted(true);
try {
const pr = await catalogInfoApi.submitPullRequest({
const pr = await catalogImportApi.submitPullRequest({
repositoryUrl: analyzeResult.url,
title: data.title,
body: data.body,
@@ -171,7 +182,7 @@ export const StepPrepareCreatePullRequest = ({
analyzeResult.generatedEntities,
analyzeResult.integrationType,
analyzeResult.url,
catalogInfoApi,
catalogImportApi,
onPrepare,
],
);
@@ -184,79 +195,81 @@ export const StepPrepareCreatePullRequest = ({
a Pull Request that creates one.
</Typography>
<PreparePullRequestForm<FormData>
onSubmit={handleResult}
defaultValues={{
title: defaultTitle,
body: defaultBody,
owner:
(analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',
componentName:
analyzeResult.generatedEntities[0]?.metadata?.name || '',
useCodeowners: false,
}}
render={({ values, formState, register, setValue }) => (
<>
{renderFormFields({
values,
formState,
register,
setValue,
groups: groups ?? [],
groupsLoading,
})}
{!prDefaultsLoading && (
<PreparePullRequestForm<FormData>
onSubmit={handleResult}
defaultValues={{
title: prDefaults?.title ?? '',
body: prDefaults?.body ?? '',
owner:
(analyzeResult.generatedEntities[0]?.spec?.owner as string) || '',
componentName:
analyzeResult.generatedEntities[0]?.metadata?.name || '',
useCodeowners: false,
}}
render={({ values, formState, register, setValue }) => (
<>
{renderFormFields({
values,
formState,
register,
setValue,
groups: groups ?? [],
groupsLoading,
})}
<Box marginTop={2}>
<Typography variant="h6">Preview Pull Request</Typography>
</Box>
<Box marginTop={2}>
<Typography variant="h6">Preview Pull Request</Typography>
</Box>
<PreviewPullRequestComponent
title={values.title}
description={values.body}
classes={{
card: classes.previewCard,
cardContent: classes.previewCardContent,
}}
/>
<PreviewPullRequestComponent
title={values.title}
description={values.body}
classes={{
card: classes.previewCard,
cardContent: classes.previewCardContent,
}}
/>
<Box marginTop={2} marginBottom={1}>
<Typography variant="h6">Preview Entities</Typography>
</Box>
<Box marginTop={2} marginBottom={1}>
<Typography variant="h6">Preview Entities</Typography>
</Box>
<PreviewCatalogInfoComponent
entities={generateEntities(
analyzeResult.generatedEntities,
values.componentName,
values.owner,
)}
repositoryUrl={analyzeResult.url}
classes={{
card: classes.previewCard,
cardContent: classes.previewCardContent,
}}
/>
{error && <FormHelperText error>{error}</FormHelperText>}
<Grid container spacing={0}>
{onGoBack && (
<BackButton onClick={onGoBack} disabled={submitted} />
)}
<NextButton
type="submit"
disabled={Boolean(
formState.errors.title ||
formState.errors.body ||
formState.errors.owner,
<PreviewCatalogInfoComponent
entities={generateEntities(
analyzeResult.generatedEntities,
values.componentName,
values.owner,
)}
loading={submitted}
>
Create PR
</NextButton>
</Grid>
</>
)}
/>
repositoryUrl={analyzeResult.url}
classes={{
card: classes.previewCard,
cardContent: classes.previewCardContent,
}}
/>
{error && <FormHelperText error>{error}</FormHelperText>}
<Grid container spacing={0}>
{onGoBack && (
<BackButton onClick={onGoBack} disabled={submitted} />
)}
<NextButton
type="submit"
disabled={Boolean(
formState.errors.title ||
formState.errors.body ||
formState.errors.owner,
)}
loading={submitted}
>
Create PR
</NextButton>
</Grid>
</>
)}
/>
)}
</>
);
};
@@ -14,7 +14,9 @@
* limitations under the License.
*/
export * from './ImportStepper';
export * from './DefaultImportPage';
export * from './EntityListComponent';
export * from './ImportInfoCard';
export * from './ImportStepper';
export * from './StepInitAnalyzeUrl';
export * from './StepPrepareCreatePullRequest';
@@ -0,0 +1,21 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { CatalogImportApi } from '../api';
export type StepperApis = {
catalogImportApi: CatalogImportApi;
};
-1
View File
@@ -25,6 +25,5 @@ export {
catalogImportPlugin as plugin,
CatalogImportPage,
} from './plugin';
export { Router } from './components/Router';
export * from './components';
export * from './api';
+8 -4
View File
@@ -14,10 +14,8 @@
* limitations under the License.
*/
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { catalogImportApiRef, CatalogImportClient } from './api';
import {
configApiRef,
createApiFactory,
createPlugin,
createRoutableExtension,
@@ -26,6 +24,9 @@ import {
githubAuthApiRef,
identityApiRef,
} from '@backstage/core-plugin-api';
import { scmIntegrationsApiRef } from '@backstage/integration-react';
import { catalogApiRef } from '@backstage/plugin-catalog-react';
import { catalogImportApiRef, CatalogImportClient } from './api';
export const rootRouteRef = createRouteRef({
path: '',
@@ -43,6 +44,7 @@ export const catalogImportPlugin = createPlugin({
identityApi: identityApiRef,
scmIntegrationsApi: scmIntegrationsApiRef,
catalogApi: catalogApiRef,
configApi: configApiRef,
},
factory: ({
discoveryApi,
@@ -50,6 +52,7 @@ export const catalogImportPlugin = createPlugin({
identityApi,
scmIntegrationsApi,
catalogApi,
configApi,
}) =>
new CatalogImportClient({
discoveryApi,
@@ -57,6 +60,7 @@ export const catalogImportPlugin = createPlugin({
scmIntegrationsApi,
identityApi,
catalogApi,
configApi,
}),
}),
],
@@ -67,7 +71,7 @@ export const catalogImportPlugin = createPlugin({
export const CatalogImportPage = catalogImportPlugin.provide(
createRoutableExtension({
component: () => import('./components/Router').then(m => m.Router),
component: () => import('./components/ImportPage').then(m => m.ImportPage),
mountPoint: rootRouteRef,
}),
);
+1 -1
View File
@@ -41,7 +41,7 @@
"@material-ui/lab": "4.0.0-alpha.57",
"@types/react": "*",
"jwt-decode": "^3.1.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"qs": "^6.9.4",
"react": "^16.13.1",
"react-router": "6.0.0-beta.0",
+1 -1
View File
@@ -42,7 +42,7 @@
"@material-ui/lab": "4.0.0-alpha.57",
"circleci-api": "^4.0.0",
"humanize-duration": "^3.27.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"luxon": "^2.0.2",
"react": "^16.13.1",
"react-dom": "^16.13.1",
+1 -1
View File
@@ -40,7 +40,7 @@
"express": "^4.17.1",
"express-promise-router": "^4.1.0",
"kafkajs": "^1.16.0-beta.6",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"winston": "^3.2.1"
},
"devDependencies": {
+1 -1
View File
@@ -47,7 +47,7 @@
"express-promise-router": "^4.1.0",
"fs-extra": "9.1.0",
"helmet": "^4.0.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"morgan": "^1.10.0",
"stream-buffers": "^3.0.2",
"winston": "^3.2.1",
+1 -1
View File
@@ -42,7 +42,7 @@
"express-promise-router": "^4.1.0",
"fs-extra": "9.1.0",
"helmet": "^4.0.0",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"morgan": "^1.10.0",
"winston": "^3.2.1",
"yn": "^4.0.0"
+1 -1
View File
@@ -40,7 +40,7 @@
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-router": "6.0.0-beta.0",
@@ -0,0 +1,3 @@
module.exports = {
extends: [require.resolve('@backstage/cli/config/eslint.backend')],
};
@@ -0,0 +1,156 @@
# scaffolder-backend-module-yeoman
Welcome to the `run:yeoman` action for the `scaffolder-backend`.
## Getting started
You need to configure the action in your backend:
## From your Backstage root directory
```
cd packages/backend
yarn add @backstage/plugin-scaffolder-backend-module-yeoman
```
Configure the action:
(you can check the [docs](https://backstage.io/docs/features/software-templates/writing-custom-actions#registering-custom-actions) to see all options):
```typescript
// packages/backend/src/plugins/scaffolder.ts
const actions = [
createRunYeomanAction(),
...createBuiltInActions({
containerRunner,
integrations,
config,
catalogClient,
reader,
}),
];
return await createRouter({
containerRunner,
logger,
config,
database,
catalogClient,
reader,
actions,
});
```
After that you can use the action in your template:
```yaml
apiVersion: backstage.io/v1beta2
kind: Template
metadata:
name: yeoman-demo
title: Yeoman Test
description: Cookiecutter example
spec:
owner: backstage/techdocs-core
type: service
parameters:
- title: Fill in some steps
required:
- name
- owner
properties:
name:
title: Name
type: string
description: Unique name of the component
ui:autofocus: true
ui:options:
rows: 5
owner:
title: Owner
type: string
description: Owner of the component
ui:field: OwnerPicker
ui:options:
allowedKinds:
- Group
system:
title: System
type: string
description: System of the component
ui:field: EntityPicker
ui:options:
allowedKinds:
- System
defaultKind: System
- title: Choose a location
required:
- repoUrl
- dryRun
properties:
repoUrl:
title: Repository Location
type: string
ui:field: RepoUrlPicker
ui:options:
allowedHosts:
- github.com
dryRun:
title: Only perform a dry run, don't publish anything
type: boolean
default: false
steps:
- id: yeoman
name: Yeoman
action: run:yeoman
input:
namespace: 'org:codeowners'
options:
codeowners: '@{{ parameters.owner }}'
- id: publish
if: '{{ not parameters.dryRun }}'
name: Publish
action: publish:github
input:
allowedHosts: ['github.com']
description: 'This is {{ parameters.name }}'
repoUrl: '{{ parameters.repoUrl }}'
- id: register
if: '{{ not parameters.dryRun }}'
name: Register
action: catalog:register
input:
repoContentsUrl: '{{ steps.publish.output.repoContentsUrl }}'
catalogInfoPath: '/catalog-info.yaml'
- name: Results
if: '{{ parameters.dryRun }}'
action: debug:log
input:
listWorkspace: true
output:
links:
- title: Repository
url: '{{ steps.publish.output.remoteUrl }}'
- title: Open in catalog
icon: 'catalog'
entityRef: '{{ steps.register.output.entityRef }}'
```
You can also visit the `/create/actions` route in your Backstage application to find out more about the parameters this action accepts when it's installed to configure how you like.
### Yeoman Generators setup
Yeoman generator should be installed a dependency of your `backstage/packages/backend` in `package.json`
```package.json
"generator-name": "^1.2.3"
```
Alternatively it can be installed globally in the environment using, e.g.: `npm install -g generator-name`.
@@ -0,0 +1,14 @@
## API Report File for "@backstage/plugin-scaffolder-backend-module-yeoman"
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { TemplateAction } from '@backstage/plugin-scaffolder-backend';
// Warning: (ae-missing-release-tag) "createRunYeomanAction" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
export function createRunYeomanAction(): TemplateAction<any>;
// (No @packageDocumentation comment for this package)
```
@@ -0,0 +1,35 @@
{
"name": "@backstage/plugin-scaffolder-backend-module-yeoman",
"version": "0.1.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
"private": false,
"publishConfig": {
"access": "public",
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts"
},
"scripts": {
"start": "backstage-cli backend:dev",
"build": "backstage-cli backend:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test",
"prepack": "backstage-cli prepack",
"postpack": "backstage-cli postpack",
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.8",
"@backstage/plugin-scaffolder-backend": "^0.15.2",
"winston": "^3.2.1",
"yeoman-environment": "^3.6.0"
},
"devDependencies": {
"@backstage/backend-common": "^0.9.2",
"@types/jest": "^26.0.7"
},
"files": [
"dist"
]
}
@@ -0,0 +1,16 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './run';
@@ -0,0 +1,16 @@
/*
* 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 { createRunYeomanAction } from './yeoman';
@@ -0,0 +1,70 @@
/*
* 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 { yeomanRun } from './yeomanRun';
jest.mock('./yeomanRun');
import { getVoidLogger } from '@backstage/backend-common';
import os from 'os';
import { PassThrough } from 'stream';
import { createRunYeomanAction } from './yeoman';
import type { ActionContext } from '@backstage/plugin-scaffolder-backend';
import { JsonObject } from '@backstage/config';
describe('run:yeoman', () => {
const mockTmpDir = os.tmpdir();
let mockContext: ActionContext<{
namespace: string;
args?: string[];
options?: JsonObject;
}>;
const action = createRunYeomanAction();
beforeEach(() => {
jest.resetAllMocks();
});
it('should call yeomanRun with the correct variables', async () => {
const namespace = 'whatever:app';
const args = ['aa', 'bb'];
const options = {
code: 'owner',
};
mockContext = {
input: {
namespace,
args,
options,
},
workspacePath: mockTmpDir,
logger: getVoidLogger(),
logStream: new PassThrough(),
output: jest.fn(),
createTemporaryDirectory: jest.fn().mockResolvedValue(mockTmpDir),
};
await action.handler(mockContext);
expect(yeomanRun).toHaveBeenCalledWith(
mockTmpDir,
namespace,
args,
options,
);
});
});
@@ -0,0 +1,67 @@
/*
* 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/config';
import { createTemplateAction } from '@backstage/plugin-scaffolder-backend';
import { yeomanRun } from './yeomanRun';
export function createRunYeomanAction() {
return createTemplateAction<{
namespace: string;
args?: string[];
options?: JsonObject;
}>({
id: 'run:yeoman',
description: 'Runs Yeoman on an installed Yeoman generator',
schema: {
input: {
type: 'object',
required: ['namespace'],
properties: {
namespace: {
title: 'Generator Namespace',
description: 'Yeoman generator namespace, e.g: node:app',
type: 'string',
},
args: {
title: 'Generator Arguments',
description: 'Arguments to pass on to Yeoman for templating',
type: 'array',
items: {
type: 'string',
},
},
options: {
title: 'Generator Options',
description: 'Options to pass on to Yeoman for templating',
type: 'object',
},
},
},
},
async handler(ctx) {
ctx.logger.info(
`Templating using Yeoman generator: ${ctx.input.namespace}`,
);
await yeomanRun(
ctx.workspacePath,
ctx.input.namespace,
ctx.input.args,
ctx.input.options,
);
},
});
}
@@ -0,0 +1,36 @@
/*
* 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/config';
/*
* This module should use '@types/yeoman-environment' eventually as soon as '@types/yeoman-environment' supports
* the latest version of Yeoman -> 3.x. Then it will be possible to test this module with jest.
*/
export async function yeomanRun(
workspace: string,
namespace: string,
args?: string[],
opts?: JsonObject,
) {
const yeoman = require('yeoman-environment');
const generator = yeoman.lookupGenerator(namespace);
const env = yeoman.createEnv(undefined, { cwd: workspace });
env.register(generator, namespace);
const yeomanArgs = [namespace, ...(args ?? [])];
await env.run(yeomanArgs, opts);
}
@@ -0,0 +1,16 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './actions';
@@ -23,7 +23,7 @@
"@backstage/backend-common": "^0.9.1",
"@backstage/search-common": "^0.2.0",
"@backstage/plugin-search-backend-node": "^0.4.2",
"lodash": "^4.17.15",
"lodash": "^4.17.21",
"knex": "^0.95.1"
},
"devDependencies": {
@@ -64,6 +64,10 @@ const useStyles = makeStyles<BackstageTheme>(theme => ({
marginLeft: '20rem',
maxWidth: 'calc(100% - 20rem * 2 - 3rem)',
marginTop: theme.spacing(1),
'@media screen and (max-width: 76.1875em)': {
marginLeft: '10rem',
maxWidth: 'calc(100% - 10rem)',
},
},
}));
+599 -15
View File
@@ -2847,6 +2847,11 @@
dependencies:
yaml-ast-parser "0.0.43"
"@gar/promisify@^1.0.1":
version "1.1.2"
resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz#30aa825f11d438671d585bd44e7fd564535fc210"
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
"@gitbeaker/core@^30.2.0", "@gitbeaker/core@^30.3.0":
version "30.3.0"
resolved "https://registry.npmjs.org/@gitbeaker/core/-/core-30.3.0.tgz#d005891d47cfacb41d4a3cc8bf3ee8c68c53d378"
@@ -4655,11 +4660,56 @@
"@nodelib/fs.scandir" "2.1.3"
fastq "^1.6.0"
"@npmcli/arborist@^2.2.2":
version "2.8.3"
resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-2.8.3.tgz#5569e7d2038f6893abc81f9c879f497b506e6980"
integrity sha512-miFcxbZjmQqeFTeRSLLh+lc/gxIKDO5L4PVCp+dp+kmcwJmYsEJmF7YvHR2yi3jF+fxgvLf3CCFzboPIXAuabg==
dependencies:
"@npmcli/installed-package-contents" "^1.0.7"
"@npmcli/map-workspaces" "^1.0.2"
"@npmcli/metavuln-calculator" "^1.1.0"
"@npmcli/move-file" "^1.1.0"
"@npmcli/name-from-folder" "^1.0.1"
"@npmcli/node-gyp" "^1.0.1"
"@npmcli/package-json" "^1.0.1"
"@npmcli/run-script" "^1.8.2"
bin-links "^2.2.1"
cacache "^15.0.3"
common-ancestor-path "^1.0.1"
json-parse-even-better-errors "^2.3.1"
json-stringify-nice "^1.1.4"
mkdirp "^1.0.4"
mkdirp-infer-owner "^2.0.0"
npm-install-checks "^4.0.0"
npm-package-arg "^8.1.5"
npm-pick-manifest "^6.1.0"
npm-registry-fetch "^11.0.0"
pacote "^11.3.5"
parse-conflict-json "^1.1.1"
proc-log "^1.0.0"
promise-all-reject-late "^1.0.0"
promise-call-limit "^1.0.1"
read-package-json-fast "^2.0.2"
readdir-scoped-modules "^1.1.0"
rimraf "^3.0.2"
semver "^7.3.5"
ssri "^8.0.1"
treeverse "^1.0.4"
walk-up-path "^1.0.0"
"@npmcli/ci-detect@^1.0.0":
version "1.3.0"
resolved "https://registry.npmjs.org/@npmcli/ci-detect/-/ci-detect-1.3.0.tgz#6c1d2c625fb6ef1b9dea85ad0a5afcbef85ef22a"
integrity sha512-oN3y7FAROHhrAt7Rr7PnTSwrHrZVRTS2ZbyxeQwSSYD0ifwM3YNgQqbaRmjcWoPyq77MjchusjJDspbzMmip1Q==
"@npmcli/fs@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-1.0.0.tgz#589612cfad3a6ea0feafcb901d29c63fd52db09f"
integrity sha512-8ltnOpRR/oJbOp8vaGUnipOi3bqkcW+sLHFlyXIr08OGHmVJLB1Hn7QtGXbYcpVtH1gAYZTlmDXtE4YV0+AMMQ==
dependencies:
"@gar/promisify" "^1.0.1"
semver "^7.3.5"
"@npmcli/git@^2.0.1":
version "2.0.6"
resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.0.6.tgz#47b97e96b2eede3f38379262fa3bdfa6eae57bf2"
@@ -4675,7 +4725,21 @@
unique-filename "^1.1.1"
which "^2.0.2"
"@npmcli/installed-package-contents@^1.0.6":
"@npmcli/git@^2.1.0":
version "2.1.0"
resolved "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz#2fbd77e147530247d37f325930d457b3ebe894f6"
integrity sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==
dependencies:
"@npmcli/promise-spawn" "^1.3.2"
lru-cache "^6.0.0"
mkdirp "^1.0.4"
npm-pick-manifest "^6.1.1"
promise-inflight "^1.0.1"
promise-retry "^2.0.1"
semver "^7.3.5"
which "^2.0.2"
"@npmcli/installed-package-contents@^1.0.6", "@npmcli/installed-package-contents@^1.0.7":
version "1.0.7"
resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz#ab7408c6147911b970a8abe261ce512232a3f4fa"
integrity sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==
@@ -4683,6 +4747,25 @@
npm-bundled "^1.1.1"
npm-normalize-package-bin "^1.0.1"
"@npmcli/map-workspaces@^1.0.2":
version "1.0.4"
resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-1.0.4.tgz#915708b55afa25e20bc2c14a766c124c2c5d4cab"
integrity sha512-wVR8QxhyXsFcD/cORtJwGQodeeaDf0OxcHie8ema4VgFeqwYkFsDPnSrIRSytX8xR6nKPAH89WnwTcaU608b/Q==
dependencies:
"@npmcli/name-from-folder" "^1.0.1"
glob "^7.1.6"
minimatch "^3.0.4"
read-package-json-fast "^2.0.1"
"@npmcli/metavuln-calculator@^1.1.0":
version "1.1.1"
resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-1.1.1.tgz#2f95ff3c6d88b366dd70de1c3f304267c631b458"
integrity sha512-9xe+ZZ1iGVaUovBVFI9h3qW+UuECUzhvZPxK9RaEA2mjU26o5D0JloGYWwLYvQELJNmBdQB6rrpuN8jni6LwzQ==
dependencies:
cacache "^15.0.5"
pacote "^11.1.11"
semver "^7.3.2"
"@npmcli/move-file@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.0.1.tgz#de103070dac0f48ce49cf6693c23af59c0f70464"
@@ -4690,11 +4773,31 @@
dependencies:
mkdirp "^1.0.4"
"@npmcli/node-gyp@^1.0.2":
"@npmcli/move-file@^1.1.0":
version "1.1.2"
resolved "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674"
integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==
dependencies:
mkdirp "^1.0.4"
rimraf "^3.0.2"
"@npmcli/name-from-folder@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz#77ecd0a4fcb772ba6fe927e2e2e155fbec2e6b1a"
integrity sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==
"@npmcli/node-gyp@^1.0.1", "@npmcli/node-gyp@^1.0.2":
version "1.0.2"
resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.2.tgz#3cdc1f30e9736dbc417373ed803b42b1a0a29ede"
integrity sha512-yrJUe6reVMpktcvagumoqD9r08fH1iRo01gn1u0zoCApa9lnZGEigVKUd2hzsCId4gdtkZZIVscLhNxMECKgRg==
"@npmcli/package-json@^1.0.1":
version "1.0.1"
resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz#1ed42f00febe5293c3502fd0ef785647355f6e89"
integrity sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg==
dependencies:
json-parse-even-better-errors "^2.3.1"
"@npmcli/promise-spawn@^1.1.0", "@npmcli/promise-spawn@^1.2.0", "@npmcli/promise-spawn@^1.3.2":
version "1.3.2"
resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz#42d4e56a8e9274fba180dabc0aea6e38f29274f5"
@@ -6552,6 +6655,11 @@
resolved "https://registry.npmjs.org/@types/estree/-/estree-0.0.50.tgz#1e0caa9364d3fccd2931c3ed96fdbeaa5d4cca83"
integrity sha512-C6N5s2ZFtuZRj54k2/zyRhNDjJwwcViAM3Nbm8zjBpbqAdZ00mr0CFxvSKeO8Y/e03WVFLpQMdHYVfUd6SB+Hw==
"@types/expect@^1.20.4":
version "1.20.4"
resolved "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz#8288e51737bf7e3ab5d7c77bfa695883745264e5"
integrity sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==
"@types/express-serve-static-core@*", "@types/express-serve-static-core@^4.17.18", "@types/express-serve-static-core@^4.17.21":
version "4.17.24"
resolved "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.24.tgz#ea41f93bf7e0d59cd5a76665068ed6aab6815c07"
@@ -6993,6 +7101,11 @@
resolved "https://registry.npmjs.org/@types/node/-/node-12.12.58.tgz#46dae9b2b9ee5992818c8f7cee01ff4ce03ab44c"
integrity sha512-Be46CNIHWAagEfINOjmriSxuv7IVcqbGe+sDSg2SYCEz/0CRBy7LRASGfRbD8KZkqoePU73Wsx3UvOSFcq/9hA==
"@types/node@^15.6.1":
version "15.14.9"
resolved "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz#bc43c990c3c9be7281868bbc7b8fdd6e2b57adfa"
integrity sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==
"@types/normalize-package-data@^2.4.0":
version "2.4.0"
resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e"
@@ -7507,6 +7620,14 @@
resolved "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.0.tgz#215c231dff736d5ba92410e6d602050cce7e273f"
integrity sha512-eQ9qFW/fhfGJF8WKHGEHZEyVWfZxrT+6CLIJGBcZPfxUh/+BnEj+UCGYMlr9qZuX/2AltsvwrGqp0LhEW8D0zQ==
"@types/vinyl@^2.0.4":
version "2.0.6"
resolved "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz#b2d134603557a7c3d2b5d3dc23863ea2b5eb29b0"
integrity sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==
dependencies:
"@types/expect" "^1.20.4"
"@types/node" "*"
"@types/webpack-dev-server@^3.11.5":
version "3.11.5"
resolved "https://registry.npmjs.org/@types/webpack-dev-server/-/webpack-dev-server-3.11.5.tgz#f4a254a3dd0667c8ee4af90d42afdb4ad1d607f3"
@@ -8137,6 +8258,13 @@ agent-base@6:
dependencies:
debug "4"
agent-base@^6.0.2:
version "6.0.2"
resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
agentkeepalive@^4.1.3, agentkeepalive@^4.1.4:
version "4.1.4"
resolved "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.1.4.tgz#d928028a4862cb11718e55227872e842a44c945b"
@@ -8593,6 +8721,14 @@ archiver@^5.0.2, archiver@^5.3.0:
tar-stream "^2.2.0"
zip-stream "^4.1.0"
are-we-there-yet@^1.1.5:
version "1.1.7"
resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz#b15474a932adab4ff8a50d9adfa7e4e926f21146"
integrity sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==
dependencies:
delegates "^1.0.0"
readable-stream "^2.0.6"
are-we-there-yet@~1.1.2:
version "1.1.5"
resolved "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21"
@@ -8840,6 +8976,11 @@ async-retry@^1.2.1, async-retry@^1.3.1:
dependencies:
retry "0.12.0"
async@0.9.x:
version "0.9.2"
resolved "https://registry.npmjs.org/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d"
integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=
async@^2.6.2:
version "2.6.3"
resolved "https://registry.npmjs.org/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@@ -9402,6 +9543,18 @@ bignumber.js@^9.0.0:
resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.1.tgz#8d7ba124c882bfd8e43260c67475518d0689e4e5"
integrity sha512-IdZR9mh6ahOBv/hYGiXyVuyCetmGJhtYkqLBpTStdhEGjegpPlUawydyaF3pbIOFynJTpllEs+NP+CS9jKFLjA==
bin-links@^2.2.1:
version "2.2.1"
resolved "https://registry.npmjs.org/bin-links/-/bin-links-2.2.1.tgz#347d9dbb48f7d60e6c11fe68b77a424bee14d61b"
integrity sha512-wFzVTqavpgCCYAh8SVBdnZdiQMxTkGR+T3b14CNpBXIBe2neJWaMGAZ55XWWHELJJ89dscuq0VCBqcVaIOgCMg==
dependencies:
cmd-shim "^4.0.1"
mkdirp "^1.0.3"
npm-normalize-package-bin "^1.0.0"
read-cmd-shim "^2.0.0"
rimraf "^3.0.0"
write-file-atomic "^3.0.3"
binary-extensions@^1.0.0:
version "1.13.1"
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
@@ -9425,6 +9578,11 @@ binary@~0.3.0:
buffers "~0.1.1"
chainsaw "~0.1.0"
binaryextensions@^4.15.0, binaryextensions@^4.16.0:
version "4.18.0"
resolved "https://registry.npmjs.org/binaryextensions/-/binaryextensions-4.18.0.tgz#22aeada2d14de062c60e8ca59a504a5636a76ceb"
integrity sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw==
bindings@^1.5.0:
version "1.5.0"
resolved "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df"
@@ -9821,6 +9979,30 @@ cacache@^12.0.2:
unique-filename "^1.1.1"
y18n "^4.0.0"
cacache@^15.0.3, cacache@^15.2.0:
version "15.3.0"
resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb"
integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==
dependencies:
"@npmcli/fs" "^1.0.0"
"@npmcli/move-file" "^1.0.1"
chownr "^2.0.0"
fs-minipass "^2.0.0"
glob "^7.1.4"
infer-owner "^1.0.4"
lru-cache "^6.0.0"
minipass "^3.1.1"
minipass-collect "^1.0.2"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.2"
mkdirp "^1.0.3"
p-map "^4.0.0"
promise-inflight "^1.0.1"
rimraf "^3.0.2"
ssri "^8.0.1"
tar "^6.0.2"
unique-filename "^1.1.1"
cacache@^15.0.5:
version "15.0.5"
resolved "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0"
@@ -10352,6 +10534,13 @@ cli-table3@0.6.0, cli-table3@~0.6.0:
optionalDependencies:
colors "^1.1.2"
cli-table@^0.3.1:
version "0.3.6"
resolved "https://registry.npmjs.org/cli-table/-/cli-table-0.3.6.tgz#e9d6aa859c7fe636981fd3787378c2a20bce92fc"
integrity sha512-ZkNZbnZjKERTY5NwC2SeMeLeifSPq/pubeRoTpdr3WchLlnZg6hEgvHkK5zL7KNFdd9PmHN8lxrENUwI3cE8vQ==
dependencies:
colors "1.0.3"
cli-truncate@2.1.0, cli-truncate@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
@@ -10400,6 +10589,11 @@ cliui@^7.0.2:
strip-ansi "^6.0.0"
wrap-ansi "^7.0.0"
clone-buffer@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58"
integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg=
clone-deep@^0.2.4:
version "0.2.4"
resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6"
@@ -10427,7 +10621,12 @@ clone-response@^1.0.2:
dependencies:
mimic-response "^1.0.0"
clone@2.x:
clone-stats@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680"
integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA=
clone@2.x, clone@^2.1.1:
version "2.1.2"
resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=
@@ -10437,12 +10636,21 @@ clone@^1.0.2:
resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
cloneable-readable@^1.0.0:
version "1.1.3"
resolved "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec"
integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==
dependencies:
inherits "^2.0.1"
process-nextick-args "^2.0.0"
readable-stream "^2.3.5"
clsx@^1.0.1, clsx@^1.0.2, clsx@^1.0.4, clsx@^1.1.0:
version "1.1.1"
resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
cmd-shim@^4.1.0:
cmd-shim@^4.0.1, cmd-shim@^4.1.0:
version "4.1.0"
resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-4.1.0.tgz#b3a904a6743e9fede4148c6f3800bf2a08135bdd"
integrity sha512-lb9L7EM4I/ZRVuljLPEtUJOP+xiQVknZ4ZMpMgEp4JzNldPb27HU03hi6K1/6CoIuit/Zm/LQXySErFeXxDprw==
@@ -10585,6 +10793,11 @@ colorette@^1.2.1, colorette@^1.2.2:
resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94"
integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w==
colors@1.0.3:
version "1.0.3"
resolved "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b"
integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs=
colors@^1.1.2, colors@^1.2.1:
version "1.4.0"
resolved "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
@@ -10628,6 +10841,11 @@ command-exists@^1.2.9:
resolved "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz#c50725af3808c8ab0260fd60b01fbfa25b954f69"
integrity sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==
commander@7.1.0:
version "7.1.0"
resolved "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz#f2eaecf131f10e36e07d894698226e36ae0eb5ff"
integrity sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==
commander@^2.19.0, commander@^2.20.0, commander@^2.20.3, commander@^2.7.1, commander@~2.20.3:
version "2.20.3"
resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -10653,6 +10871,11 @@ commander@^7.1.0, commander@^7.2.0:
resolved "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7"
integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==
common-ancestor-path@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz#4f7d2d1394d91b7abdf51871c62f71eadb0182a7"
integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==
common-tags@1.8.0, common-tags@^1.8.0:
version "1.8.0"
resolved "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz#8e3153e542d4a39e9b10554434afaaf98956a937"
@@ -11855,6 +12078,11 @@ dateformat@^3.0.0, dateformat@^3.0.3:
resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
integrity sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==
dateformat@^4.5.0:
version "4.5.1"
resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.5.1.tgz#c20e7a9ca77d147906b6dc2261a8be0a5bd2173c"
integrity sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q==
dayjs@^1.10.4:
version "1.10.4"
resolved "https://registry.npmjs.org/dayjs/-/dayjs-1.10.4.tgz#8e544a9b8683f61783f570980a8a80eaf54ab1e2"
@@ -12556,6 +12784,13 @@ ee-first@1.1.1:
resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
ejs@^3.1.6:
version "3.1.6"
resolved "https://registry.npmjs.org/ejs/-/ejs-3.1.6.tgz#5bfd0a0689743bb5268b3550cceeebbc1702822a"
integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw==
dependencies:
jake "^10.6.1"
elastic-builder@^2.16.0:
version "2.16.0"
resolved "https://registry.npmjs.org/elastic-builder/-/elastic-builder-2.16.0.tgz#684757ab9e6a4214653d23d84cec5ab8d185892f"
@@ -12753,6 +12988,11 @@ error-stack-parser@^2.0.6:
dependencies:
stackframe "^1.1.1"
error@^10.4.0:
version "10.4.0"
resolved "https://registry.npmjs.org/error/-/error-10.4.0.tgz#6fcf0fd64bceb1e750f8ed9a3dd880f00e46a487"
integrity sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw==
es-abstract@^1.17.0, es-abstract@^1.17.0-next.0, es-abstract@^1.17.0-next.1, es-abstract@^1.17.2, es-abstract@^1.17.4, es-abstract@^1.17.5, es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2:
version "1.18.0"
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4"
@@ -13792,6 +14032,13 @@ filefy@0.1.10:
resolved "https://registry.npmjs.org/filefy/-/filefy-0.1.10.tgz#174677c8e2fa5bc39a3af0ed6fb492f16b8fbf42"
integrity sha512-VgoRVOOY1WkTpWH+KBy8zcU1G7uQTVsXqhWEgzryB9A5hg2aqCyZ6aQ/5PSzlqM5+6cnVrX6oYV0XqD3HZSnmQ==
filelist@^1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/filelist/-/filelist-1.0.2.tgz#80202f21462d4d1c2e214119b1807c1bc0380e5b"
integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ==
dependencies:
minimatch "^3.0.4"
filesize@6.1.0:
version "6.1.0"
resolved "https://registry.npmjs.org/filesize/-/filesize-6.1.0.tgz#e81bdaa780e2451d714d71c0d7a4f3238d37ad00"
@@ -13910,6 +14157,13 @@ find-yarn-workspace-root2@1.2.16:
micromatch "^4.0.2"
pkg-dir "^4.2.0"
first-chunk-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz#1bdecdb8e083c0664b91945581577a43a9f31d70"
integrity sha1-G97NuOCDwGZLkZRVgVd6Q6nzHXA=
dependencies:
readable-stream "^2.0.2"
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
@@ -14951,6 +15205,11 @@ graphql@^15.3.0, graphql@^15.4.0:
resolved "https://registry.npmjs.org/graphql/-/graphql-15.5.1.tgz#f2f84415d8985e7b84731e7f3536f8bb9d383aad"
integrity sha512-FeTRX67T3LoE3LWAxxOlW2K3Bz+rMYAC18rRguK4wgXaTZMiJwSUwDmPFo3UadAKbzirKIg5Qy+sNJXbpPRnQw==
grouped-queue@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz#a2c6713f2171e45db2c300a3a9d7c119d694dac8"
integrity sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw==
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -15268,6 +15527,13 @@ hosted-git-info@^3.0.6:
dependencies:
lru-cache "^6.0.0"
hosted-git-info@^4.0.1:
version "4.0.2"
resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.0.2.tgz#5e425507eede4fea846b7262f0838456c4209961"
integrity sha512-c9OGXbZ3guC/xOlCg1Ci/VgWlwsqDv1yMQL1CWqXDL0hDjXuNcq0zuR4xqPSuasI3kqFDhqSyTjREz5gzq0fXg==
dependencies:
lru-cache "^6.0.0"
hpack.js@^2.1.6:
version "2.1.6"
resolved "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2"
@@ -15856,6 +16122,26 @@ inquirer@^7.0.4, inquirer@^7.3.3:
strip-ansi "^6.0.0"
through "^2.3.6"
inquirer@^8.0.0:
version "8.1.5"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.1.5.tgz#2dc5159203c826d654915b5fe6990fd17f54a150"
integrity sha512-G6/9xUqmt/r+UvufSyrPpt84NYwhKZ9jLsgMbQzlx804XErNupor8WQdBnBRrXmBfTPpuwf1sV+ss2ovjgdXIg==
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.2.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
through "^2.3.6"
inquirer@^8.1.0:
version "8.1.1"
resolved "https://registry.npmjs.org/inquirer/-/inquirer-8.1.1.tgz#7c53d94c6d03011c7bb2a947f0dca3b98246c26a"
@@ -16439,6 +16725,13 @@ is-root@2.1.0:
resolved "https://registry.npmjs.org/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
is-scoped@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz#fef0713772658bdf5bee418608267ddae6d3566d"
integrity sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==
dependencies:
scoped-regex "^2.0.0"
is-set@^2.0.1:
version "2.0.1"
resolved "https://registry.npmjs.org/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
@@ -16534,7 +16827,7 @@ is-upper-case@^2.0.2:
dependencies:
tslib "^2.0.3"
is-utf8@^0.2.0:
is-utf8@^0.2.0, is-utf8@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72"
integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI=
@@ -16634,9 +16927,9 @@ isomorphic-form-data@^2.0.0:
form-data "^2.3.2"
isomorphic-git@^1.8.0:
version "1.9.2"
resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.9.2.tgz#0e492dbcd9873070b2a57eef257a45b90020ed72"
integrity sha512-puCXcGgtkDXdMYLZlAEGbpkbmHn/Q4Lsl2uMFwMLOKmmr8Qe7Fe3+c6k2+aHW3rMdJYg9xTv95BJ+PRzR8Ydww==
version "1.10.0"
resolved "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.10.0.tgz#59a4604d1190d1e7fc52172085da25e6a428bc07"
integrity sha512-CijspEYaOQAnsHWXyq8ICZXzLJ/1wYQAa0jdfLcugA/68oNzrxykjGZz8Up7B8huA1VfkFHm4VviExtj/zpViw==
dependencies:
async-lock "^1.1.0"
clean-git-ref "^2.0.1"
@@ -16719,6 +17012,16 @@ iterate-value@^1.0.0:
es-get-iterator "^1.0.2"
iterate-iterator "^1.0.1"
jake@^10.6.1:
version "10.8.2"
resolved "https://registry.npmjs.org/jake/-/jake-10.8.2.tgz#ebc9de8558160a66d82d0eadc6a2e58fbc500a7b"
integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A==
dependencies:
async "0.9.x"
chalk "^2.4.2"
filelist "^1.0.1"
minimatch "^3.0.4"
jenkins@^0.28.1:
version "0.28.1"
resolved "https://registry.npmjs.org/jenkins/-/jenkins-0.28.1.tgz#f7951798ee5d2bb501a831979b6b5ecc1a922a64"
@@ -17399,7 +17702,7 @@ json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==
json-parse-even-better-errors@^2.3.0:
json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
@@ -17468,6 +17771,11 @@ json-stable-stringify@^1.0.1:
dependencies:
jsonify "~0.0.0"
json-stringify-nice@^1.1.4:
version "1.1.4"
resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz#2c937962b80181d3f317dd39aa323e14f5a60a67"
integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==
json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1:
version "5.0.1"
resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
@@ -17684,6 +17992,16 @@ junk@^3.1.0:
resolved "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1"
integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==
just-diff-apply@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-3.0.0.tgz#a77348d24f0694e378b57293dceb65bdf5a91c4f"
integrity sha512-K2MLc+ZC2DVxX4V61bIKPeMUUfj1YYZ3h0myhchDXOW1cKoPZMnjIoNCqv9bF2n5Oob1PFxuR2gVJxkxz4e58w==
just-diff@^3.0.1:
version "3.1.1"
resolved "https://registry.npmjs.org/just-diff/-/just-diff-3.1.1.tgz#d50c597c6fd4776495308c63bdee1b6839082647"
integrity sha512-sdMWKjRq8qWZEjDcVA6llnUT8RDEBIfOiGpYFPYa9u+2c39JCsejktSP7mj5eRid5EIvTzIpQ2kDOCw1Nq9BjQ==
just-extend@^4.0.2:
version "4.2.1"
resolved "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744"
@@ -18651,6 +18969,28 @@ make-fetch-happen@^8.0.9:
socks-proxy-agent "^5.0.0"
ssri "^8.0.0"
make-fetch-happen@^9.0.1:
version "9.1.0"
resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz#53085a09e7971433e6765f7971bf63f4e05cb968"
integrity sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==
dependencies:
agentkeepalive "^4.1.3"
cacache "^15.2.0"
http-cache-semantics "^4.1.0"
http-proxy-agent "^4.0.1"
https-proxy-agent "^5.0.0"
is-lambda "^1.0.1"
lru-cache "^6.0.0"
minipass "^3.1.3"
minipass-collect "^1.0.2"
minipass-fetch "^1.3.2"
minipass-flush "^1.0.5"
minipass-pipeline "^1.2.4"
negotiator "^0.6.2"
promise-retry "^2.0.1"
socks-proxy-agent "^6.0.0"
ssri "^8.0.0"
makeerror@1.0.x:
version "1.0.11"
resolved "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c"
@@ -18895,6 +19235,32 @@ media-typer@0.3.0:
resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
"mem-fs-editor@^8.1.2 || ^9.0.0":
version "9.3.0"
resolved "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-9.3.0.tgz#85ce80541b1961d1d9f433e275c7cee9d0a1c9a2"
integrity sha512-QKFbPwGCh1ypmc2H8BUYpbapwT/x2AOCYZQogzSui4rUNes7WVMagQXsirPIfp18EarX0SSY9Fpg426nSjew4Q==
dependencies:
binaryextensions "^4.16.0"
commondir "^1.0.1"
deep-extend "^0.6.0"
ejs "^3.1.6"
globby "^11.0.3"
isbinaryfile "^4.0.8"
minimatch "^3.0.4"
multimatch "^5.0.0"
normalize-path "^3.0.0"
textextensions "^5.13.0"
"mem-fs@^1.2.0 || ^2.0.0":
version "2.2.1"
resolved "https://registry.npmjs.org/mem-fs/-/mem-fs-2.2.1.tgz#c87bc8a53fb17971b129d4bcd59a9149fb78c5b1"
integrity sha512-yiAivd4xFOH/WXlUi6v/nKopBh1QLzwjFi36NK88cGt/PRXI8WeBASqY+YSjIVWvQTx3hR8zHKDBMV6hWmglNA==
dependencies:
"@types/node" "^15.6.1"
"@types/vinyl" "^2.0.4"
vinyl "^2.0.1"
vinyl-file "^3.0.0"
mem@^8.1.1:
version "8.1.1"
resolved "https://registry.npmjs.org/mem/-/mem-8.1.1.tgz#cf118b357c65ab7b7e0817bdf00c8062297c0122"
@@ -19650,7 +20016,7 @@ needle@^2.2.1, needle@^2.5.0:
iconv-lite "^0.4.4"
sax "^1.2.4"
negotiator@0.6.2:
negotiator@0.6.2, negotiator@^0.6.2:
version "0.6.2"
resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb"
integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==
@@ -20023,6 +20389,15 @@ npm-package-arg@^8.0.0, npm-package-arg@^8.0.1, npm-package-arg@^8.1.0:
semver "^7.0.0"
validate-npm-package-name "^3.0.0"
npm-package-arg@^8.1.2, npm-package-arg@^8.1.5:
version "8.1.5"
resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz#3369b2d5fe8fdc674baa7f1786514ddc15466e44"
integrity sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==
dependencies:
hosted-git-info "^4.0.1"
semver "^7.3.4"
validate-npm-package-name "^3.0.0"
npm-packlist@^1.1.6:
version "1.4.8"
resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e"
@@ -20051,6 +20426,28 @@ npm-pick-manifest@^6.0.0:
npm-package-arg "^8.0.0"
semver "^7.0.0"
npm-pick-manifest@^6.1.0, npm-pick-manifest@^6.1.1:
version "6.1.1"
resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz#7b5484ca2c908565f43b7f27644f36bb816f5148"
integrity sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==
dependencies:
npm-install-checks "^4.0.0"
npm-normalize-package-bin "^1.0.1"
npm-package-arg "^8.1.2"
semver "^7.3.4"
npm-registry-fetch@^11.0.0:
version "11.0.0"
resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-11.0.0.tgz#68c1bb810c46542760d62a6a965f85a702d43a76"
integrity sha512-jmlgSxoDNuhAtxUIG6pVwwtz840i994dL14FoNVZisrmZW5kWd63IUTNv1m/hyRSGSqWjCUp/YZlS1BJyNp9XA==
dependencies:
make-fetch-happen "^9.0.1"
minipass "^3.1.3"
minipass-fetch "^1.3.0"
minipass-json-stream "^1.0.1"
minizlib "^2.0.0"
npm-package-arg "^8.0.0"
npm-registry-fetch@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-9.0.0.tgz#86f3feb4ce00313bc0b8f1f8f69daae6face1661"
@@ -20368,7 +20765,7 @@ optionator@^0.9.1:
type-check "^0.4.0"
word-wrap "^1.2.3"
ora@^5.3.0:
ora@^5.3.0, ora@^5.4.1:
version "5.4.1"
resolved "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
@@ -20637,6 +21034,31 @@ packet-reader@1.0.0:
resolved "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz#9238e5480dedabacfe1fe3f2771063f164157d74"
integrity sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==
pacote@^11.1.11, pacote@^11.3.5:
version "11.3.5"
resolved "https://registry.npmjs.org/pacote/-/pacote-11.3.5.tgz#73cf1fc3772b533f575e39efa96c50be8c3dc9d2"
integrity sha512-fT375Yczn4zi+6Hkk2TBe1x1sP8FgFsEIZ2/iWaXY2r/NkhDJfxbcn5paz1+RTFCyNf+dPnaoBDJoAxXSU8Bkg==
dependencies:
"@npmcli/git" "^2.1.0"
"@npmcli/installed-package-contents" "^1.0.6"
"@npmcli/promise-spawn" "^1.2.0"
"@npmcli/run-script" "^1.8.2"
cacache "^15.0.5"
chownr "^2.0.0"
fs-minipass "^2.1.0"
infer-owner "^1.0.4"
minipass "^3.1.3"
mkdirp "^1.0.3"
npm-package-arg "^8.0.1"
npm-packlist "^2.1.4"
npm-pick-manifest "^6.0.0"
npm-registry-fetch "^11.0.0"
promise-retry "^2.0.1"
read-package-json-fast "^2.0.1"
rimraf "^3.0.2"
ssri "^8.0.1"
tar "^6.1.0"
pacote@^11.2.6:
version "11.2.6"
resolved "https://registry.npmjs.org/pacote/-/pacote-11.2.6.tgz#c0426e5d5c8d33aeea3461a75e1390f1ba78f953"
@@ -20708,6 +21130,15 @@ parse-asn1@^5.0.0:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-conflict-json@^1.1.1:
version "1.1.1"
resolved "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-1.1.1.tgz#54ec175bde0f2d70abf6be79e0e042290b86701b"
integrity sha512-4gySviBiW5TRl7XHvp1agcS7SOe0KZOjC//71dzZVWJrY9hCrgtvl5v3SyIxCZ4fZF47TxD9nfzmxcx76xmbUw==
dependencies:
json-parse-even-better-errors "^2.3.0"
just-diff "^3.0.1"
just-diff-apply "^3.0.0"
parse-entities@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/parse-entities/-/parse-entities-2.0.0.tgz#53c6eb5b9314a1f4ec99fa0fdf7ce01ecda0cbe8"
@@ -21815,6 +22246,16 @@ preferred-pm@^3.0.0:
path-exists "^4.0.0"
which-pm "2.0.0"
preferred-pm@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz#1b6338000371e3edbce52ef2e4f65eb2e73586d6"
integrity sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==
dependencies:
find-up "^5.0.0"
find-yarn-workspace-root2 "1.2.16"
path-exists "^4.0.0"
which-pm "2.0.0"
prelude-ls@^1.2.1:
version "1.2.1"
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
@@ -21845,7 +22286,7 @@ prettier@~2.2.1:
resolved "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz#795a1a78dd52f073da0cd42b21f9c91381923ff5"
integrity sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==
pretty-bytes@^5.6.0:
pretty-bytes@^5.3.0, pretty-bytes@^5.6.0:
version "5.6.0"
resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz#356256f643804773c82f64723fe78c92c62beaeb"
integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==
@@ -21898,7 +22339,12 @@ private@^0.1.8:
resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
process-nextick-args@~2.0.0:
proc-log@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz#0d927307401f69ed79341e83a0b2c9a13395eb77"
integrity sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg==
process-nextick-args@^2.0.0, process-nextick-args@~2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
@@ -21920,6 +22366,16 @@ prom-client@^13.2.0:
dependencies:
tdigest "^0.1.1"
promise-all-reject-late@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz#f8ebf13483e5ca91ad809ccc2fcf25f26f8643c2"
integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==
promise-call-limit@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz#4bdee03aeb85674385ca934da7114e9bcd3c6e24"
integrity sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q==
promise-inflight@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
@@ -22837,6 +23293,14 @@ read-package-json-fast@^2.0.1:
json-parse-even-better-errors "^2.3.0"
npm-normalize-package-bin "^1.0.1"
read-package-json-fast@^2.0.2:
version "2.0.3"
resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz#323ca529630da82cb34b36cc0b996693c98c2b83"
integrity sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==
dependencies:
json-parse-even-better-errors "^2.3.0"
npm-normalize-package-bin "^1.0.1"
read-package-json@^2.0.0:
version "2.1.1"
resolved "https://registry.npmjs.org/read-package-json/-/read-package-json-2.1.1.tgz#16aa66c59e7d4dad6288f179dd9295fd59bb98f1"
@@ -22991,7 +23455,7 @@ readdir-glob@^1.0.0:
dependencies:
minimatch "^3.0.4"
readdir-scoped-modules@^1.0.0:
readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
integrity sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==
@@ -23396,6 +23860,11 @@ replace-ext@1.0.0:
resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb"
integrity sha1-3mMSg3P8v3w8z6TeWkgMRaZ5WOs=
replace-ext@^1.0.0:
version "1.0.1"
resolved "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a"
integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==
replace-in-file@^6.0.0:
version "6.1.0"
resolved "https://registry.npmjs.org/replace-in-file/-/replace-in-file-6.1.0.tgz#9f9ddd7bb70d6ad231d2ad692e1b646e73d06647"
@@ -23806,7 +24275,7 @@ rxjs@^6.6.6:
dependencies:
tslib "^1.9.0"
rxjs@^7.1.0:
rxjs@^7.1.0, rxjs@^7.2.0:
version "7.3.0"
resolved "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz#39fe4f3461dc1e50be1475b2b85a0a88c1e938c6"
integrity sha512-p2yuGIg9S1epc3vrjKf6iVb3RCaAYjYskkO+jHIaV0IjOPlJop4UnodOoFb2xeNwlguqLYvGw1b1McillYb5Gw==
@@ -23942,6 +24411,11 @@ schema-utils@^3.1.0:
ajv "^6.12.5"
ajv-keywords "^3.5.2"
scoped-regex@^2.0.0:
version "2.1.0"
resolved "https://registry.npmjs.org/scoped-regex/-/scoped-regex-2.1.0.tgz#7b9be845d81fd9d21d1ec97c61a0b7cf86d2015f"
integrity sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==
screenfull@^5.0.0, screenfull@^5.1.0:
version "5.1.0"
resolved "https://registry.npmjs.org/screenfull/-/screenfull-5.1.0.tgz#85c13c70f4ead4c1b8a935c70010dfdcd2c0e5c8"
@@ -24421,6 +24895,15 @@ socks-proxy-agent@^5.0.0:
debug "4"
socks "^2.3.3"
socks-proxy-agent@^6.0.0:
version "6.1.0"
resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.1.0.tgz#869cf2d7bd10fea96c7ad3111e81726855e285c3"
integrity sha512-57e7lwCN4Tzt3mXz25VxOErJKXlPfXmkMLnk310v/jwW20jWRVcgsOit+xNkN3eIEdB47GwnfAEBLacZ/wVIKg==
dependencies:
agent-base "^6.0.2"
debug "^4.3.1"
socks "^2.6.1"
socks@^2.3.3:
version "2.5.1"
resolved "https://registry.npmjs.org/socks/-/socks-2.5.1.tgz#7720640b6b5ec9a07d556419203baa3f0596df5f"
@@ -24429,6 +24912,14 @@ socks@^2.3.3:
ip "^1.1.5"
smart-buffer "^4.1.0"
socks@^2.6.1:
version "2.6.1"
resolved "https://registry.npmjs.org/socks/-/socks-2.6.1.tgz#989e6534a07cf337deb1b1c94aaa44296520d30e"
integrity sha512-kLQ9N5ucj8uIcxrDwjm0Jsqk06xdpBjGNQtpXy4Q8/QY2k+fY7nZH8CARy+hkbG+SGAovmzzuauCpBlb8FrnBA==
dependencies:
ip "^1.1.5"
smart-buffer "^4.1.0"
sonic-boom@^0.7.5:
version "0.7.7"
resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-0.7.7.tgz#d921de887428208bfa07b0ae32c278de043f350a"
@@ -25069,6 +25560,21 @@ strip-ansi@^7.0.0:
dependencies:
ansi-regex "^6.0.1"
strip-bom-buf@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz#1cb45aaf57530f4caf86c7f75179d2c9a51dd572"
integrity sha1-HLRar1dTD0yvhsf3UXnSyaUd1XI=
dependencies:
is-utf8 "^0.2.1"
strip-bom-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz#f87db5ef2613f6968aa545abfe1ec728b6a829ca"
integrity sha1-+H217yYT9paKpUWr/h7HKLaoKco=
dependencies:
first-chunk-stream "^2.0.0"
strip-bom "^2.0.0"
strip-bom@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e"
@@ -25723,6 +26229,11 @@ text-table@0.2.0, text-table@^0.2.0:
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
textextensions@^5.12.0, textextensions@^5.13.0:
version "5.14.0"
resolved "https://registry.npmjs.org/textextensions/-/textextensions-5.14.0.tgz#a6ff6aee5faaa751e6157d422c722a2bfd59eedf"
integrity sha512-4cAYwNFNYlIAHBUo7p6zw8POUvWbZor+/R0Tanv+rIhsauEyV9QSrEXL40pI+GfTQxKX8k6Tyw6CmdSDSmASrg==
thenify-all@^1.0.0:
version "1.6.0"
resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
@@ -25992,6 +26503,11 @@ tree-kill@^1.2.2:
resolved "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
treeverse@^1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz#a6b0ebf98a1bca6846ddc7ecbc900df08cb9cd5f"
integrity sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g==
trim-newlines@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
@@ -26971,6 +27487,29 @@ vfile@^4.0.0:
unist-util-stringify-position "^2.0.0"
vfile-message "^2.0.0"
vinyl-file@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz#b104d9e4409ffa325faadd520642d0a3b488b365"
integrity sha1-sQTZ5ECf+jJfqt1SBkLQo7SIs2U=
dependencies:
graceful-fs "^4.1.2"
pify "^2.3.0"
strip-bom-buf "^1.0.0"
strip-bom-stream "^2.0.0"
vinyl "^2.0.1"
vinyl@^2.0.1:
version "2.2.1"
resolved "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz#23cfb8bbab5ece3803aa2c0a1eb28af7cbba1974"
integrity sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==
dependencies:
clone "^2.1.1"
clone-buffer "^1.0.0"
clone-stats "^1.0.0"
cloneable-readable "^1.0.0"
remove-trailing-separator "^1.0.1"
replace-ext "^1.0.0"
vm-browserify@^1.0.1:
version "1.1.2"
resolved "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0"
@@ -27011,6 +27550,11 @@ wait-on@6.0.0:
minimist "^1.2.5"
rxjs "^7.1.0"
walk-up-path@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz#d4745e893dd5fd0dbb58dd0a4c6a33d9c9fec53e"
integrity sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==
walker@^1.0.7, walker@~1.0.5:
version "1.0.7"
resolved "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"
@@ -27857,6 +28401,46 @@ yauzl@^2.10.0:
buffer-crc32 "~0.2.3"
fd-slicer "~1.1.0"
yeoman-environment@^3.6.0:
version "3.6.0"
resolved "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-3.6.0.tgz#91267ced0d474c4dec330b83f232e72e796c0730"
integrity sha512-X16N9lhzRdUKFT8MZrpwjLDKsdgAUqh4VPR2wAXeAqjJJaUxYBxCQGFxtZVTf3vbyNuIHXPunwOLtK60bpapbg==
dependencies:
"@npmcli/arborist" "^2.2.2"
are-we-there-yet "^1.1.5"
arrify "^2.0.1"
binaryextensions "^4.15.0"
chalk "^4.1.0"
cli-table "^0.3.1"
commander "7.1.0"
dateformat "^4.5.0"
debug "^4.1.1"
diff "^5.0.0"
error "^10.4.0"
escape-string-regexp "^4.0.0"
execa "^5.0.0"
find-up "^5.0.0"
globby "^11.0.1"
grouped-queue "^2.0.0"
inquirer "^8.0.0"
is-scoped "^2.1.0"
lodash "^4.17.10"
log-symbols "^4.0.0"
mem-fs "^1.2.0 || ^2.0.0"
mem-fs-editor "^8.1.2 || ^9.0.0"
minimatch "^3.0.4"
npmlog "^4.1.2"
p-queue "^6.6.2"
pacote "^11.2.6"
preferred-pm "^3.0.3"
pretty-bytes "^5.3.0"
semver "^7.1.3"
slash "^3.0.0"
strip-ansi "^6.0.0"
text-table "^0.2.0"
textextensions "^5.12.0"
untildify "^4.0.0"
yml-loader@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/yml-loader/-/yml-loader-2.1.0.tgz#b976b8691b537b6d3dc7d92a9a7d34b90de10870"