Techdocs: Updates preparers to make it work in actual usecases (#1957)
* feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * feature(techdocs): JIT generation of techdocs * Fix linting issues * Add Techdocs tab to entity page * Added missing dep * Added missing dep * Removed duplicate dep * Better tab navigation * Update label on entity page to say docs * Fix lint issue * Move building of docs from entity to a function * attempt to add back react as a dep * Fixed failing test * fix(techdocs): Hopefully fixed tests * Fixed another failing test * Initial apiRef for techdocs storage * Cleaned up some code in reader * test(headertabs): add tests to HeaderTabs component * fix(headertabs): change tab mock id * fix(techdocs-generator): fix problem with macOS symlink to tmp folder * fix(lint): remove unused configApiRef * wip * Ongoing cleanups * Cleanups and fix tests * Initial github preparer. Doesn't work properly yet * Updated dir preparer to handle github managed-by-location and added github preparer * Removed old file * Fixed feedback and added better logging to techdocs build process * Updated create-app template Co-authored-by: Fredrik Adelöw <freben@gmail.com> Co-authored-by: Emma Indal <emmai@spotify.com>
This commit is contained in:
committed by
GitHub
parent
f42bdf2490
commit
613c06bdd6
@@ -20,22 +20,27 @@ import {
|
||||
Preparers,
|
||||
Generators,
|
||||
LocalPublish,
|
||||
TechdocsGenerator
|
||||
TechdocsGenerator,
|
||||
GithubPreparer,
|
||||
} from '@backstage/plugin-techdocs-backend';
|
||||
import { PluginEnvironment } from '../types';
|
||||
import Docker from 'dockerode';
|
||||
|
||||
export default async function createPlugin({ logger, config }: PluginEnvironment) {
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
const generators = new Generators();
|
||||
const techdocsGenerator = new TechdocsGenerator();
|
||||
const techdocsGenerator = new TechdocsGenerator(logger);
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
|
||||
const directoryPreparer = new DirectoryPreparer();
|
||||
const preparers = new Preparers();
|
||||
|
||||
const githubPreparer = new GithubPreparer(logger);
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
preparers.register('dir', directoryPreparer);
|
||||
preparers.register('github', githubPreparer);
|
||||
|
||||
const publisher = new LocalPublish();
|
||||
const publisher = new LocalPublish(logger);
|
||||
|
||||
const dockerClient = new Docker();
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@ export default async function createPlugin({
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
const generators = new Generators();
|
||||
const techdocsGenerator = new TechdocsGenerator();
|
||||
const techdocsGenerator = new TechdocsGenerator(logger);
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
|
||||
const directoryPreparer = new DirectoryPreparer();
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
const preparers = new Preparers();
|
||||
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
const publisher = new LocalPublish();
|
||||
const publisher = new LocalPublish(logger);
|
||||
|
||||
const dockerClient = new Docker();
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ metadata:
|
||||
name: documented-component
|
||||
description: A Service with TechDocs documentation
|
||||
annotations:
|
||||
backstage.io/techdocs-ref: 'dir:./'
|
||||
backstage.io/techdocs-ref: 'github:https://github.com/spotify/backstage/blob/master/plugins/techdocs-backend/examples/documented-component'
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
|
||||
@@ -30,8 +30,10 @@
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^3.0.3",
|
||||
"fs-extra": "^9.0.1",
|
||||
"git-url-parse": "^11.1.3",
|
||||
"knex": "^0.21.1",
|
||||
"node-fetch": "^2.6.0",
|
||||
"nodegit": "^0.27.0",
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -24,12 +24,13 @@ import { createRouter } from './router';
|
||||
|
||||
describe('createRouter', () => {
|
||||
let app: express.Express;
|
||||
const logger = getVoidLogger();
|
||||
|
||||
beforeAll(async () => {
|
||||
const router = await createRouter({
|
||||
preparers: new Preparers(),
|
||||
generators: new Generators(),
|
||||
publisher: new LocalPublish(),
|
||||
publisher: new LocalPublish(logger),
|
||||
logger: getVoidLogger(),
|
||||
dockerClient: new Docker(),
|
||||
config: ConfigReader.fromConfigs([]),
|
||||
|
||||
@@ -45,18 +45,34 @@ export async function createRouter({
|
||||
publisher,
|
||||
config,
|
||||
dockerClient,
|
||||
logger,
|
||||
}: RouterOptions): Promise<express.Router> {
|
||||
const router = Router();
|
||||
|
||||
const getEntityId = (entity: Entity) => {
|
||||
return `${entity.kind}:${entity.metadata.namespace ?? ''}:${
|
||||
entity.metadata.name
|
||||
}`;
|
||||
};
|
||||
|
||||
const buildDocsForEntity = async (entity: Entity) => {
|
||||
const preparer = preparers.get(entity);
|
||||
const generator = generators.get(entity);
|
||||
|
||||
logger.info(`[TechDocs] Running preparer on entity ${getEntityId(entity)}`);
|
||||
const preparedDir = await preparer.prepare(entity);
|
||||
|
||||
logger.info(
|
||||
`[TechDocs] Running generator on entity ${getEntityId(entity)}`,
|
||||
);
|
||||
const { resultDir } = await generator.run({
|
||||
directory: await preparer.prepare(entity),
|
||||
directory: preparedDir,
|
||||
dockerClient,
|
||||
});
|
||||
|
||||
logger.info(
|
||||
`[TechDocs] Running publisher on entity ${getEntityId(entity)}`,
|
||||
);
|
||||
await publisher.publish({
|
||||
entity,
|
||||
directory: resultDir,
|
||||
|
||||
@@ -41,14 +41,14 @@ export async function startStandaloneServer(
|
||||
|
||||
logger.debug('Creating application...');
|
||||
const preparers = new Preparers();
|
||||
const directoryPreparer = new DirectoryPreparer();
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
preparers.register('dir', directoryPreparer);
|
||||
|
||||
const generators = new Generators();
|
||||
const techdocsGenerator = new TechdocsGenerator();
|
||||
const techdocsGenerator = new TechdocsGenerator(logger);
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
|
||||
const publisher = new LocalPublish();
|
||||
const publisher = new LocalPublish(logger);
|
||||
|
||||
const dockerClient = new Docker();
|
||||
|
||||
|
||||
@@ -13,17 +13,26 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
import {
|
||||
GeneratorBase,
|
||||
GeneratorRunOptions,
|
||||
GeneratorRunResult,
|
||||
} from './types';
|
||||
import { runDockerContainer } from './helpers';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
export class TechdocsGenerator implements GeneratorBase {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public async run({
|
||||
directory,
|
||||
logStream,
|
||||
@@ -36,18 +45,23 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
path.join(tmpdirResolvedPath, 'techdocs-tmp-'),
|
||||
);
|
||||
|
||||
await runDockerContainer({
|
||||
imageName: 'spotify/techdocs',
|
||||
args: ['build', '-d', '/result'],
|
||||
logStream,
|
||||
docsDir: directory,
|
||||
resultDir,
|
||||
dockerClient,
|
||||
});
|
||||
|
||||
console.log(
|
||||
`[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`,
|
||||
);
|
||||
try {
|
||||
await runDockerContainer({
|
||||
imageName: 'spotify/techdocs',
|
||||
args: ['build', '-d', '/result'],
|
||||
logStream,
|
||||
docsDir: directory,
|
||||
resultDir,
|
||||
dockerClient,
|
||||
});
|
||||
this.logger.info(
|
||||
`[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`,
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.debug(
|
||||
`[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`,
|
||||
);
|
||||
}
|
||||
|
||||
return { resultDir };
|
||||
}
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DirectoryPreparer } from './dir';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
const createMockEntity = (annotations: {}) => {
|
||||
return {
|
||||
@@ -30,7 +33,7 @@ const createMockEntity = (annotations: {}) => {
|
||||
|
||||
describe('directory preparer', () => {
|
||||
it('should merge managed-by-location and techdocs-ref when techdocs-ref is relative', async () => {
|
||||
const directoryPreparer = new DirectoryPreparer();
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/managed-by-location':
|
||||
@@ -44,7 +47,7 @@ describe('directory preparer', () => {
|
||||
});
|
||||
|
||||
it('should merge managed-by-location and techdocs-ref when techdocs-ref is absolute', async () => {
|
||||
const directoryPreparer = new DirectoryPreparer();
|
||||
const directoryPreparer = new DirectoryPreparer(logger);
|
||||
|
||||
const mockEntity = createMockEntity({
|
||||
'backstage.io/managed-by-location':
|
||||
|
||||
@@ -13,26 +13,95 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import fs from 'fs-extra';
|
||||
import os from 'os';
|
||||
import { PreparerBase } from './types';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import path from 'path';
|
||||
import { parseReferenceAnnotation } from './helpers';
|
||||
import { InputError } from '@backstage/backend-common';
|
||||
import { Clone } from 'nodegit';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
export class DirectoryPreparer implements PreparerBase {
|
||||
prepare(entity: Entity): Promise<string> {
|
||||
const { location: managedByLocation } = parseReferenceAnnotation(
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private async cloneGithubRepo(entity: Entity) {
|
||||
const { type, target } = parseReferenceAnnotation(
|
||||
'backstage.io/managed-by-location',
|
||||
entity,
|
||||
);
|
||||
const { location: techdocsLocation } = parseReferenceAnnotation(
|
||||
|
||||
if (type !== 'github') {
|
||||
throw new InputError(`Wrong target type: ${type}, should be 'github'`);
|
||||
}
|
||||
|
||||
const parsedGitLocation = parseGitUrl(target);
|
||||
const repositoryTmpPath = path.join(
|
||||
os.tmpdir(),
|
||||
'backstage-repo',
|
||||
parsedGitLocation.source,
|
||||
parsedGitLocation.owner,
|
||||
parsedGitLocation.name,
|
||||
parsedGitLocation.ref,
|
||||
);
|
||||
if (fs.existsSync(repositoryTmpPath)) {
|
||||
return repositoryTmpPath;
|
||||
}
|
||||
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
|
||||
|
||||
this.logger.debug(
|
||||
`[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`,
|
||||
);
|
||||
|
||||
fs.mkdirSync(repositoryTmpPath, { recursive: true });
|
||||
await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {});
|
||||
|
||||
return repositoryTmpPath;
|
||||
}
|
||||
|
||||
private async resolveManagedByLocationToDir(entity: Entity) {
|
||||
const { type, target } = parseReferenceAnnotation(
|
||||
'backstage.io/managed-by-location',
|
||||
entity,
|
||||
);
|
||||
|
||||
this.logger.debug(
|
||||
`[TechDocs] Building docs for entity with type 'dir' and managed-by-location '${type}'`,
|
||||
);
|
||||
switch (type) {
|
||||
case 'github': {
|
||||
const parsedGitLocation = parseGitUrl(target);
|
||||
const repoLocation = await this.cloneGithubRepo(entity);
|
||||
|
||||
return path.dirname(
|
||||
path.join(repoLocation, parsedGitLocation.filepath),
|
||||
);
|
||||
}
|
||||
case 'file':
|
||||
return path.dirname(target);
|
||||
default:
|
||||
throw new InputError(`Unable to resolve location type ${type}`);
|
||||
}
|
||||
}
|
||||
|
||||
async prepare(entity: Entity): Promise<string> {
|
||||
const { target } = parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
entity,
|
||||
);
|
||||
|
||||
const managedByLocationDirectory = path.dirname(managedByLocation);
|
||||
const managedByLocationDirectory = await this.resolveManagedByLocationToDir(
|
||||
entity,
|
||||
);
|
||||
|
||||
return new Promise(resolve => {
|
||||
resolve(path.resolve(managedByLocationDirectory, techdocsLocation));
|
||||
resolve(path.resolve(managedByLocationDirectory, target));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { InputError } from '@backstage/backend-common';
|
||||
import { PreparerBase } from './types';
|
||||
import parseGitUrl from 'git-url-parse';
|
||||
import { Clone } from 'nodegit';
|
||||
import { parseReferenceAnnotation } from './helpers';
|
||||
import { Logger } from 'winston';
|
||||
|
||||
export class GithubPreparer implements PreparerBase {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
async prepare(entity: Entity): Promise<string> {
|
||||
const { type, target } = parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
entity,
|
||||
);
|
||||
|
||||
if (type !== 'github') {
|
||||
throw new InputError(`Wrong target type: ${type}, should be 'github'`);
|
||||
}
|
||||
|
||||
const parsedGitLocation = parseGitUrl(target);
|
||||
const repositoryTmpPath = path.join(
|
||||
os.tmpdir(),
|
||||
'backstage-repo',
|
||||
parsedGitLocation.source,
|
||||
parsedGitLocation.owner,
|
||||
parsedGitLocation.name,
|
||||
parsedGitLocation.ref,
|
||||
);
|
||||
|
||||
if (fs.existsSync(repositoryTmpPath)) {
|
||||
this.logger.debug(
|
||||
`[TechDocs] Found repository already checked out at ${repositoryTmpPath}`,
|
||||
);
|
||||
return path.join(repositoryTmpPath, parsedGitLocation.filepath);
|
||||
}
|
||||
const repositoryCheckoutUrl = parsedGitLocation.toString('https');
|
||||
|
||||
this.logger.debug(
|
||||
`[TechDocs] Checking out repository ${repositoryCheckoutUrl} to ${repositoryTmpPath}`,
|
||||
);
|
||||
|
||||
fs.mkdirSync(repositoryTmpPath, { recursive: true });
|
||||
await Clone.clone(repositoryCheckoutUrl, repositoryTmpPath, {});
|
||||
|
||||
return path.join(repositoryTmpPath, parsedGitLocation.filepath);
|
||||
}
|
||||
}
|
||||
@@ -18,8 +18,8 @@ import { InputError } from '@backstage/backend-common';
|
||||
import { RemoteProtocol } from './types';
|
||||
|
||||
export type ParsedLocationAnnotation = {
|
||||
protocol: RemoteProtocol;
|
||||
location: string;
|
||||
type: RemoteProtocol;
|
||||
target: string;
|
||||
};
|
||||
|
||||
export const parseReferenceAnnotation = (
|
||||
@@ -36,19 +36,19 @@ export const parseReferenceAnnotation = (
|
||||
|
||||
// split on the first colon for the protocol and the rest after the first split
|
||||
// is the location.
|
||||
const [protocol, location] = annotation.split(/:(.+)/) as [
|
||||
const [type, target] = annotation.split(/:(.+)/) as [
|
||||
RemoteProtocol?,
|
||||
string?,
|
||||
];
|
||||
|
||||
if (!protocol || !location) {
|
||||
if (!type || !target) {
|
||||
throw new InputError(
|
||||
`Failure to parse either protocol or location for entity: ${entity.metadata.name}`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
protocol,
|
||||
location,
|
||||
type,
|
||||
target,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -14,5 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { DirectoryPreparer } from './dir';
|
||||
export { GithubPreparer } from './github';
|
||||
export { Preparers } from './preparers';
|
||||
export type { PreparerBuilder } from './types';
|
||||
|
||||
@@ -26,13 +26,16 @@ export class Preparers implements PreparerBuilder {
|
||||
}
|
||||
|
||||
get(entity: Entity): PreparerBase {
|
||||
const { protocol } = parseReferenceAnnotation('backstage.io/techdocs-ref', entity);
|
||||
const preparer = this.preparerMap.get(protocol);
|
||||
const { type } = parseReferenceAnnotation(
|
||||
'backstage.io/techdocs-ref',
|
||||
entity,
|
||||
);
|
||||
const preparer = this.preparerMap.get(type);
|
||||
|
||||
if (!preparer) {
|
||||
throw new Error(`No preparer registered for type: "${protocol}"`);
|
||||
throw new Error(`No preparer registered for type: "${type}"`);
|
||||
}
|
||||
|
||||
return preparer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { LocalPublish } from './local';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { LocalPublish } from './local';
|
||||
|
||||
const createMockEntity = (annotations = {}) => {
|
||||
return {
|
||||
@@ -30,9 +31,11 @@ const createMockEntity = (annotations = {}) => {
|
||||
};
|
||||
};
|
||||
|
||||
const logger = getVoidLogger();
|
||||
|
||||
describe('local publisher', () => {
|
||||
it('should publish generated documentation dir', async () => {
|
||||
const publisher = new LocalPublish();
|
||||
const publisher = new LocalPublish(logger);
|
||||
|
||||
const mockEntity = createMockEntity();
|
||||
|
||||
|
||||
@@ -13,12 +13,19 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { PublisherBase } from './types';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import path from 'path';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import { Logger } from 'winston';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { PublisherBase } from './types';
|
||||
|
||||
export class LocalPublish implements PublisherBase {
|
||||
private readonly logger: Logger;
|
||||
|
||||
constructor(logger: Logger) {
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
publish({
|
||||
entity,
|
||||
directory,
|
||||
@@ -37,16 +44,22 @@ export class LocalPublish implements PublisherBase {
|
||||
'../../../../static/docs/',
|
||||
entity.kind,
|
||||
entityNamespace,
|
||||
entity.metadata.name
|
||||
entity.metadata.name,
|
||||
);
|
||||
|
||||
if (!fs.existsSync(publishDir)) {
|
||||
this.logger.info(
|
||||
`[TechDocs]: Could not find ${publishDir}, creates the directory.`,
|
||||
);
|
||||
fs.mkdirSync(publishDir, { recursive: true });
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
fs.copy(directory, publishDir, err => {
|
||||
if (err) {
|
||||
this.logger.debug(
|
||||
`[TechDocs]: Failed to copy docs from ${directory} to ${publishDir}`,
|
||||
);
|
||||
reject(err);
|
||||
}
|
||||
|
||||
|
||||
@@ -3419,6 +3419,11 @@
|
||||
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
||||
|
||||
"@sindresorhus/is@^2.0.0":
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-2.1.1.tgz#ceff6a28a5b4867c2dd4a1ba513de278ccbe8bb1"
|
||||
integrity sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==
|
||||
|
||||
"@sinonjs/commons@^1.7.0":
|
||||
version "1.7.1"
|
||||
resolved "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.7.1.tgz#da5fd19a5f71177a53778073978873964f49acf1"
|
||||
@@ -4302,6 +4307,13 @@
|
||||
dependencies:
|
||||
defer-to-connect "^1.0.1"
|
||||
|
||||
"@szmarczak/http-timer@^4.0.0":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.5.tgz#bfbd50211e9dfa51ba07da58a14cdfd333205152"
|
||||
integrity sha512-PyRA9sm1Yayuj5OIoJ1hGt2YISX45w9WcFbh6ddT0Z/0yaFxOtGLInr4jUfU1EAFVs0Yfyfev4RNwBlUaHdlDQ==
|
||||
dependencies:
|
||||
defer-to-connect "^2.0.0"
|
||||
|
||||
"@testing-library/cypress@^6.0.0":
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmjs.org/@testing-library/cypress/-/cypress-6.0.0.tgz#935f7716e0e495f02fd753a42621e4d350097dce"
|
||||
@@ -4467,6 +4479,16 @@
|
||||
"@types/connect" "*"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.1.tgz#5d22f3dded1fd3a84c0bbeb5039a7419c2c91976"
|
||||
integrity sha512-ykFq2zmBGOCbpIXtoVbz4SKY5QriWPh3AjyU4G74RYbtt5yOc5OfaY75ftjg7mikMOla1CTGpX3lLbuJh8DTrQ==
|
||||
dependencies:
|
||||
"@types/http-cache-semantics" "*"
|
||||
"@types/keyv" "*"
|
||||
"@types/node" "*"
|
||||
"@types/responselike" "*"
|
||||
|
||||
"@types/cheerio@^0.22.8":
|
||||
version "0.22.21"
|
||||
resolved "https://registry.npmjs.org/@types/cheerio/-/cheerio-0.22.21.tgz#5e37887de309ba11b2e19a6e14cad7874b31a8a3"
|
||||
@@ -4739,6 +4761,11 @@
|
||||
resolved "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b"
|
||||
integrity sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==
|
||||
|
||||
"@types/http-cache-semantics@*":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz#9140779736aa2655635ee756e2467d787cfe8a2a"
|
||||
integrity sha512-c3Xy026kOF7QOTn00hbIllV1dLR9hG9NkSrLQgCVs8NF6sBU+VGWjD3wLPhmh1TYAc7ugCFsvHYMN4VcBN1U1A==
|
||||
|
||||
"@types/http-errors@^1.6.3":
|
||||
version "1.8.0"
|
||||
resolved "https://registry.npmjs.org/@types/http-errors/-/http-errors-1.8.0.tgz#682477dbbbd07cd032731cb3b0e7eaee3d026b69"
|
||||
@@ -4833,6 +4860,13 @@
|
||||
resolved "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72"
|
||||
integrity sha512-GJhpTepz2udxGexqos8wgaBx4I/zWIDPh/KOGEwAqtuGDkOUJu5eFvwmdBX4AmB8Odsr+9pHCQqiAqDL/yKMKw==
|
||||
|
||||
"@types/keyv@*", "@types/keyv@^3.1.1":
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.1.tgz#e45a45324fca9dab716ab1230ee249c9fb52cfa7"
|
||||
integrity sha512-MPtoySlAZQ37VoLaPcTHCu1RWJ4llDkULYZIzOYxlhxBqYPB0RsRlmMU0R6tahtFe27mIdkHV+551ZWV4PLmVw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/koa-compose@*":
|
||||
version "3.2.5"
|
||||
resolved "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.5.tgz#85eb2e80ac50be95f37ccf8c407c09bbe3468e9d"
|
||||
@@ -5150,6 +5184,13 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/responselike@*":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/rollup-plugin-peer-deps-external@^2.2.0":
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/@types/rollup-plugin-peer-deps-external/-/rollup-plugin-peer-deps-external-2.2.0.tgz#eae7d8b9d27fa037f5bcaded24e389f85b81973c"
|
||||
@@ -7389,6 +7430,14 @@ cache-base@^1.0.1:
|
||||
union-value "^1.0.0"
|
||||
unset-value "^1.0.0"
|
||||
|
||||
cacheable-lookup@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-2.0.1.tgz#87be64a18b925234875e10a9bb1ebca4adce6b38"
|
||||
integrity sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==
|
||||
dependencies:
|
||||
"@types/keyv" "^3.1.1"
|
||||
keyv "^4.0.0"
|
||||
|
||||
cacheable-request@^2.1.1:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
|
||||
@@ -7415,6 +7464,19 @@ cacheable-request@^6.0.0:
|
||||
normalize-url "^4.1.0"
|
||||
responselike "^1.0.2"
|
||||
|
||||
cacheable-request@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.1.tgz#062031c2856232782ed694a257fa35da93942a58"
|
||||
integrity sha512-lt0mJ6YAnsrBErpTMWeu5kl/tg9xMAWjavYTN6VQXM1A/teBITuNcccXsCxF0tDQQJf9DfAaX5O4e0zp0KlfZw==
|
||||
dependencies:
|
||||
clone-response "^1.0.2"
|
||||
get-stream "^5.1.0"
|
||||
http-cache-semantics "^4.0.0"
|
||||
keyv "^4.0.0"
|
||||
lowercase-keys "^2.0.0"
|
||||
normalize-url "^4.1.0"
|
||||
responselike "^2.0.0"
|
||||
|
||||
cachedir@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz#0c75892a052198f0b21c7c1804d8331edfcae0e8"
|
||||
@@ -9100,6 +9162,13 @@ decompress-response@^4.2.0:
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
|
||||
decompress-response@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-5.0.0.tgz#7849396e80e3d1eba8cb2f75ef4930f76461cb0f"
|
||||
integrity sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==
|
||||
dependencies:
|
||||
mimic-response "^2.0.0"
|
||||
|
||||
decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz#718cbd3fcb16209716e70a26b84e7ba4592e5af1"
|
||||
@@ -9215,6 +9284,11 @@ defer-to-connect@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591"
|
||||
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
|
||||
|
||||
defer-to-connect@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.0.tgz#83d6b199db041593ac84d781b5222308ccf4c2c1"
|
||||
integrity sha512-bYL2d05vOSf1JEZNx5vSAtPuBMkX8K9EUutg7zlKvTqKXHt7RhWJFbmd7qakVuf13i+IkGmp6FwSsONOf6VYIg==
|
||||
|
||||
define-properties@^1.1.2, define-properties@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
|
||||
@@ -11630,6 +11704,13 @@ git-url-parse@^11.1.2:
|
||||
dependencies:
|
||||
git-up "^4.0.0"
|
||||
|
||||
git-url-parse@^11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.3.tgz#03625b6fc09905e9ad1da7bb2b84be1bf9123143"
|
||||
integrity sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA==
|
||||
dependencies:
|
||||
git-up "^4.0.0"
|
||||
|
||||
gitconfiglocal@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz#41d045f3851a5ea88f03f24ca1c6178114464b9b"
|
||||
@@ -11853,6 +11934,27 @@ good-listener@^1.2.2:
|
||||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
got@^10.7.0:
|
||||
version "10.7.0"
|
||||
resolved "https://registry.npmjs.org/got/-/got-10.7.0.tgz#62889dbcd6cca32cd6a154cc2d0c6895121d091f"
|
||||
integrity sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^2.0.0"
|
||||
"@szmarczak/http-timer" "^4.0.0"
|
||||
"@types/cacheable-request" "^6.0.1"
|
||||
cacheable-lookup "^2.0.0"
|
||||
cacheable-request "^7.0.1"
|
||||
decompress-response "^5.0.0"
|
||||
duplexer3 "^0.1.4"
|
||||
get-stream "^5.0.0"
|
||||
lowercase-keys "^2.0.0"
|
||||
mimic-response "^2.1.0"
|
||||
p-cancelable "^2.0.0"
|
||||
p-event "^4.0.0"
|
||||
responselike "^2.0.0"
|
||||
to-readable-stream "^2.0.0"
|
||||
type-fest "^0.10.0"
|
||||
|
||||
got@^7.0.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.npmjs.org/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a"
|
||||
@@ -14292,6 +14394,11 @@ json-buffer@3.0.0:
|
||||
resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
|
||||
|
||||
json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
|
||||
@@ -14547,6 +14654,13 @@ keyv@^3.0.0:
|
||||
dependencies:
|
||||
json-buffer "3.0.0"
|
||||
|
||||
keyv@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.1.tgz#9fe703cb4a94d6d11729d320af033307efd02ee6"
|
||||
integrity sha512-xz6Jv6oNkbhrFCvCP7HQa8AaII8y8LRpoSm661NOKLr4uHuBwhX4epXrPQgF3+xdJnN4Esm5X0xwY4bOlALOtw==
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
@@ -15733,7 +15847,7 @@ mimic-response@^1.0.0, mimic-response@^1.0.1:
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
mimic-response@^2.0.0:
|
||||
mimic-response@^2.0.0, mimic-response@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43"
|
||||
integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==
|
||||
@@ -16323,6 +16437,21 @@ nodegit@0.26.5:
|
||||
request-promise-native "^1.0.5"
|
||||
tar-fs "^1.16.3"
|
||||
|
||||
nodegit@^0.27.0:
|
||||
version "0.27.0"
|
||||
resolved "https://registry.npmjs.org/nodegit/-/nodegit-0.27.0.tgz#4e8cc236f60e1c97324a5acff99056fe116a6ebe"
|
||||
integrity sha512-E9K4gPjWiA0b3Tx5lfWCzG7Cvodi2idl3V5UD2fZrOrHikIfrN7Fc2kWLtMUqqomyoToYJLeIC8IV7xb1CYRLA==
|
||||
dependencies:
|
||||
fs-extra "^7.0.0"
|
||||
got "^10.7.0"
|
||||
json5 "^2.1.0"
|
||||
lodash "^4.17.14"
|
||||
nan "^2.14.0"
|
||||
node-gyp "^4.0.0"
|
||||
node-pre-gyp "^0.13.0"
|
||||
ramda "^0.25.0"
|
||||
tar-fs "^1.16.3"
|
||||
|
||||
nodemon@^2.0.2:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz#55b09319eb488d6394aa9818148c0c2d1c04c416"
|
||||
@@ -16870,6 +16999,11 @@ p-cancelable@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc"
|
||||
integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==
|
||||
|
||||
p-cancelable@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e"
|
||||
integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg==
|
||||
|
||||
p-each-series@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz#961c8dd3f195ea96c747e636b262b800a6b1af48"
|
||||
@@ -16889,6 +17023,13 @@ p-event@^2.1.0:
|
||||
dependencies:
|
||||
p-timeout "^2.0.1"
|
||||
|
||||
p-event@^4.0.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5"
|
||||
integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==
|
||||
dependencies:
|
||||
p-timeout "^3.1.0"
|
||||
|
||||
p-finally@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
@@ -19745,6 +19886,13 @@ responselike@1.0.2, responselike@^1.0.2:
|
||||
dependencies:
|
||||
lowercase-keys "^1.0.0"
|
||||
|
||||
responselike@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/responselike/-/responselike-2.0.0.tgz#26391bcc3174f750f9a79eacc40a12a5c42d7723"
|
||||
integrity sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==
|
||||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
|
||||
restore-cursor@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-1.0.1.tgz#34661f46886327fed2991479152252df92daa541"
|
||||
@@ -21906,6 +22054,11 @@ to-readable-stream@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771"
|
||||
integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==
|
||||
|
||||
to-readable-stream@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-2.1.0.tgz#82880316121bea662cdc226adb30addb50cb06e8"
|
||||
integrity sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==
|
||||
|
||||
to-regex-range@^2.1.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38"
|
||||
@@ -22204,6 +22357,11 @@ type-detect@4.0.8:
|
||||
resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
|
||||
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
|
||||
|
||||
type-fest@^0.10.0:
|
||||
version "0.10.0"
|
||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.10.0.tgz#7f06b2b9fbfc581068d1341ffabd0349ceafc642"
|
||||
integrity sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==
|
||||
|
||||
type-fest@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
|
||||
|
||||
Reference in New Issue
Block a user