diff --git a/Source/configure/activate.ts b/Source/configure/activate.ts new file mode 100644 index 00000000..fc45460d --- /dev/null +++ b/Source/configure/activate.ts @@ -0,0 +1,43 @@ +import * as vscode from 'vscode'; +import { AzureTreeItem, createApiProvider, IActionContext, registerCommand } from 'vscode-azureextensionui'; +import { AzureExtensionApi, AzureExtensionApiProvider } from 'vscode-azureextensionui/api'; +import { browsePipeline } from './browse'; +import { configurePipeline } from './configure'; +import { telemetryHelper } from './helper/telemetryHelper'; +import { AzureAccountExtensionExports, extensionVariables, IResourceNode } from './model/models'; +import { Messages } from './resources/messages'; + +export async function activateConfigurePipeline(): Promise { + let azureAccountExtension = vscode.extensions.getExtension("ms-vscode.azure-account"); + if (!azureAccountExtension) { + throw new Error(Messages.azureAccountExntesionUnavailable); + } + + if (!azureAccountExtension.isActive) { + await azureAccountExtension.activate(); + } + + extensionVariables.azureAccountExtensionApi = azureAccountExtension.exports; + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + registerCommand('configure-cicd-pipeline', async (actionContext: IActionContext, node: IResourceNode | vscode.Uri) => { + // The code you place here will be executed every time your command is executed + telemetryHelper.initialize(actionContext, 'configure-cicd-pipeline'); + await configurePipeline(node); + }); + + registerCommand('browse-cicd-pipeline', async (actionContext: IActionContext, node: AzureTreeItem) => { + // The code you place here will be executed every time your command is executed + telemetryHelper.initialize(actionContext, 'browse-cicd-pipeline'); + await browsePipeline(node); + }); + + return createApiProvider([ + { + configurePipelineApi: configurePipeline, + browsePipeline: browsePipeline, + apiVersion: "0.0.1" + }]); +} diff --git a/Source/configure/browse.ts b/Source/configure/browse.ts new file mode 100644 index 00000000..86ea50f9 --- /dev/null +++ b/Source/configure/browse.ts @@ -0,0 +1,100 @@ +import * as vscode from 'vscode'; +import { AzureTreeItem, UserCancelledError } from 'vscode-azureextensionui'; +import { AppServiceClient, ScmType } from './clients/azure/appServiceClient'; +import { getSubscriptionSession } from './helper/azureSessionHelper'; +import { ControlProvider } from './helper/controlProvider'; +import { Result, telemetryHelper } from './helper/telemetryHelper'; +import { AzureSession, extensionVariables, ParsedAzureResourceId } from './model/models'; +import * as constants from './resources/constants'; +import { Messages } from './resources/messages'; +import { TelemetryKeys } from './resources/telemetryKeys'; +import { TracePoints } from './resources/tracePoints'; + +const Layer = 'browsePipeline'; + +export async function browsePipeline(node: AzureTreeItem): Promise { + await telemetryHelper.executeFunctionWithTimeTelemetry(async () => { + try { + if (!!node && !!node.fullId) { + let parsedAzureResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(node.fullId); + let session: AzureSession = await getSubscriptionSession(parsedAzureResourceId.subscriptionId); + let appServiceClient = new AppServiceClient(session.credentials, session.environment, session.tenantId, parsedAzureResourceId.subscriptionId); + await browsePipelineInternal(node.fullId, appServiceClient); + } + else { + throw new Error(Messages.didNotRecieveAzureResourceNodeToProcess); + } + } + catch (error) { + if (!(error instanceof UserCancelledError)) { + extensionVariables.outputChannel.appendLine(error.message); + vscode.window.showErrorMessage(error.message); + telemetryHelper.setResult(Result.Failed, error); + } + else { + telemetryHelper.setResult(Result.Canceled, error); + } + } + }, TelemetryKeys.CommandExecutionDuration); +} + +async function browsePipelineInternal(resourceId: string, appServiceClient: AppServiceClient): Promise { + let siteConfig = await appServiceClient.getAppServiceConfig(resourceId); + let scmType = !!siteConfig && !!siteConfig.scmType && siteConfig.scmType.toLowerCase(); + telemetryHelper.setTelemetry(TelemetryKeys.ScmType, scmType); + + if (scmType === ScmType.VSTSRM.toLowerCase()) { + await browseAzurePipeline(resourceId, appServiceClient); + } + else if (scmType === ScmType.GITHUBACTION.toLowerCase() && extensionVariables.enableGitHubWorkflow) { + await browseGitHubWorkflow(resourceId, appServiceClient); + } + else if (scmType === '' || scmType === ScmType.NONE.toLowerCase()) { + let deployToAzureAction = 'Deploy to Azure'; + let controlProvider = new ControlProvider(); + let result = await controlProvider.showInformationBox( + constants.BrowseNotAvailableConfigurePipeline, + Messages.browseNotAvailableConfigurePipeline, + deployToAzureAction); + + if (result === deployToAzureAction) { + vscode.commands.executeCommand('configure-pipeline', { fullId: resourceId }); + telemetryHelper.setTelemetry(TelemetryKeys.ClickedConfigurePipeline, 'true'); + } + } + else { + await openDeploymentCenter(resourceId, appServiceClient); + } +} + +async function browseAzurePipeline(resourceId: string, appServiceClient: AppServiceClient): Promise { + try { + let pipelineUrl = await appServiceClient.getAzurePipelineUrl(resourceId); + vscode.env.openExternal(vscode.Uri.parse(pipelineUrl)); + telemetryHelper.setTelemetry(TelemetryKeys.BrowsedExistingPipeline, 'true'); + } + catch (ex) { + telemetryHelper.logError(Layer, TracePoints.CorruptMetadataForVstsRmScmType, ex); + await openDeploymentCenter(resourceId, appServiceClient); + } +} + +async function browseGitHubWorkflow(resourceId: string, appServiceClient: AppServiceClient): Promise { + let webAppSourceControl = await appServiceClient.getSourceControl(resourceId); + let webAppMetaData = await appServiceClient.getAppServiceMetadata(resourceId); + + if (!!webAppSourceControl && !!webAppSourceControl.properties && webAppSourceControl.properties.isGitHubAction) { + let url = `${webAppSourceControl.properties.repoUrl}/actions?query=${encodeURI("workflow:\"" + (!!webAppMetaData.properties.configName ? webAppMetaData.properties.configName : webAppMetaData.properties.configPath) + "\"")}`; + await vscode.env.openExternal(vscode.Uri.parse(url)); + telemetryHelper.setTelemetry(TelemetryKeys.BrowsedExistingPipeline, 'true'); + } + else { + await openDeploymentCenter(resourceId, appServiceClient); + } +} + +async function openDeploymentCenter(resourceId: string, appServiceClient: AppServiceClient): Promise { + let deploymentCenterUrl: string = await appServiceClient.getDeploymentCenterUrl(resourceId); + await vscode.env.openExternal(vscode.Uri.parse(deploymentCenterUrl)); + telemetryHelper.setTelemetry(TelemetryKeys.BrowsedDeploymentCenter, 'true'); +} diff --git a/Source/configure/clients/IProvisioningServiceClient.ts b/Source/configure/clients/IProvisioningServiceClient.ts new file mode 100644 index 00000000..f9579be4 --- /dev/null +++ b/Source/configure/clients/IProvisioningServiceClient.ts @@ -0,0 +1,6 @@ +import { ProvisioningConfiguration } from "../model/provisioningConfiguration"; + +export interface IProvisioningServiceClient { + createProvisioningConfiguration(provisioningConfiguration: ProvisioningConfiguration, githubOrg: string, repositoryId: string): Promise; + getProvisioningConfiguration(jobId: string, githubOrg: string, repositoryId: string): Promise; +} diff --git a/Source/configure/clients/ITemplateServiceClient.ts b/Source/configure/clients/ITemplateServiceClient.ts new file mode 100644 index 00000000..84717819 --- /dev/null +++ b/Source/configure/clients/ITemplateServiceClient.ts @@ -0,0 +1,12 @@ +import { RepositoryAnalysis } from "azureintegration-repoanalysis-client-internal"; +import { ExtendedPipelineTemplate } from "../model/Contracts"; +import { StringMap } from "../model/models"; +import { TemplateInfo } from "../model/templateModels"; + +export interface ITemplateServiceClient { + getTemplates(body: RepositoryAnalysis): Promise; + getTemplateParameters(templateId: string): Promise; + getTemplateConfiguration(templateId: string, inputs: StringMap): Promise; + getTemplateFile(templateId: string, fileName: string): Promise<{ id: string, content: string }[]>; + getTemplatesInfoByFilter(language: string, deployTargetFilter?: string, buildTargetFilter?: string): Promise; +} diff --git a/Source/configure/clients/ProvisioningServiceClient.ts b/Source/configure/clients/ProvisioningServiceClient.ts new file mode 100644 index 00000000..396d5811 --- /dev/null +++ b/Source/configure/clients/ProvisioningServiceClient.ts @@ -0,0 +1,52 @@ +import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from "ms-rest"; +import { IServiceUrlDefinition, ServiceFramework } from "../helper/remoteServiceUrlHelper"; +import { ProvisioningConfiguration } from "../model/provisioningConfiguration"; +import { IProvisioningServiceClient } from "./IProvisioningServiceClient"; +import { RestClient } from "./restClient"; + +export class ProvisioningServiceClient implements IProvisioningServiceClient { + private restClient: RestClient; + private serviceDefinition: IServiceUrlDefinition; + private defaultHeaders: { [propertyName: string]: string }; + private defaultParameters: { [propertyName: string]: string }; + private readonly pipelineProvisioningJob = "PipelineProvisioningJob"; + private readonly PEProvisioningServiceAPIVersion = "6.1-preview.1"; + + constructor(serviceDefinition: IServiceUrlDefinition, headers: { [propertyName: string]: string }, credentials: ServiceClientCredentials) { + this.restClient = new RestClient(credentials); + this.serviceDefinition = serviceDefinition; + this.defaultHeaders = headers; + if (this.serviceDefinition.serviceFramework === ServiceFramework.Vssf) { + this.defaultParameters = { "api-version": this.PEProvisioningServiceAPIVersion }; + } + } + + public async createProvisioningConfiguration(provisioningConfiguration: ProvisioningConfiguration, githubOrg: string, repositoryId: string): Promise { + const requestUrl = this.serviceDefinition.serviceUrl + githubOrg + "/" + repositoryId + "/" + this.pipelineProvisioningJob; + + return this.restClient.sendRequest({ + url: requestUrl, + method: "POST", + headers: this.defaultHeaders, + queryParameters: this.defaultParameters, + body: provisioningConfiguration, + serializationMapper: null, + deserializationMapper: null, + } + ); + } + + public async getProvisioningConfiguration(jobId: string, githubOrg: string, repositoryId: string): Promise { + const requestUrl = this.serviceDefinition.serviceUrl + githubOrg + "/" + repositoryId + "/" + this.pipelineProvisioningJob + "/" + jobId; + + return this.restClient.sendRequest({ + url: requestUrl, + method: "GET", + headers: this.defaultHeaders, + queryParameters: this.defaultParameters, + serializationMapper: null, + deserializationMapper: null, + } + ); + } +} diff --git a/Source/configure/clients/TemplateServiceClientFactory.ts b/Source/configure/clients/TemplateServiceClientFactory.ts new file mode 100644 index 00000000..69af3258 --- /dev/null +++ b/Source/configure/clients/TemplateServiceClientFactory.ts @@ -0,0 +1,43 @@ +import { ServiceClientCredentials, TokenCredentials } from "ms-rest"; +import { RemoteServiceUrlHelper, ServiceFramework } from "../helper/remoteServiceUrlHelper"; +import { Messages } from '../resources/messages'; +import { TemplateServiceClient } from "./github/TemplateServiceClient"; +import { ITemplateServiceClient } from "./ITemplateServiceClient"; +const UserAgent = "deploy-to-azure-vscode"; + +export class TemplateServiceClientFactory { + + public static async getClient(): Promise { + if (!!this.client) { + return this.client; + } + + if (!this.githubPatToken || !this.credentials) { + throw new Error(Messages.UndefinedClientCredentials); + } + + const serviceDefinition = await RemoteServiceUrlHelper.getTemplateServiceDefinition(); + if (serviceDefinition.serviceFramework === ServiceFramework.Vssf) { + this.client = new TemplateServiceClient(serviceDefinition.serviceUrl, this.credentials, { + "Content-Type": "application/json; charset=utf-8" + }); + } else { + this.client = new TemplateServiceClient(serviceDefinition.serviceUrl, new TokenCredentials(this.githubPatToken, "token"), { + "User-Agent": UserAgent, + "Content-Type": "application/json; charset=utf-8" + }); + } + + return this.client; + } + + public static initialize(credentials: ServiceClientCredentials, githubPatToken: string): void { + this.client = null; + this.credentials = credentials; + this.githubPatToken = githubPatToken + } + + private static client: ITemplateServiceClient; + private static credentials: ServiceClientCredentials; + private static githubPatToken: string; +} diff --git a/Source/configure/clients/azure/appServiceClient.ts b/Source/configure/clients/azure/appServiceClient.ts new file mode 100644 index 00000000..781fbf40 --- /dev/null +++ b/Source/configure/clients/azure/appServiceClient.ts @@ -0,0 +1,178 @@ +const uuid = require('uuid/v4'); +import { GenericResource, ResourceListResult } from 'azure-arm-resource/lib/resource/models'; +import { WebSiteManagementClient } from 'azure-arm-website'; +import { Deployment, SiteConfigResource, StringDictionary } from 'azure-arm-website/lib/models'; +import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from 'ms-rest'; +import { AzureEnvironment } from 'ms-rest-azure'; +import * as Q from 'q'; +import { telemetryHelper } from '../../helper/telemetryHelper'; +import { ParsedAzureResourceId, TargetKind, WebAppSourceControl } from '../../model/models'; +import { Messages } from '../../resources/messages'; +import { TelemetryKeys } from '../../resources/telemetryKeys'; +import { AzureResourceClient } from './azureResourceClient'; + +export class AppServiceClient extends AzureResourceClient { + + private static resourceType = 'Microsoft.Web/sites'; + private webSiteManagementClient: WebSiteManagementClient; + private tenantId: string; + private environment: AzureEnvironment; + + constructor(credentials: ServiceClientCredentials, environment: AzureEnvironment, tenantId: string, subscriptionId: string) { + super(credentials, subscriptionId); + this.webSiteManagementClient = new WebSiteManagementClient(credentials, subscriptionId); + this.tenantId = tenantId; + this.environment = environment; + } + + public async getAppServiceResource(resourceId: string): Promise { + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + return await this.webSiteManagementClient.webApps.get(parsedResourceId.resourceGroup, parsedResourceId.resourceName); + } + + public async GetAppServices(filtersForResourceKind: TargetKind[]): Promise { + let resourceList: ResourceListResult = await this.getResourceList(AppServiceClient.resourceType); + if (!!filtersForResourceKind && filtersForResourceKind.length > 0) { + let filteredResourceList: ResourceListResult = []; + + resourceList.forEach((resource) => { + if (filtersForResourceKind.some((kind) => resource.kind === kind)) { + filteredResourceList.push(resource); + } + }); + + resourceList = filteredResourceList; + } + return resourceList; + } + + public async getWebAppPublishProfileXml(resourceId: string): Promise { + const deferred: Q.Deferred = Q.defer(); + const parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + let publishProfile = ''; + this.webSiteManagementClient.webApps.listPublishingProfileXmlWithSecrets(parsedResourceId.resourceGroup, parsedResourceId.resourceName, {}, (err, result, request, response) => { + if (err) { + deferred.reject(err); + } + response.on("data", (chunk: Buffer) => { + publishProfile += chunk; + }); + response.on('end', () => { + deferred.resolve(publishProfile); + }); + }); + return deferred.promise; + } + + public async getDeploymentCenterUrl(resourceId: string): Promise { + return `${this.environment.portalUrl}/#@${this.tenantId}/resource/${resourceId}/vstscd`; + } + + public async getAzurePipelineUrl(resourceId: string): Promise { + let metadata = await this.getAppServiceMetadata(resourceId); + if (metadata.properties['VSTSRM_BuildDefinitionWebAccessUrl']) { + return metadata.properties['VSTSRM_BuildDefinitionWebAccessUrl']; + } + + throw new Error(Messages.cannotFindPipelineUrlInMetaDataException); + } + + public async getAppServiceConfig(resourceId: string): Promise { + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + return this.webSiteManagementClient.webApps.getConfiguration(parsedResourceId.resourceGroup, parsedResourceId.resourceName); + } + + public async updateScmType(resourceId: string): Promise { + let siteConfig = await this.getAppServiceConfig(resourceId); + siteConfig.scmType = ScmType.VSTSRM; + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + return this.webSiteManagementClient.webApps.updateConfiguration(parsedResourceId.resourceGroup, parsedResourceId.resourceName, siteConfig); + } + + public async getAppServiceMetadata(resourceId: string): Promise { + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + return this.webSiteManagementClient.webApps.listMetadata(parsedResourceId.resourceGroup, parsedResourceId.resourceName); + } + + public async updateAppServiceMetadata(resourceId: string, metadata: StringDictionary): Promise { + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + return this.webSiteManagementClient.webApps.updateMetadata(parsedResourceId.resourceGroup, parsedResourceId.resourceName, metadata); + } + + public async publishDeploymentToAppService(resourceId: string, deploymentMessage: string, author: string = 'VSTS', deployer: string = 'VSTS'): Promise { + let parsedResourceId: ParsedAzureResourceId = new ParsedAzureResourceId(resourceId); + + // create deployment object + let deploymentId = uuid(); + let deployment = this.createDeploymentObject(deploymentId, deploymentMessage, author, deployer); + return this.webSiteManagementClient.webApps.createDeployment(parsedResourceId.resourceGroup, parsedResourceId.resourceName, deploymentId, deployment); + } + + public async setSourceControl(resourceId: string, properties: any): Promise { + await this.webSiteManagementClient.sendRequest({ + url: `${this.environment.resourceManagerEndpointUrl}${resourceId}/sourcecontrols/web`, + method: "PUT", + queryParameters: { + 'api-version': '2018-11-01' + }, + body: { + "properties": properties + }, + serializationMapper: null, + deserializationMapper: null + }); + } + + public async getSourceControl(resourceId: string): Promise { + return this.webSiteManagementClient.sendRequest({ + url: `${this.environment.resourceManagerEndpointUrl}${resourceId}/sourcecontrols/web`, + method: "GET", + queryParameters: { + 'api-version': '2018-11-01' + }, + serializationMapper: null, + deserializationMapper: null + }); + } + + public async isScmTypeSet(resourceId: string): Promise { + // Check for SCM type, if its value is set then a pipeline is already setup. + let siteConfig = await this.getAppServiceConfig(resourceId); + if (!!siteConfig.scmType && siteConfig.scmType.toLowerCase() !== ScmType.NONE.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.ScmType, siteConfig.scmType.toLowerCase()); + return true; + } + + return false; + } + + private createDeploymentObject(deploymentId: string, deploymentMessage: string, author: string, deployer: string): Deployment { + let deployment: Deployment = { + id: deploymentId, + status: 4, + author: author, + deployer: deployer, + message: deploymentMessage + }; + + return deployment; + } +} + +export enum ScmType { + VSTSRM = 'VSTSRM', + NONE = 'NONE', + GITHUBACTION = 'GITHUBACTION' +} + +export interface DeploymentMessage { + // tslint:disable-next-line: no-reserved-keywords + type: string; + message: string; +} + +export interface VSTSDeploymentMessage extends DeploymentMessage { + VSTSRM_BuildDefinitionWebAccessUrl?: string; + VSTSRM_ConfiguredCDEndPoint: string; + VSTSRM_BuildWebAccessUrl: string; +} \ No newline at end of file diff --git a/Source/configure/clients/azure/armRestClient.ts b/Source/configure/clients/azure/armRestClient.ts new file mode 100644 index 00000000..a6d4218b --- /dev/null +++ b/Source/configure/clients/azure/armRestClient.ts @@ -0,0 +1,65 @@ +import { AzureSession, ParsedAzureResourceId } from '../../model/models'; +import { RestClient } from '../restClient'; + +export class ArmRestClient { + private resourceManagerEndpointUrl: string; + private restClient: RestClient; + + public constructor(azureSession: AzureSession) { + this.resourceManagerEndpointUrl = azureSession.environment.resourceManagerEndpointUrl; + this.restClient = new RestClient(azureSession.credentials); + } + + public fetchArmData(endPointUri: string, httpMethod: string, body?: any) { + return this.sendRequest( + this.resourceManagerEndpointUrl + endPointUri, + httpMethod, + null, + body + ); + } + + public async getAcrCredentials(acrId: string): Promise { + let parsedResourceId = new ParsedAzureResourceId(acrId); + return this.sendRequest( + this.resourceManagerEndpointUrl + `/subscriptions/${parsedResourceId.subscriptionId}/resourceGroups/${parsedResourceId.resourceGroup}/providers/Microsoft.ContainerRegistry/registries/${parsedResourceId.resourceName}/listCredentials`, + 'POST', + '2019-05-01', + null); + } + + public async getAksKubeConfig(clusterId: string): Promise { + let parsedResourceId = new ParsedAzureResourceId(clusterId); + return this.sendRequest( + this.resourceManagerEndpointUrl + `/subscriptions/${parsedResourceId.subscriptionId}/resourceGroups/${parsedResourceId.resourceGroup}/providers/Microsoft.ContainerService/managedClusters/${parsedResourceId.resourceName}/listClusterAdminCredential`, + 'POST', + '2020-01-01', + null); + } + + public async createResourceGroup(subscriptionId: string, resourceGroup: string, location: string ): Promise{ + return this.sendRequest( + this.resourceManagerEndpointUrl + `/subscriptions/${subscriptionId}/resourceGroups/${resourceGroup}`, + 'PUT', + '2020-06-01', + { "location": location } + ); + } + + private async sendRequest(url: string, httpMethod: string, apiVersion: string, body?: any): Promise { + return this.restClient.sendRequest( + { + url: url, + method: httpMethod, + headers: { + "Content-Type": "application/json" + }, + queryParameters: { + 'api-version': apiVersion + }, + body: body, + deserializationMapper: null, + serializationMapper: null + }); + } +} \ No newline at end of file diff --git a/Source/configure/clients/azure/azureResourceClient.ts b/Source/configure/clients/azure/azureResourceClient.ts new file mode 100644 index 00000000..3e2ed57a --- /dev/null +++ b/Source/configure/clients/azure/azureResourceClient.ts @@ -0,0 +1,132 @@ +import { GenericResource, ResourceListResult } from 'azure-arm-resource/lib/resource/models'; +import * as ResourceManagementClient from 'azure-arm-resource/lib/resource/resourceManagementClient'; +import { ServiceClientCredentials } from 'ms-rest'; +import * as utils from 'util'; +import { TargetKind, TargetResourceType } from '../../model/models'; +import { Messages } from '../../resources/messages'; + +export class AzureResourceClient { + + private azureRmClient: ResourceManagementClient.ResourceManagementClient; + + constructor(credentials: ServiceClientCredentials, subscriptionId: string) { + this.azureRmClient = new ResourceManagementClient.ResourceManagementClient(credentials, subscriptionId); + } + + public static validateTargetResourceType(resource: GenericResource): void { + if (!resource) { + throw new Error(Messages.azureResourceIsNull); + } + + switch (resource.type.toLowerCase()) { + case TargetResourceType.WebApp.toLowerCase(): + switch (resource.kind ? resource.kind.toLowerCase() : '') { + case TargetKind.LinuxApp: + case TargetKind.FunctionAppLinux: + case TargetKind.WindowsApp: + return; + case TargetKind.LinuxContainerApp: + case TargetKind.FunctionApp: + default: + throw new Error(utils.format(Messages.appKindIsNotSupported, resource.kind)); + } + case TargetResourceType.AKS.toLowerCase(): + return; + default: + throw new Error(utils.format(Messages.resourceTypeIsNotSupported, resource.type)); + } + } + + public async getResourceList(resourceType: string, followNextLink: boolean = true): Promise { + let resourceListResult: ResourceListResult = await this.azureRmClient.resources.list({ filter: `resourceType eq '${resourceType}'` }); + + if (followNextLink) { + let nextLink: string = resourceListResult.nextLink; + while (!!nextLink) { + let nextResourceListResult = await this.azureRmClient.resources.listNext(nextLink); + resourceListResult = resourceListResult.concat(nextResourceListResult); + nextLink = nextResourceListResult.nextLink; + } + } + + return resourceListResult; + } + + public async getResource(resourceId: string, apiVersion: string = '2019-10-01'): Promise { + let resource: GenericResource = await this.azureRmClient.resources.getById(resourceId, apiVersion); + return resource; + } + + public async updateCdSetupResourceTag(resource: GenericResource, repositoryId: string, branch: string, workflowFileName: string, commitId: string, namespaceName: string, apiVersion: string = '2019-10-01'): Promise { + let deploymentData: string = "GH" + ":" + repositoryId + ":" + branch + ":" + workflowFileName + ":" + workflowFileName + ":" + commitId + ":" + namespaceName + ":" + Date.now(); + resource.tags = resource.tags ? resource.tags : {}; + resource.tags = this.ComputeDeploymentResourceTags(resource.tags, deploymentData); + return await this.azureRmClient.resources.updateById(resource.id, apiVersion, resource); + } + + private ComputeDeploymentResourceTags(resourceTags: { [key: string]: string }, deploymentData: string) { + let startNewRow: boolean = true; + let storageColumn: boolean = true; //if storageColumn = true -> store in Tag Key field, else Tag Value field + let newTagKey: string = ""; + let newTagValue: string = ""; + + for (let tagName in resourceTags) { + //check if existing entry for resource tags + if (tagName.startsWith(DevopsInfoTagHeader)) { + // check if resource tags can be stored in tag Key field + if (tagName.length + deploymentData.length < MaxTagKeyLength) { + startNewRow = false; + newTagKey = tagName; + newTagValue = resourceTags[tagName]; + resourceTags[tagName] = null; + break; + } + // check if resource tags can be stored in tag Value field + else if (resourceTags[tagName].length + deploymentData.length < MaxTagValueLength) { + storageColumn = false; + startNewRow = false; + newTagKey = tagName; + newTagValue = resourceTags[tagName]; + resourceTags[tagName] = null; + break; + } + } + } + + if (newTagKey) { + let tempResourceTags = {}; + for (let key in resourceTags) { + if (key !== newTagKey) { + tempResourceTags[key] = resourceTags[key]; + } + } + resourceTags = tempResourceTags; + } + + if (startNewRow) { + if (Object.keys(resourceTags).length > MaxTagsRow) { + throw new Error(Messages.EmptyTagRowUnavailable); + } + newTagKey = DevopsInfoTagHeader; + } + + if (storageColumn) { //Store resource tag in key field + newTagKey += deploymentData + ";"; + } + else { //Store resource tag in value field + newTagValue += deploymentData + ";"; + } + + resourceTags[newTagKey] = newTagValue; + return resourceTags; + } +} + +export let ApiVersions: Map = new Map(); +ApiVersions.set(TargetResourceType.ACR, '2019-05-01'); +ApiVersions.set(TargetResourceType.AKS, '2019-10-01'); + +const DevopsInfoTagHeader: string = "hidden-DevOpsInfo:"; +const MaxTagKeyLength: number = 512; +const MaxTagValueLength: number = 256; +const MaxTagsRow: number = 50; diff --git a/Source/configure/clients/devOps/azureDevOpsClient.ts b/Source/configure/clients/devOps/azureDevOpsClient.ts new file mode 100644 index 00000000..f5b95de6 --- /dev/null +++ b/Source/configure/clients/devOps/azureDevOpsClient.ts @@ -0,0 +1,371 @@ +import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from 'ms-rest'; +import * as Q from 'q'; +import * as util from 'util'; +import { sleepForMilliSeconds, stringCompareFunction } from "../../helper/commonHelper"; +import { telemetryHelper } from '../../helper/telemetryHelper'; +import { Build, BuildDefinition, Repository } from '../../model/azureDevOps'; +import { DevOpsProject, Organization } from '../../model/models'; +import { AzureDevOpsBaseUrl, ReservedHostNames } from '../../resources/constants'; +import { Messages } from '../../resources/messages'; +import { RestClient } from '../restClient'; + +export class AzureDevOpsClient { + private restClient: RestClient; + private listOrgPromise: Promise; + + constructor(credentials: ServiceClientCredentials) { + this.restClient = new RestClient(credentials); + this.listOrgPromise = this.listOrganizations(); + } + + public async sendRequest(urlBasedRequestPrepareOptions: UrlBasedRequestPrepareOptions): Promise { + if (urlBasedRequestPrepareOptions.headers) { + urlBasedRequestPrepareOptions.headers['X-TFS-Session'] = telemetryHelper.getJourneyId(); + } + else { + urlBasedRequestPrepareOptions.headers = { 'X-TFS-Session': telemetryHelper.getJourneyId() }; + } + + return this.restClient.sendRequest(urlBasedRequestPrepareOptions); + } + + public async createOrganization(organizationName: string): Promise { + return this.sendRequest({ + url: "https://app.vsaex.visualstudio.com/_apis/HostAcquisition/collections", + headers: { + "Content-Type": "application/json" + }, + method: "POST", + queryParameters: { + "collectionName": organizationName, + "api-version": "4.0-preview.1", + "preferredRegion": "CUS" + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createProject(organizationName: string, projectName: string): Promise { + let collectionUrl = `https://dev.azure.com/${organizationName}`; + + return this.sendRequest({ + url: `${collectionUrl}/_apis/projects`, + headers: { + "Content-Type": "application/json" + }, + method: "POST", + queryParameters: { + "api-version": "5.0" + }, + body: { + "name": projectName, + "visibility": 0, + "capabilities": { + "versioncontrol": { "sourceControlType": "Git" }, + "processTemplate": { "templateTypeId": "adcc42ab-9882-485e-a3ed-7678f01f66bc" } + } + }, + deserializationMapper: null, + serializationMapper: null + }) + .then((operation) => { + if (operation.url) { + return this.monitorOperationStatus(operation.url); + } + else { + throw new Error(util.format(Messages.failedToCreateAzureDevOpsProject, operation.message)); + } + }); + } + + public async listOrganizations(forceRefresh?: boolean): Promise { + if (!this.listOrgPromise || forceRefresh) { + this.listOrgPromise = this.getUserData() + .then((connectionData) => { + return this.sendRequest({ + url: "https://app.vssps.visualstudio.com/_apis/accounts", + headers: { + "Content-Type": "application/json" + }, + method: "GET", + queryParameters: { + "memberId": connectionData.authenticatedUser.id, + "api-version": "5.0", + "properties": "Microsoft.VisualStudio.Services.Account.ServiceUrl.00025394-6065-48ca-87d9-7f5672854ef7" + }, + deserializationMapper: null, + serializationMapper: null + }); + }) + .then((organizations) => { + let organizationList: Array = organizations.value; + organizationList = organizationList.sort((org1, org2) => stringCompareFunction(org1.accountName, org2.accountName)); + return organizationList; + }); + } + + return this.listOrgPromise; + } + + public async listProjects(organizationName: string): Promise> { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/_apis/projects`; + let response = await this.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json" + }, + method: "GET", + queryParameters: { + "includeCapabilities": "true" + }, + deserializationMapper: null, + serializationMapper: null + }); + + let projects: Array = []; + if (response.value && response.value.length > 0) { + projects = response.value.map((project) => { + return { id: project.id, name: project.name }; + }); + projects = projects.sort((proj1, proj2) => stringCompareFunction(proj1.name, proj2.name)); + } + return projects; + } + + public async getRepository(organizationName: string, projectName: string, repositoryName: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/${projectName}/_apis/git/repositories/${repositoryName}`; + + return this.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + }, + method: "GET", + queryParameters: { + "api-version": "5.0" + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createBuildDefinition(organizationName: string, buildDefinition: BuildDefinition): Promise { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/${buildDefinition.project.id}/_apis/build/definitions`; + + return this.sendRequest({ + url: url, + method: "POST", + headers: { + "Accept": "application/json;api-version=5.0-preview.7;" + }, + body: buildDefinition, + serializationMapper: null, + deserializationMapper: null + }); + } + + public async queueBuild(organizationName: string, build: Build): Promise { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/${build.project.id}/_apis/build/builds`; + + return this.sendRequest({ + url: url, + method: "POST", + headers: { + "Accept": "application/json;api-version=5.2-preview.5;" + }, + body: build, + serializationMapper: null, + deserializationMapper: null + }); + } + + public async validateOrganizationName(organizationName: string): Promise { + let deferred = Q.defer(); + let accountNameRegex = new RegExp(/^[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]$|^[a-zA-Z]$/); + + if (!organizationName || /^\\s/.test(organizationName) || /\\s$/.test(organizationName) || organizationName.indexOf("-") === 0 || !accountNameRegex.test(organizationName)) { + deferred.resolve(Messages.organizationNameStaticValidationMessage); + } + else if (ReservedHostNames.indexOf(organizationName) >= 0) { + deferred.resolve(util.format(Messages.organizationNameReservedMessage, organizationName)); + } + else { + let url = `https://app.vsaex.visualstudio.com/_apis/HostAcquisition/NameAvailability/${organizationName}`; + + this.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "api-version=5.0-preview.1" + }, + method: "GET", + deserializationMapper: null, + serializationMapper: null + }) + .then((response) => { + if (response.name === organizationName && !response.isAvailable) { + deferred.resolve(util.format(Messages.organizationNameReservedMessage, organizationName)); + } + deferred.resolve(""); + }) + .catch(() => { + deferred.resolve(""); + }); + } + return deferred.promise; + } + + public async getProjectIdFromName(organizationName: string, projectName: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/_apis/projects/${projectName}`; + + return this.sendRequest({ + url: url, + method: "GET", + headers: { + "Accept": "application/json;api-version=5.2-preview.5;" + }, + queryParameters: { + "api-version": "5.0", + "includeCapabilities": false + }, + serializationMapper: null, + deserializationMapper: null + }) + .then((project) => { + return project && project.id; + }); + } + + public async getOrganizationIdFromName(organizationName: string) { + let organization = (await this.listOrgPromise).find((org) => { + return org.accountName.toLowerCase() === organizationName.toLowerCase(); + }); + + if (!organizationName) { + organization = (await this.listOrganizations(true)).find((org) => { + return org.accountName.toLowerCase() === organizationName.toLowerCase(); + }); + + if (!organization) { + throw new Error(Messages.cannotFindOrganizationWithName); + } + } + + return organization.accountId; + } + + public getOldFormatBuildDefinitionUrl(accountName: string, projectName: string, buildDefinitionId: number) { + return `https://${accountName}.visualstudio.com/${projectName}/_build?definitionId=${buildDefinitionId}&_a=summary`; + } + + public getOldFormatBuildUrl(accountName: string, projectName: string, buildId: string) { + return `https://${accountName}.visualstudio.com/${projectName}/_build/results?buildId=${buildId}&view=results`; + } + + private getUserData(): Promise { + return this.getConnectionData() + .catch(() => { + return this.createUserProfile() + .then(() => { + return this.getConnectionData(); + }); + }); + } + + private getConnectionData(): Promise { + return this.sendRequest({ + url: "https://app.vssps.visualstudio.com/_apis/connectiondata", + headers: { + "Content-Type": "application/json" + }, + method: "GET", + deserializationMapper: null, + serializationMapper: null + }); + } + + private createUserProfile(): Promise { + return this.sendRequest({ + url: "https://app.vssps.visualstudio.com/_apis/_AzureProfile/CreateProfile", + headers: { + "Content-Type": "application/json" + }, + method: "POST", + deserializationMapper: null, + serializationMapper: null + }); + } + + private async monitorOperationStatus(operationUrl: string): Promise { + let retryCount = 0; + let operationResult: any; + + while (retryCount < 30) { + operationResult = await this.getOperationResult(operationUrl); + let result = operationResult.status.toLowerCase(); + if (result === "succeeded") { + return; + } + else if (result === "failed") { + throw new Error(util.format(Messages.failedToCreateAzureDevOpsProject, operationResult.detailedMessage)); + } + else { + retryCount++; + await sleepForMilliSeconds(2000); + } + } + throw new Error(util.format(Messages.failedToCreateAzureDevOpsProject, + (operationResult && operationResult.detailedMessage) || Messages.operationTimedOut)); + } + + private async getOperationResult(operationUrl: string): Promise { + return this.sendRequest({ + url: operationUrl, + queryParameters: { + "api-version": "5.0" + }, + method: "GET", + deserializationMapper: null, + serializationMapper: null + }); + } + + public getAgentQueues(organizationName: string, projectName: string): Promise> { + let url = `${AzureDevOpsBaseUrl}/${organizationName}/${projectName}/_apis/distributedtask/queues`; + + return this.sendRequest({ + url: url, + method: "GET", + headers: { + "Accept": "application/json;" + }, + queryParameters: { + "api-version": "5.1-preview.1" + }, + serializationMapper: null, + deserializationMapper: null + }) + .then((response) => { + return response.value; + }); + } + + public createRepository(organizationName: string, projectId: string, repositoryName: string): Promise { + return this.sendRequest({ + url: `${AzureDevOpsBaseUrl}/${organizationName}/${projectId}/_apis/git/repositories`, + headers: { + 'Content-Type': 'application/json' + }, + method: 'POST', + queryParameters: { + 'api-version': '5.1' + }, + body: { + 'name': repositoryName + }, + deserializationMapper: null, + serializationMapper: null + }); + } +} diff --git a/Source/configure/clients/devOps/serviceConnectionClient.ts b/Source/configure/clients/devOps/serviceConnectionClient.ts new file mode 100644 index 00000000..28ddf0a3 --- /dev/null +++ b/Source/configure/clients/devOps/serviceConnectionClient.ts @@ -0,0 +1,241 @@ +import { UrlBasedRequestPrepareOptions } from 'ms-rest'; +import { AadApplication, ParsedAzureResourceId } from '../../model/models'; +import { AzureDevOpsBaseUrl } from "../../resources/constants"; +import { AzureDevOpsClient } from './azureDevOpsClient'; + + +export class ServiceConnectionClient { + private azureDevOpsClient: AzureDevOpsClient; + private organizationName: string; + private projectName: string; + + constructor(organizationName: string, projectName: string, azureDevOpsClient: AzureDevOpsClient) { + this.azureDevOpsClient = azureDevOpsClient; + this.organizationName = organizationName; + this.projectName = projectName; + } + + public async createGitHubServiceConnection(endpointName: string, gitHubPat: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints`; + + return this.azureDevOpsClient.sendRequest( + { + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "POST", + body: { + "administratorsGroup": null, + "authorization": { + "parameters": { + "accessToken": gitHubPat + }, + "scheme": "PersonalAccessToken" + }, + "description": "", + "groupScopeId": null, + "name": endpointName, + "operationStatus": null, + "readersGroup": null, + "type": "github", + "url": "http://github.com" + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createAzureSPNServiceConnection(endpointName: string, tenantId: string, subscriptionId: string, scope: string, aadApp: AadApplication): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints`; + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "POST", + body: { + "administratorsGroup": null, + "authorization": { + "parameters": { + "authenticationType": "spnKey", + "scope": scope, + "serviceprincipalid": aadApp.appId, + "serviceprincipalkey": aadApp.secret, + "tenantid": tenantId + }, + "scheme": "ServicePrincipal" + }, + "data": { + "creationMode": "Manual", + "subscriptionId": subscriptionId, + "subscriptionName": subscriptionId + }, + "description": "", + "groupScopeId": null, + "name": endpointName, + "operationStatus": null, + "readersGroup": null, + "type": "azurerm", + "url": "https://management.azure.com/" + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createKubernetesServiceConnectionWithKubeConfig(endpointName: string, kubeconfig: string, apiServerAddress: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints`; + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "POST", + body: { + "administratorsGroup": null, + "authorization": { + "parameters": { + "kubeconfig": kubeconfig + }, + "scheme": "Kubernetes" + }, + "data": { + "authorizationType": "Kubeconfig", + "acceptUntrustedCerts": "true", + }, + "description": "", + "groupScopeId": null, + "name": endpointName, + "operationStatus": null, + "readersGroup": null, + "type": "kubernetes", + "url": apiServerAddress + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createContainerRegistryServiceConnection(endpointName: string, registryUrl: string, registryUsername: string, registryPassword?: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints`; + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "POST", + body: { + "administratorsGroup": null, + "authorization": { + "parameters": { + "registry": registryUrl, + "username": registryUsername, + "password": registryPassword, + "email": "" + }, + "scheme": "UsernamePassword" + }, + "data": { + "registryType": "Others" + }, + "description": "", + "groupScopeId": null, + "name": endpointName, + "operationStatus": null, + "readersGroup": null, + "type": "dockerregistry", + "url": registryUrl + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async createAzurePublishProfileServiceConnection(endpointName: string, tenantId: string, resourceId: string, publishProfile: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints`; + let parsedResourceId = new ParsedAzureResourceId(resourceId); + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "POST", + body: { + "administratorsGroup": null, + "authorization": { + "parameters": { + "publishProfile": publishProfile, + "tenantid": tenantId + }, + "scheme": "PublishProfile" + }, + "data": { + "subscriptionName": parsedResourceId.subscriptionId, + "resourceId": resourceId + }, + "description": "", + "groupScopeId": null, + "name": endpointName, + "operationStatus": null, + "readersGroup": null, + "type": "azurerm", + "url": "https://management.azure.com/" + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async getEndpointStatus(endpointId: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/serviceendpoint/endpoints/${endpointId}`; + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.2;excludeUrls=true" + }, + method: "Get", + deserializationMapper: null, + serializationMapper: null + }); + } + + public async authorizeEndpointForAllPipelines(endpointId: string): Promise { + let url = `${AzureDevOpsBaseUrl}/${this.organizationName}/${this.projectName}/_apis/pipelines/pipelinePermissions/endpoint/${endpointId}`; + + return this.azureDevOpsClient.sendRequest({ + url: url, + headers: { + "Content-Type": "application/json", + "Accept": "application/json;api-version=5.1-preview.1;excludeUrls=true;enumsAsNumbers=true;msDateFormat=true;noArrayWrap=true" + }, + method: "PATCH", + body: { + "allPipelines": { + "authorized": true, + "authorizedBy": null, + "authorizedOn": null + }, + "pipelines": null, + "resource": { + "id": endpointId, + "type": "endpoint" + } + }, + deserializationMapper: null, + serializationMapper: null + }); + } + +} diff --git a/Source/configure/clients/github/TemplateServiceClient.ts b/Source/configure/clients/github/TemplateServiceClient.ts new file mode 100644 index 00000000..9b348e5a --- /dev/null +++ b/Source/configure/clients/github/TemplateServiceClient.ts @@ -0,0 +1,112 @@ +import { RepositoryAnalysis } from "azureintegration-repoanalysis-client-internal"; +import { ServiceClientCredentials } from "ms-rest"; +import { ExtendedPipelineTemplate } from "../../model/Contracts"; +import { StringMap } from "../../model/models"; +import { TemplateInfo } from "../../model/templateModels"; +import { ITemplateServiceClient } from "../ITemplateServiceClient"; +import { RestClient } from "../restClient"; + +export class TemplateServiceClient implements ITemplateServiceClient { + private restClient: RestClient; + private templateServiceUri: string; + private headers; + private readonly apiVersion = "6.0-preview.1"; + private readonly extendedPipelineTemplateResource = "ExtendedPipelineTemplates"; + private readonly templatesInfoResource = "TemplatesInfo"; + private readonly templateAssetFilesResource = "TemplateAssetFiles"; + private readonly hideKey = "vside"; + + constructor(url: string, creds?: ServiceClientCredentials, headers?) { + this.restClient = new RestClient(creds); + this.templateServiceUri = url; + this.headers = headers; + } + + public async getTemplatesInfoByFilter(language: string, deployTarget: string, buildTarget: string): Promise { + return this.restClient.sendRequest( + { + url: this.templateServiceUri + this.templatesInfoResource, + method: 'GET', + headers: this.headers, + queryParameters: { + 'api-version': this.apiVersion, + 'languageFilter': language, + 'deployTargetFilter': deployTarget, + 'buildTargetFilter': buildTarget, + 'hideKey': this.hideKey + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async getTemplates(body: RepositoryAnalysis): Promise { + return this.restClient.sendRequest( + { + url: this.templateServiceUri + this.templatesInfoResource, + method: 'POST', + headers: this.headers, + queryParameters: { + 'api-version': this.apiVersion, + 'hideKey': this.hideKey + }, + body: body, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async getTemplateParameters(templateId: string): Promise { + const requestUri = this.templateServiceUri + this.extendedPipelineTemplateResource; + return this.restClient.sendRequest( + { + url: requestUri, + method: 'GET', + headers: this.headers, + queryParameters: { + 'templateId': templateId, + 'templatePartToGet': 'parameters', + 'api-version': this.apiVersion + }, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async getTemplateConfiguration(templateId: string, inputs: StringMap): Promise { + const requestUri = this.templateServiceUri + this.extendedPipelineTemplateResource; + return this.restClient.sendRequest( + { + url: requestUri, + method: 'POST', + headers: this.headers, + queryParameters: { + 'templateId': templateId, + 'templatePartToGet': 'configuration', + 'api-version': this.apiVersion + }, + body: inputs, + deserializationMapper: null, + serializationMapper: null + }); + } + + public async getTemplateFile(templateId: string, fileName: string): Promise<{ id: string, content: string }[]> { + const requestUri = this.templateServiceUri + this.templateAssetFilesResource; + + return this.restClient.sendRequest( + { + url: requestUri, + method: 'GET', + headers: this.headers, + queryParameters: { + 'templateId': templateId, + 'fileNames': fileName, + 'api-version': this.apiVersion + }, + deserializationMapper: null, + serializationMapper: null + }); + } + +} \ No newline at end of file diff --git a/Source/configure/clients/github/githubClient.ts b/Source/configure/clients/github/githubClient.ts new file mode 100644 index 00000000..082d5d6b --- /dev/null +++ b/Source/configure/clients/github/githubClient.ts @@ -0,0 +1,156 @@ +import { UrlBasedRequestPrepareOptions } from 'ms-rest'; +import { stringCompareFunction } from "../../helper/commonHelper"; +import { GitHubProvider } from "../../helper/gitHubHelper"; +import { SodiumLibHelper } from '../../helper/sodium/SodiumLibHelper'; +import { GitHubOrganization, GitHubRepo } from '../../model/models'; +import { Messages } from '../../resources/messages'; +import { WhiteListedError } from '../../utilities/utilities'; +import { IUrlBasedRequestPrepareOptions2, RestClient } from "../restClient"; + +const UserAgent = "deploy-to-azure-vscode"; + +export class GithubClient { + + private patToken: string; + private url: string; + private listOrgPromise: Promise; + + constructor(patToken: string, remoteUrl: string) { + this.patToken = patToken; + this.url = remoteUrl; + } + + public async setRepoUrl(repoUrl: string) { + this.url = repoUrl; + } + + public async createOrUpdateGithubSecret(secretName: string, body: string): Promise { + const secretKeyObject: IGitHubSecretKey = await this._getGitHubSecretKey(); + const sodiumObj = new SodiumLibHelper(secretKeyObject.key); + const encryptedBytes: Uint8Array = sodiumObj.encrypt(body); + const encryptedBytesAsString: string = SodiumLibHelper.convertUint8ArrayToString(encryptedBytes); + const encryptedEncodedText = SodiumLibHelper.encodeToBase64(encryptedBytesAsString); + await this._setGithubSecret(secretName, secretKeyObject.key_id, encryptedEncodedText); + } + + public async createGithubRepo(orgName: string, repoName: string, isUserAccount: boolean = false): Promise { + const Url = isUserAccount ? "https://api.github.com/user/repos" : "https://api.github.com/orgs/" + orgName + "/repos"; + return this._sendRequest({ + url: Url, + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer " + this.patToken, + "User-Agent": UserAgent + }, + method: 'POST', + body: { + name: repoName, + description: "Repo created from VScode extension 'Deploy to Azure'", + homepage: "https://github.com", + private: true, + has_issues: true, + has_projects: true, + has_wiki: true + }, + deserializationMapper: null, + serializationMapper: null, + }) + .then((detail: GitHubRepo) => { + return detail; + }).catch((error) => { + if (error.response.statusCode === 422) { + return null; + } + throw new Error(JSON.parse(error.response.body).message); + }); + } + + public async listOrganizations(forceRefresh?: boolean): Promise { + if (!this.listOrgPromise || forceRefresh) { + this.listOrgPromise = Promise.all([ + this._sendRequest({ + url: "https://api.github.com/user/orgs", + method: 'GET', + headers: { + "Authorization": "Bearer " + this.patToken, + "User-Agent": UserAgent, + "Content-Type": "application/json", + "Accept": "*/*" + }, + deserializationMapper: null, + serializationMapper: null + }), + this._sendRequest({ + url: "https://api.github.com/user", + method: 'GET', + headers: { + "Authorization": "Bearer " + this.patToken, + "User-Agent": UserAgent, + "Content-Type": "application/json", + "Accept": "*/*" + }, + deserializationMapper: null, + serializationMapper: null + }) + ]) + .then(([organizations, userInfo]) => { + (userInfo as GitHubOrganization).isUserAccount = true; + return ((organizations as GitHubOrganization[]).concat(userInfo as GitHubOrganization)).sort((org1, org2) => stringCompareFunction(org1.login, org2.login)); + }); + } + return this.listOrgPromise; + } + + public async _getGitHubSecretKey(): Promise { + const request = { + url: GitHubProvider.getFormattedGitHubApiUrlBase(this.url) + "/actions/secrets/public-key", + method: 'GET', + headers: { + "User-Agent": UserAgent, + "Content-Type": "application/json", + "Authorization": "Bearer " + this.patToken, + "Accept": "*/*" + }, + serializationMapper: null, + deserializationMapper: null + }; + return (await this._sendRequest(request)) as IGitHubSecretKey; + } + + public async _setGithubSecret(secretName: string, key_id: string, encrypted_secret: string): Promise { + const request = { + url: GitHubProvider.getFormattedGitHubApiUrlBase(this.url) + "/actions/secrets/" + secretName, + headers: { + "User-Agent": UserAgent, + "Content-Type": "application/json", + "Authorization": "Bearer " + this.patToken, + "Accept": "*/*" + }, + method: "PUT", + deserializationMapper: null, + serializationMapper: null, + body: { + encrypted_value: encrypted_secret, + key_id: key_id + } + }; + await this._sendRequest(request); + } + + private _sendRequest(request: IUrlBasedRequestPrepareOptions2): Promise<{}> { + const restClient = new RestClient(); + return restClient.sendRequest({ ...request, returnFullResponseForFailure: true }) + .catch((error) => { + if (error.response.statusCode === 401 || error.response.statusCode === 403) { + throw new WhiteListedError(Messages.GitHubPatInvalid); + } + + throw error; + }); + } +} + +export interface IGitHubSecretKey { + key_id: string; + key: string; +} diff --git a/Source/configure/clients/modaRepositoryAnalysisClient.ts b/Source/configure/clients/modaRepositoryAnalysisClient.ts new file mode 100644 index 00000000..cea8e498 --- /dev/null +++ b/Source/configure/clients/modaRepositoryAnalysisClient.ts @@ -0,0 +1,27 @@ +import { RepositoryAnalysis, SourceRepository } from "azureintegration-repoanalysis-client-internal"; +import { RestClient } from "typed-rest-client"; +import vscodeUri from "vscode-uri"; +import { IRepositoryAnalysisClient } from "./repositoryAnalyisClient"; + +export class ModaRepositoryAnalysisClient implements IRepositoryAnalysisClient { + private restClient: RestClient; + private githubPat: string; + private pathUrl: string; + + constructor(url: string, githubPat: string) { + const u = vscodeUri.parse(url); + this.restClient = new RestClient("deploy-to-azure", u.scheme + "://" + u.authority); + this.pathUrl = u.path; + this.githubPat = githubPat; + } + + public async getRepositoryAnalysis(body: SourceRepository): Promise { + const requestOptions = { + acceptHeader: "application/json", + additionalHeaders: { + "Authorization": "Bearer " + this.githubPat + } + }; + return ((await this.restClient.create(this.pathUrl, body, requestOptions)).result); + } +} diff --git a/Source/configure/clients/portalExtensionRepositoryAnalysisClient.ts b/Source/configure/clients/portalExtensionRepositoryAnalysisClient.ts new file mode 100644 index 00000000..ba30c3dc --- /dev/null +++ b/Source/configure/clients/portalExtensionRepositoryAnalysisClient.ts @@ -0,0 +1,27 @@ +import { RepositoryAnalysis, SourceRepository } from "azureintegration-repoanalysis-client-internal"; +import { ServiceClientCredentials, UrlBasedRequestPrepareOptions } from "ms-rest"; +import { IRepositoryAnalysisClient } from "./repositoryAnalyisClient"; +import { RestClient } from "./restClient"; + +export class PortalExtensionRepositoryAnalysisClient implements IRepositoryAnalysisClient { + private restClient: RestClient; + private url: string; + constructor(url: string, credentials: ServiceClientCredentials) { + this.restClient = new RestClient(credentials); + this.url = url; + } + + public async getRepositoryAnalysis(body: SourceRepository): Promise { + + return this.restClient.sendRequest({ + url: this.url, + headers: { + "Content-Type": "application/json" + }, + method: "POST", + body: body, + serializationMapper: null, + deserializationMapper: null + }); + } +} diff --git a/Source/configure/clients/provisioningServiceClientFactory.ts b/Source/configure/clients/provisioningServiceClientFactory.ts new file mode 100644 index 00000000..3e8288ef --- /dev/null +++ b/Source/configure/clients/provisioningServiceClientFactory.ts @@ -0,0 +1,39 @@ +import { ServiceClientCredentials, TokenCredentials } from "ms-rest"; +import { RemoteServiceUrlHelper, ServiceFramework } from "../helper/remoteServiceUrlHelper"; +import { Messages } from '../resources/messages'; +import { IProvisioningServiceClient } from "./IProvisioningServiceClient"; +import { ProvisioningServiceClient } from "./ProvisioningServiceClient"; + + +export class ProvisioningServiceClientFactory { + public static async getClient(): Promise { + if (!!this.client) { + return this.client; + } + + if (!this.githubPatToken || !this.credentials) { + throw new Error(Messages.UndefinedClientCredentials); + } + + const defaultHeaders: { [propertyName: string]: string } = { "Content-Type": "application/json" }; + const serviceDefinition = await RemoteServiceUrlHelper.getProvisioningServiceDefinition(); + if (serviceDefinition.serviceFramework === ServiceFramework.Vssf) { + defaultHeaders["X-GITHUB-TOKEN"] = "token " + this.githubPatToken; + this.client = new ProvisioningServiceClient(serviceDefinition, defaultHeaders, this.credentials); + } else { + this.client = new ProvisioningServiceClient(serviceDefinition, defaultHeaders, new TokenCredentials(this.githubPatToken, "token")); + } + + return this.client; + } + + public static initialize(credentials: ServiceClientCredentials, githubPatToken: string): void { + this.client = null; + this.credentials = credentials; + this.githubPatToken = githubPatToken + } + + private static client: IProvisioningServiceClient; + private static credentials: ServiceClientCredentials; + private static githubPatToken: string; +} diff --git a/Source/configure/clients/repositoryAnalyisClient.ts b/Source/configure/clients/repositoryAnalyisClient.ts new file mode 100644 index 00000000..f097b9fb --- /dev/null +++ b/Source/configure/clients/repositoryAnalyisClient.ts @@ -0,0 +1,5 @@ +import { RepositoryAnalysis, SourceRepository } from "azureintegration-repoanalysis-client-internal"; + +export interface IRepositoryAnalysisClient { + getRepositoryAnalysis(body: SourceRepository): Promise; +} diff --git a/Source/configure/clients/restClient.ts b/Source/configure/clients/restClient.ts new file mode 100644 index 00000000..34ec4070 --- /dev/null +++ b/Source/configure/clients/restClient.ts @@ -0,0 +1,52 @@ +import { PathTemplateBasedRequestPrepareOptions, ServiceClient, ServiceClientCredentials, ServiceClientOptions, UrlBasedRequestPrepareOptions } from "ms-rest"; + +export interface IPathTemplateBasedRequestPrepareOptions2 extends PathTemplateBasedRequestPrepareOptions { + returnFullResponseForFailure?: boolean; +} + +export interface IUrlBasedRequestPrepareOptions2 extends UrlBasedRequestPrepareOptions { + returnFullResponseForFailure?: boolean; +} + +export class RestClient extends ServiceClient { + constructor(credentials?: ServiceClientCredentials, options?: ServiceClientOptions) { + super(credentials, options); + } + + public sendRequest(options: IPathTemplateBasedRequestPrepareOptions2 | IUrlBasedRequestPrepareOptions2): Promise { + return new Promise((resolve, reject) => { + super.sendRequestWithHttpOperationResponse(options) + .then((response) => { + if (response.response.statusCode >= 300) { + if (options.returnFullResponseForFailure === true) { + reject(response); + } + else { + reject(response.body); + } + } + resolve(response.body); + }) + .catch((error) => { + reject(error); + }); + }); + } + + public sendRequest2(url: string, httpMethod: string, apiVersion: string, body?: any): Promise { + return this.sendRequest( + { + url: url, + method: httpMethod, + headers: { + "Content-Type": "application/json; charset=utf-8" + }, + queryParameters: { + 'api-version': apiVersion + }, + body: body, + deserializationMapper: null, + serializationMapper: null + }); + } +} \ No newline at end of file diff --git a/Source/configure/configure.ts b/Source/configure/configure.ts new file mode 100644 index 00000000..e97dd64e --- /dev/null +++ b/Source/configure/configure.ts @@ -0,0 +1,766 @@ +import { GenericResource } from 'azure-arm-resource/lib/resource/models'; +import { ApplicationSettings, RepositoryAnalysis } from 'azureintegration-repoanalysis-client-internal'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import { UserCancelledError } from 'vscode-azureextensionui'; +import { AppServiceClient } from './clients/azure/appServiceClient'; +import { AzureResourceClient } from './clients/azure/azureResourceClient'; +import { ProvisioningServiceClientFactory } from './clients/provisioningServiceClientFactory'; +import { TemplateServiceClientFactory } from './clients/TemplateServiceClientFactory'; +import { Configurer } from './configurers/configurerBase'; +import { ConfigurerFactory } from './configurers/configurerFactory'; +import { ProvisioningConfigurer } from './configurers/provisioningConfigurer'; +import { RemoteGitHubWorkflowConfigurer } from './configurers/remoteGitHubWorkflowConfigurer'; +import { ResourceSelectorFactory } from './configurers/ResourceSelectorFactory'; +import { AssetHandler } from './helper/AssetHandler'; +import { getAzureSession, getSubscriptionSession } from './helper/azureSessionHelper'; +import { ControlProvider } from './helper/controlProvider'; +import { AzureDevOpsHelper } from './helper/devOps/azureDevOpsHelper'; +import { GitHubProvider } from './helper/gitHubHelper'; +import { LocalGitRepoHelper } from './helper/LocalGitRepoHelper'; +import { RepoAnalysisHelper } from './helper/repoAnalysisHelper'; +import { Result, telemetryHelper } from './helper/telemetryHelper'; +import * as templateHelper from './helper/templateHelper'; +import { TemplateParameterHelper } from './helper/templateParameterHelper'; +import { ConfigurationStage } from './model/Contracts'; +import { extensionVariables, GitBranchDetails, GitRepositoryParameters, IResourceNode, MustacheContext, ParsedAzureResourceId, PipelineType, QuickPickItemWithData, RepositoryProvider, SourceOptions, StringMap, TargetResourceType, WizardInputs } from './model/models'; +import { DraftPipelineConfiguration, ProvisioningConfiguration, provisioningMode } from './model/provisioningConfiguration'; +import { LocalPipelineTemplate, PipelineTemplate, RemotePipelineTemplate, TemplateAssetType, TemplateType } from './model/templateModels'; +import * as constants from './resources/constants'; +import { Messages } from './resources/messages'; +import { TelemetryKeys } from './resources/telemetryKeys'; +import { TracePoints } from './resources/tracePoints'; +import { InputControlProvider } from './templateInputHelper/InputControlProvider'; +import { Utilities, WhiteListedError } from './utilities/utilities'; + +const uuid = require('uuid/v4'); + +const Layer: string = 'configure'; +export let UniqueResourceNameSuffix: string = uuid().substr(0, 5); + +export async function configurePipeline(node: IResourceNode | vscode.Uri) { + await telemetryHelper.executeFunctionWithTimeTelemetry(async () => { + try { + if (!(await extensionVariables.azureAccountExtensionApi.waitForLogin())) { + // set telemetry + telemetryHelper.setTelemetry(TelemetryKeys.AzureLoginRequired, 'true'); + + const loginOption = await vscode.window.showInformationMessage(Messages.azureLoginRequired, Messages.signInLabel, Messages.signUpLabel); + if (loginOption && loginOption.toLowerCase() === Messages.signInLabel.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.AzureLoginOption, 'SignIn'); + await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.waitForAzureSignIn }, + async () => { + await vscode.commands.executeCommand("azure-account.login"); + await extensionVariables.azureAccountExtensionApi.waitForSubscriptions(); + }); + } + else if (loginOption && loginOption.toLowerCase() === Messages.signUpLabel.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.AzureLoginOption, 'SignUp'); + await vscode.commands.executeCommand("azure-account.createAccount"); + return; + } + else { + const error = new UserCancelledError(Messages.azureLoginRequired); + throw error; + } + } + + const orchestrator = new Orchestrator(); + await orchestrator.configure(node); + } + catch (error) { + if (!(error instanceof UserCancelledError)) { + vscode.window.showErrorMessage(error.message); + extensionVariables.outputChannel.appendLine(error.message); + if (error instanceof WhiteListedError) { + telemetryHelper.setTelemetry(TelemetryKeys.IsErrorWhitelisted, "true"); + telemetryHelper.setResult(Result.Succeeded, error); + } else { + telemetryHelper.setResult(Result.Failed, error); + } + } + else { + telemetryHelper.setResult(Result.Canceled, error); + } + } + }, TelemetryKeys.CommandExecutionDuration); +} + +class Orchestrator { + private inputs: WizardInputs; + private localGitRepoHelper: LocalGitRepoHelper; + private azureResourceClient: AzureResourceClient; + private workspacePath: string; + private controlProvider: ControlProvider; + private continueOrchestration: boolean = true; + private context: StringMap = {}; + private pipelineType: PipelineType; + + public constructor() { + this.inputs = new WizardInputs(); + this.controlProvider = new ControlProvider(); + UniqueResourceNameSuffix = uuid().substr(0, 5); + this.context['isResourceAlreadySelected'] = false; + this.context['resourceId'] = ''; + } + + public async configure(node: IResourceNode | vscode.Uri): Promise { + telemetryHelper.setCurrentStep('GetAllRequiredInputs'); + await this.getInputs(node); + if (this.continueOrchestration) { + if (this.doesLanguageAndTargetSupportRemoteProvisioning()) { + return await this.configurePipelineRemotely(); + } + return this.ConfigurePipelineLocally(); + } + } + + private doesLanguageAndTargetSupportRemoteProvisioning(): boolean { + // This check is to enable for all remote repository webapps and aks flows to use remote provisioning service. + // Both template type as remote and remote url check is required because remote provisioning is only applicable for remote templates and there are scenario where template selected is remote but repo is not remote (in cases where resource is already selected) + return extensionVariables.remoteConfigurerEnabled === true && this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github && + (this.inputs.targetResource.resource.type === TargetResourceType.AKS || this.inputs.targetResource.resource.type === TargetResourceType.WebApp) && !!this.inputs.sourceRepository.remoteUrl && this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE; + } + + private async getAzureResource(targetType: TargetResourceType) { + const azureResourceSelector = ResourceSelectorFactory.getAzureResourceSelector(targetType); + this.inputs.targetResource.resource = await azureResourceSelector.getAzureResource(this.inputs); + this.azureResourceClient = ResourceSelectorFactory.getAzureResourceClient(targetType, this.inputs.azureSession.credentials, + this.inputs.azureSession.environment, this.inputs.azureSession.tenantId, this.inputs.subscriptionId); + telemetryHelper.setTelemetry(TelemetryKeys.resourceType, this.inputs.targetResource.resource.type); + if (targetType === TargetResourceType.WebApp) { + this.context['resourceId'] = this.inputs.targetResource.resource.id; + telemetryHelper.setTelemetry(TelemetryKeys.resourceKind, this.inputs.targetResource.resource.kind); + telemetryHelper.setTelemetry(TelemetryKeys.resourceIdHash, Utilities.createSha256Hash(this.inputs.targetResource.resource.id)); + } + } + + private async selectTemplate(resource: GenericResource): Promise { + switch (resource.type) { + case TargetResourceType.AKS: + if (extensionVariables.remoteConfigurerEnabled === true && this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github && !!this.inputs.sourceRepository.remoteUrl) { + this.inputs.pipelineConfiguration.template = this.inputs.potentialTemplates.find((template) => template.templateType === TemplateType.REMOTE); + } else { + this.inputs.pipelineConfiguration.template = this.inputs.potentialTemplates.find((template) => template.templateType === TemplateType.LOCAL); + } + if (this.inputs.pipelineConfiguration.template === undefined) { + telemetryHelper.logError(Layer, TracePoints.TemplateNotFound, new Error(Messages.TemplateNotFound + " RepoId: " + this.inputs.sourceRepository.repositoryId)); + throw new Error(Messages.TemplateNotFound); + } + break; + case TargetResourceType.WebApp: + let shortlistedTemplates = []; + shortlistedTemplates = this.inputs.potentialTemplates.filter((template) => template.targetKind === resource.kind); + if (!!shortlistedTemplates && shortlistedTemplates.length > 1) { + this.inputs.pipelineConfiguration.template = shortlistedTemplates.find((template) => template.templateType === TemplateType.REMOTE); + } + else if (!!shortlistedTemplates) { + this.inputs.pipelineConfiguration.template = shortlistedTemplates[0]; + } + else { + telemetryHelper.logError(Layer, TracePoints.TemplateNotFound, new Error(Messages.TemplateNotFound + " RepoId: " + this.inputs.sourceRepository.repositoryId)); + throw new Error(Messages.TemplateNotFound); + } + break; + + default: + throw new Error(Messages.ResourceNotSupported); + } + } + + private setPipelineType() { + if (this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github && extensionVariables.enableGitHubWorkflow) { + this.pipelineType = PipelineType.GitHubPipeline; + } else { + this.pipelineType = PipelineType.AzurePipeline; + } + } + + private isResourceAlreadySelected(): boolean { + return this.context['isResourceAlreadySelected']; + } + + private async getInputs(node: IResourceNode | vscode.Uri): Promise { + telemetryHelper.setTelemetry(TelemetryKeys.FF_UseGithubForCreatingNewRepository, + vscode.workspace.getConfiguration().get('deployToAzure.UseGithubForCreatingNewRepository')); + telemetryHelper.setTelemetry(TelemetryKeys.FF_UseAzurePipelinesForGithub, + vscode.workspace.getConfiguration().get('deployToAzure.UseAzurePipelinesForGithub')); + + await this.analyzeNode(node); + + if (this.continueOrchestration) { + await this.getSourceRepositoryDetails(); + if (!this.inputs.azureSession) { + this.inputs.azureSession = await getAzureSession(); + } + + // Right click scenario not supported for Azure and local repo + if (this.isResourceAlreadySelected()) { + if (this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.AzureRepos || extensionVariables.isLocalRepo) { + throw new WhiteListedError(Messages.GithubRepoRequired); + } else if (!extensionVariables.enableGitHubWorkflow) { + // For github repo, we create a github pipeline + extensionVariables.enableGitHubWorkflow = true; + } + } + + const repoAnalysisResult = await this.getRepositoryAnalysis(); + + this.initializeClientFactories(); + this.setPipelineType(); + await this.getTemplatesByRepoAnalysis(repoAnalysisResult); + try { + if (!this.isResourceAlreadySelected()) { + await this.getAzureSubscription(); + await this.getAzureResource(this.getSelectedPipelineTargetType()); + } + + this.selectTemplate(this.inputs.targetResource.resource); + telemetryHelper.setTelemetry(TelemetryKeys.SelectedTemplate, this.inputs.pipelineConfiguration.template.label); + telemetryHelper.setTelemetry(TelemetryKeys.SelectedTemplateType, (this.inputs.pipelineConfiguration.template.templateType).toString()); + + await this.updateRepositoryAnalysisApplicationSettings(repoAnalysisResult); + + await this.getTemplateParameters(); + } + catch (err) { + if (err.message === Messages.setupAlreadyConfigured) { + this.continueOrchestration = false; + return; + } + else { + throw err; + } + } + } + } + + private async getTemplateParameters() { + if (this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE) { + const template = this.inputs.pipelineConfiguration.template as RemotePipelineTemplate; + const extendedPipelineTemplate = await templateHelper.getTemplateParameters(template.id); + template.attributes = extendedPipelineTemplate.attributes; + template.parameters = extendedPipelineTemplate.parameters; + const controlProvider = new InputControlProvider(this.inputs.azureSession, extendedPipelineTemplate, this.context); + this.inputs.pipelineConfiguration.params = await controlProvider.getAllPipelineTemplateInputs(); + } + else if (this.inputs.pipelineConfiguration.template.targetType === TargetResourceType.AKS && this.inputs.sourceRepository.repositoryId != RepositoryProvider.Github) { + const templateParameterHelper = await new TemplateParameterHelper(); + const template = this.inputs.pipelineConfiguration.template as LocalPipelineTemplate; + await templateParameterHelper.setParameters(template.parameters, this.inputs); + } + } + + private async getGithubPatToken(): Promise { + if (this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github) { + this.inputs.githubPATToken = await this.controlProvider.showInputBox(constants.GitHubPat, { + placeHolder: Messages.enterGitHubPat, + prompt: Messages.githubPatTokenHelpMessage, + validateInput: (inputValue) => { + return !inputValue ? Messages.githubPatTokenErrorMessage : null; + } + }); + } + } + + private async getRepositoryAnalysis() { + if (this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github) { + await this.getGithubPatToken(); + return await vscode.window.withProgress( + { location: vscode.ProgressLocation.Notification, title: Messages.AnalyzingRepo }, + () => telemetryHelper.executeFunctionWithTimeTelemetry(async () => { + return await new RepoAnalysisHelper(this.inputs.azureSession, this.inputs.githubPATToken).getRepositoryAnalysis( + this.inputs.sourceRepository, this.inputs.pipelineConfiguration.workingDirectory.split('/').join('\\')); + }, TelemetryKeys.RepositoryAnalysisDuration) + ); + } + return null; + } + + private getSelectedPipelineTargetType(): TargetResourceType { + return this.inputs.potentialTemplates[0].targetType; + } + + private async analyzeNode(node: IResourceNode | vscode.Uri): Promise { + if (!!node) { + const folderNode = node as vscode.Uri; + if (!!(folderNode.fsPath)) { + // right click on a folder + this.workspacePath = folderNode.fsPath; + telemetryHelper.setTelemetry(TelemetryKeys.SourceRepoLocation, SourceOptions.CurrentWorkspace); + } else if (await this.extractAzureResourceFromNode(node as IResourceNode)) { + // right click on a resource + this.context['isResourceAlreadySelected'] = true; + this.context['resourceId'] = this.inputs.targetResource.resource.id; + } + } + } + + private async getSourceRepositoryDetails(): Promise { + try { + if (!this.workspacePath) { // This is to handle when we have already identified the repository details. + await this.setWorkspace(); + } + await this.getGitDetailsFromRepository(); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.GetSourceRepositoryDetailsFailed, error); + throw error; + } + } + + private async setWorkspace(): Promise { + const workspaceFolders = vscode.workspace && vscode.workspace.workspaceFolders; + if (workspaceFolders && workspaceFolders.length > 0) { + telemetryHelper.setTelemetry(TelemetryKeys.SourceRepoLocation, SourceOptions.CurrentWorkspace); + + if (workspaceFolders.length === 1) { + telemetryHelper.setTelemetry(TelemetryKeys.MultipleWorkspaceFolders, 'false'); + this.workspacePath = workspaceFolders[0].uri.fsPath; + } + else { + telemetryHelper.setTelemetry(TelemetryKeys.MultipleWorkspaceFolders, 'true'); + const workspaceFolderOptions: QuickPickItemWithData[] = []; + for (const folder of workspaceFolders) { + workspaceFolderOptions.push({ label: folder.name, data: folder }); + } + const selectedWorkspaceFolder = await this.controlProvider.showQuickPick( + constants.SelectFromMultipleWorkSpace, + workspaceFolderOptions, + { placeHolder: Messages.selectWorkspaceFolder }); + this.workspacePath = selectedWorkspaceFolder.data.uri.fsPath; + } + } + else { + telemetryHelper.setTelemetry(TelemetryKeys.SourceRepoLocation, SourceOptions.BrowseLocalMachine); + const selectedFolder: vscode.Uri[] = await vscode.window.showOpenDialog( + { + openLabel: Messages.selectFolderLabel, + canSelectFiles: false, + canSelectFolders: true, + canSelectMany: false, + } + ); + if (selectedFolder && selectedFolder.length > 0) { + this.workspacePath = selectedFolder[0].fsPath; + } + else { + throw new UserCancelledError(Messages.noWorkSpaceSelectedError); + } + } + } + + private async getGitDetailsFromRepository(): Promise { + this.localGitRepoHelper = LocalGitRepoHelper.GetHelperInstance(this.workspacePath); + const isGitRepository = await this.localGitRepoHelper.isGitRepository(); + + if (isGitRepository) { + const gitBranchDetails = await this.localGitRepoHelper.getGitBranchDetails(); + + if (!gitBranchDetails.remoteName) { + // Remote tracking branch is not set + const remotes = await this.localGitRepoHelper.getGitRemotes(); + if (remotes.length === 0) { + this.setDefaultRepositoryDetails(); + } + else if (remotes.length === 1) { + gitBranchDetails.remoteName = remotes[0].name; + } + else { + // Show an option to user to select remote to be configured + const selectedRemote = await this.controlProvider.showQuickPick( + constants.SelectRemoteForRepo, + remotes.map((remote) => ({ label: remote.name })), + { placeHolder: Messages.selectRemoteForBranch }); + gitBranchDetails.remoteName = selectedRemote.label; + } + } + + // Set working directory relative to repository root + const gitRootDir = await this.localGitRepoHelper.getGitRootDirectory(); + this.inputs.pipelineConfiguration.workingDirectory = path.relative(gitRootDir, this.workspacePath).split(path.sep).join('/'); + + if (this.inputs.pipelineConfiguration.workingDirectory === "") { + this.inputs.pipelineConfiguration.workingDirectory = "."; + } + + this.inputs.sourceRepository = this.inputs.sourceRepository ? this.inputs.sourceRepository : await this.getGitRepositoryParameters(gitBranchDetails); + } + else { + this.setDefaultRepositoryDetails(); + } + // set telemetry + telemetryHelper.setTelemetry(TelemetryKeys.RepoProvider, this.inputs.sourceRepository.repositoryProvider); + telemetryHelper.setTelemetry(TelemetryKeys.RepoId, this.inputs.sourceRepository.repositoryId); + } + + private setDefaultRepositoryDetails(): void { + extensionVariables.isLocalRepo = true; + this.inputs.pipelineConfiguration.workingDirectory = '.'; + this.inputs.sourceRepository = { + branch: 'master', + commitId: '', + localPath: this.workspacePath, + remoteName: 'origin', + remoteUrl: '', + repositoryId: '', + repositoryName: '', + repositoryProvider: vscode.workspace.getConfiguration().get('deployToAzure.UseGithubForCreatingNewRepository') ? RepositoryProvider.Github : RepositoryProvider.AzureRepos, + }; + } + + private async getGitRepositoryParameters(gitRepositoryDetails: GitBranchDetails): Promise { + const remoteUrl = await this.localGitRepoHelper.getGitRemoteUrl(gitRepositoryDetails.remoteName); + + if (remoteUrl) { + if (AzureDevOpsHelper.isAzureReposUrl(remoteUrl)) { + return { + repositoryProvider: RepositoryProvider.AzureRepos, + repositoryId: "", + repositoryName: AzureDevOpsHelper.getRepositoryDetailsFromRemoteUrl(remoteUrl).repositoryName, + remoteName: gitRepositoryDetails.remoteName, + remoteUrl, + branch: gitRepositoryDetails.branch, + commitId: "", + localPath: this.workspacePath + }; + } + else if (GitHubProvider.isGitHubUrl(remoteUrl)) { + const repoId = GitHubProvider.getRepositoryIdFromUrl(remoteUrl); + return { + repositoryProvider: RepositoryProvider.Github, + repositoryId: repoId, + repositoryName: repoId, + remoteName: gitRepositoryDetails.remoteName, + remoteUrl, + branch: gitRepositoryDetails.branch, + commitId: "", + localPath: this.workspacePath + }; + } + else { + let repositoryProvider: string; + + if (remoteUrl.indexOf("bitbucket.org") >= 0) { + repositoryProvider = "Bitbucket"; + } + else if (remoteUrl.indexOf("gitlab.com") >= 0) { + repositoryProvider = "GitLab"; + } + else { + repositoryProvider = remoteUrl; + } + + telemetryHelper.setTelemetry(TelemetryKeys.RepoProvider, repositoryProvider); + throw new WhiteListedError(Messages.cannotIdentifyRespositoryDetails); + } + } + else { + throw new Error(Messages.remoteRepositoryNotConfigured); + } + } + + private async extractAzureResourceFromNode(node: IResourceNode): Promise { + if (!!node.resource.id && node.resource.type != 'cluster') { + this.inputs.subscriptionId = node.subscriptionId; + this.inputs.azureSession = await getSubscriptionSession(this.inputs.subscriptionId); + this.azureResourceClient = new AppServiceClient(this.inputs.azureSession.credentials, this.inputs.azureSession.environment, this.inputs.azureSession.tenantId, this.inputs.subscriptionId); + + try { + const azureResource: GenericResource = await (this.azureResourceClient as AppServiceClient).getAppServiceResource(node.resource.id); + telemetryHelper.setTelemetry(TelemetryKeys.resourceType, azureResource.type); + telemetryHelper.setTelemetry(TelemetryKeys.resourceKind, azureResource.kind); + AzureResourceClient.validateTargetResourceType(azureResource); + if (azureResource.type.toLowerCase() === TargetResourceType.WebApp.toLowerCase()) { + if (await (this.azureResourceClient as AppServiceClient).isScmTypeSet(node.resource.id)) { + this.continueOrchestration = false; + await openBrowseExperience(node.resource.id); + } + } + + this.inputs.targetResource.resource = azureResource; + return true; + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.ExtractAzureResourceFromNodeFailed, error); + throw error; + } + } + else if (!!node.resource.id && node.resource.type === "cluster") { + this.inputs.subscriptionId = node.subscriptionId; + this.context['subscriptionId'] = this.inputs.subscriptionId; + this.inputs.azureSession = await getSubscriptionSession(this.inputs.subscriptionId); + this.azureResourceClient = new AzureResourceClient(this.inputs.azureSession.credentials, this.inputs.subscriptionId); + const cluster = await this.azureResourceClient.getResource(node.resource.id, '2019-08-01'); + telemetryHelper.setTelemetry(TelemetryKeys.resourceType, cluster.type); + AzureResourceClient.validateTargetResourceType(cluster); + cluster["parsedResourceId"] = new ParsedAzureResourceId(cluster.id); + this.inputs.targetResource.resource = cluster; + return true; + } + return false; + } + + private async getAzureSubscription(): Promise { + // show available subscriptions and get the chosen one + const subscriptionList = extensionVariables.azureAccountExtensionApi.filters.map((subscriptionObject) => { + return { + label: `${subscriptionObject.subscription.displayName}`, + data: subscriptionObject, + description: `${subscriptionObject.subscription.subscriptionId}` + }; + }); + const selectedSubscription: QuickPickItemWithData = await this.controlProvider.showQuickPick(constants.SelectSubscription, subscriptionList, { placeHolder: Messages.selectSubscription }, TelemetryKeys.SubscriptionListCount); + this.inputs.subscriptionId = selectedSubscription.data.subscription.subscriptionId; + this.context['subscriptionId'] = this.inputs.subscriptionId; + this.inputs.azureSession = await getSubscriptionSession(this.inputs.subscriptionId); + telemetryHelper.setTelemetry(TelemetryKeys.SubscriptionId, this.inputs.subscriptionId); + } + + private async getTemplatesByRepoAnalysis(repoAnalysisResult: RepositoryAnalysis): Promise { + + let appropriatePipelines: PipelineTemplate[] = []; + const remotePipelines: PipelineTemplate[] = await vscode.window.withProgress( + { location: vscode.ProgressLocation.Notification, title: Messages.fetchingTemplates }, + () => templateHelper.analyzeRepoAndListAppropriatePipeline2( + this.inputs.azureSession, + this.inputs.sourceRepository, + this.pipelineType, + repoAnalysisResult, + this.inputs.githubPATToken, + this.inputs.targetResource.resource) + ); + appropriatePipelines = remotePipelines; + + const pipelineMap = this.getMapOfUniqueLabels(appropriatePipelines); + const pipelineLabels = Array.from(pipelineMap.keys()); + if (pipelineLabels.length === 0) { + telemetryHelper.setTelemetry(TelemetryKeys.UnsupportedLanguage, Messages.languageNotSupported); + throw new Error(Messages.languageNotSupported); + } + + if (pipelineLabels.length > 1) { + const selectedOption = await this.controlProvider.showQuickPick( + constants.SelectPipelineTemplate, + pipelineLabels.map((pipeline) => ({ label: pipeline })), + { placeHolder: Messages.selectPipelineTemplate }, + TelemetryKeys.PipelineTempateListCount); + // only label gets finalized, template isn't final yet + this.inputs.potentialTemplates = pipelineMap.get(selectedOption.label); + } + else { + this.inputs.potentialTemplates = pipelineMap.get(pipelineLabels[0]); + } + } + + private getMapOfUniqueLabels(pipelines: PipelineTemplate[]): Map { + const pipelineMap: Map = new Map(); + if (!!pipelines) { + pipelines.forEach((element) => { + if (pipelineMap.has(element.label)) { + pipelineMap.get(element.label).push(element); + } + else { + pipelineMap.set(element.label, [element]); + } + }); + } + return pipelineMap; + } + + private async updateRepositoryAnalysisApplicationSettings(repoAnalysisResult: RepositoryAnalysis): Promise { + // If RepoAnalysis is disabled or didn't provided response related to language of selected template + this.inputs.repositoryAnalysisApplicationSettings = {} as ApplicationSettings; + + if (!repoAnalysisResult || !repoAnalysisResult.applicationSettingsList) { + return; + } + + let applicationSettings: ApplicationSettings[] = repoAnalysisResult.applicationSettingsList; + if (!this.isResourceAlreadySelected()) { + const workingDirectories = Array.from(new Set(this.inputs.potentialTemplates + .filter((template) => template.templateType === TemplateType.REMOTE) + .map((template: RemotePipelineTemplate) => template.workingDirectory.toLowerCase()))); + applicationSettings = repoAnalysisResult.applicationSettingsList.filter((applicationSetting) => { + if (this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE) { + return workingDirectories.indexOf(applicationSetting.settings.workingDirectory.toLowerCase()) >= 0; + } + return applicationSetting.language === this.inputs.pipelineConfiguration.template.language; + + }); + } + this.context['repoAnalysisSettings'] = applicationSettings; + + if (!applicationSettings || applicationSettings.length === 0 || + this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE) { + return; + } + + if (applicationSettings.length === 1) { + this.inputs.repositoryAnalysisApplicationSettings = applicationSettings[0]; + this.inputs.pipelineConfiguration.workingDirectory = applicationSettings[0].settings.workingDirectory; + return; + } + + const workspacePaths = Array.from(new Set(applicationSettings.map((a) => a.settings.workingDirectory))); + const workspacePathQuickPickItemList: QuickPickItemWithData[] = []; + for (const workspacePath of workspacePaths) { + workspacePathQuickPickItemList.push({ label: workspacePath, data: workspacePath }); + } + const selectedWorkspacePathItem = await this.controlProvider.showQuickPick( + constants.SelectWorkspace, + workspacePathQuickPickItemList, + { placeHolder: Messages.selectWorkspace }); + + this.inputs.pipelineConfiguration.workingDirectory = selectedWorkspacePathItem.data; + this.inputs.repositoryAnalysisApplicationSettings = + repoAnalysisResult.applicationSettingsList.find((applicationSettings) => { + return (applicationSettings.language === this.inputs.pipelineConfiguration.template.language + && applicationSettings.settings.workingDirectory === selectedWorkspacePathItem.data); + }); + } + + private async checkInPipelineFileToRepository(pipelineConfigurer: Configurer): Promise { + let filesToCommit: string[] = []; + if (this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE) { + filesToCommit = await (pipelineConfigurer as RemoteGitHubWorkflowConfigurer).getPipelineFilesToCommit(this.inputs); + } + else { + try { + const mustacheContext = new MustacheContext(this.inputs); + if (this.inputs.pipelineConfiguration.template.targetType === TargetResourceType.AKS) { + try { + await this.localGitRepoHelper.createAndDisplayManifestFile(constants.deploymentManifest, pipelineConfigurer, filesToCommit, this.inputs); + const properties = this.inputs.pipelineConfiguration.params.aksCluster.properties; + if (properties.addonProfiles && properties.addonProfiles.httpApplicationRouting && properties.addonProfiles.httpApplicationRouting.enabled) { + await this.localGitRepoHelper.createAndDisplayManifestFile(constants.serviceIngressManifest, pipelineConfigurer, filesToCommit, this.inputs, constants.serviceManifest); + await this.localGitRepoHelper.createAndDisplayManifestFile(constants.ingressManifest, pipelineConfigurer, filesToCommit, this.inputs); + } + else { + await this.localGitRepoHelper.createAndDisplayManifestFile(constants.serviceManifest, pipelineConfigurer, filesToCommit, this.inputs); + } + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CreatingManifestsFailed, error); + throw error; + } + } + + this.inputs.pipelineConfiguration.filePath = await pipelineConfigurer.getPathToPipelineFile(this.inputs, this.localGitRepoHelper); + filesToCommit.push(this.inputs.pipelineConfiguration.filePath); + await this.localGitRepoHelper.addContentToFile( + await templateHelper.renderContent((this.inputs.pipelineConfiguration.template as LocalPipelineTemplate).path, mustacheContext), + this.inputs.pipelineConfiguration.filePath); + await vscode.window.showTextDocument(vscode.Uri.file(this.inputs.pipelineConfiguration.filePath)); + telemetryHelper.setTelemetry(TelemetryKeys.DisplayWorkflow, 'true'); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AddingContentToPipelineFileFailed, error); + throw error; + } + } + + try { + await pipelineConfigurer.checkInPipelineFilesToRepository(filesToCommit, this.inputs, this.localGitRepoHelper); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.PipelineFileCheckInFailed, error); + throw error; + } + } + + private createProvisioningConfigurationObject(templateId: string, branch: string, pipeplineTemplateParameters: { [key: string]: string }, mode: provisioningMode): ProvisioningConfiguration { + return { + id: null, + branch, + pipelineTemplateId: templateId, + pipelineTemplateParameters: pipeplineTemplateParameters, + provisioningMode: mode, + } as ProvisioningConfiguration; + } + + private async configurePipelineRemotely(): Promise { + const provisioningConfigurer = new ProvisioningConfigurer(this.localGitRepoHelper); + const template = this.inputs.pipelineConfiguration.template as RemotePipelineTemplate; + try { + // prerequisite params + telemetryHelper.setCurrentStep('ConfiguringPreRequisiteParams'); + await provisioningConfigurer.createPreRequisiteParams(this.inputs); + + // Draft pipeline step + telemetryHelper.setCurrentStep('ConfiguringDraftProvisioningPipeline'); + const provisioningConfiguration = this.createProvisioningConfigurationObject(template.id, this.inputs.sourceRepository.branch, this.inputs.pipelineConfiguration.params, provisioningMode.draft); + const draftProvisioningPipeline: ProvisioningConfiguration = await provisioningConfigurer.preSteps(provisioningConfiguration, this.inputs); + + // After recieving the draft workflow files, show them to user and confirm to checkin + telemetryHelper.setCurrentStep('ConfiguringCompleteProvisioningPipeline'); + provisioningConfiguration.provisioningMode = provisioningMode.complete; + await provisioningConfigurer.postSteps(provisioningConfiguration, (draftProvisioningPipeline.result.pipelineConfiguration as DraftPipelineConfiguration), this.inputs); + + // All done, now browse the pipeline + telemetryHelper.setCurrentStep('BrowsingPipeline'); + await provisioningConfigurer.browseQueuedWorkflow(); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.RemotePipelineConfiguringFailed, error); + throw error; + } + } + + private async ConfigurePipelineLocally() { + const pipelineConfigurer = ConfigurerFactory.GetConfigurer(this.inputs.sourceRepository, this.inputs.azureSession, + this.inputs.pipelineConfiguration.template.templateType, this.localGitRepoHelper); + const selectedCICDProvider = (pipelineConfigurer.constructor.name === "AzurePipelineConfigurer") ? constants.azurePipeline : constants.githubWorkflow; + telemetryHelper.setTelemetry(TelemetryKeys.SelectedCICDProvider, selectedCICDProvider); + await pipelineConfigurer.getInputs(this.inputs); + telemetryHelper.setCurrentStep('CreatePreRequisites'); + await pipelineConfigurer.createPreRequisites(this.inputs, !!this.azureResourceClient ? this.azureResourceClient : new AppServiceClient(this.inputs.azureSession.credentials, this.inputs.azureSession.environment, this.inputs.azureSession.tenantId, this.inputs.subscriptionId)); + + telemetryHelper.setCurrentStep('CreateAssets'); + if (this.inputs.pipelineConfiguration.template.templateType === TemplateType.REMOTE && this.inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github && extensionVariables.enableGitHubWorkflow) { + await (pipelineConfigurer as RemoteGitHubWorkflowConfigurer).createAssets(ConfigurationStage.Pre); + } else { + await new AssetHandler().createAssets((this.inputs.pipelineConfiguration.template as LocalPipelineTemplate).assets, this.inputs, (name: string, assetType: TemplateAssetType, data: any, inputs: WizardInputs) => pipelineConfigurer.createAsset(name, assetType, data, inputs)); + } + + telemetryHelper.setCurrentStep('CheckInPipeline'); + await this.checkInPipelineFileToRepository(pipelineConfigurer); + + telemetryHelper.setCurrentStep('CreateAndRunPipeline'); + await pipelineConfigurer.createAndQueuePipeline(this.inputs); + + telemetryHelper.setCurrentStep('PostPipelineCreation'); + // This step should be determined by the resoruce target provider (azure app service, function app, aks) type and pipelineProvider(azure pipeline vs github) + pipelineConfigurer.executePostPipelineCreationSteps(this.inputs, this.azureResourceClient ? this.azureResourceClient : new AzureResourceClient(this.inputs.azureSession.credentials, this.inputs.subscriptionId)); + + telemetryHelper.setCurrentStep('DisplayCreatedPipeline'); + pipelineConfigurer.browseQueuedPipeline(); + } + + private initializeClientFactories(): void { + TemplateServiceClientFactory.initialize(this.inputs.azureSession.credentials, this.inputs.githubPATToken); + ProvisioningServiceClientFactory.initialize(this.inputs.azureSession.credentials, this.inputs.githubPATToken); + } +} + +export async function openBrowseExperience(resourceId: string): Promise { + try { + // if pipeline is already setup, the ask the user if we should continue. + telemetryHelper.setTelemetry(TelemetryKeys.PipelineAlreadyConfigured, 'true'); + + const browsePipelineAction = await new ControlProvider().showInformationBox( + constants.SetupAlreadyExists, + Messages.setupAlreadyConfigured, + constants.Browse); + + if (browsePipelineAction === constants.Browse) { + vscode.commands.executeCommand('browse-cicd-pipeline', { fullId: resourceId }); + } + } + catch (err) { + if (!(err instanceof UserCancelledError)) { + throw err; + } + } + + telemetryHelper.setResult(Result.Succeeded); +} diff --git a/Source/configure/configurers/AksAzureResourceSelector.ts b/Source/configure/configurers/AksAzureResourceSelector.ts new file mode 100644 index 00000000..ffdf9e32 --- /dev/null +++ b/Source/configure/configurers/AksAzureResourceSelector.ts @@ -0,0 +1,12 @@ +import { GenericResource } from "azure-arm-resource/lib/resource/models"; +import { TargetResourceType } from "../model/models"; +import { IAzureResourceSelector } from "./IAzureResourceSelector"; + +export class AksAzureResourceSelector implements IAzureResourceSelector { + async getAzureResource(inputs): Promise { + return { type: TargetResourceType.AKS }; + + } + +} + diff --git a/Source/configure/configurers/IAzureResourceSelector.ts b/Source/configure/configurers/IAzureResourceSelector.ts new file mode 100644 index 00000000..0e697bfd --- /dev/null +++ b/Source/configure/configurers/IAzureResourceSelector.ts @@ -0,0 +1,6 @@ +import { GenericResource } from "azure-arm-resource/lib/resource/models"; + +export interface IAzureResourceSelector { + getAzureResource(inputs): Promise; +} + diff --git a/Source/configure/configurers/IProvisioningConfigurer.ts b/Source/configure/configurers/IProvisioningConfigurer.ts new file mode 100644 index 00000000..06a676d2 --- /dev/null +++ b/Source/configure/configurers/IProvisioningConfigurer.ts @@ -0,0 +1,10 @@ +import { WizardInputs } from '../model/models'; +import { DraftPipelineConfiguration, ProvisioningConfiguration } from '../model/provisioningConfiguration'; + +export interface IProvisioningConfigurer { + queueProvisioningPipelineJob(provisioningConfiguration: ProvisioningConfiguration, wizardInputs: WizardInputs): Promise; + getProvisioningPipeline(jobId: string, githubOrg: string, repository: string, wizardInputs: WizardInputs): Promise; + postSteps(provisioningConfiguration: ProvisioningConfiguration, draftPipelineConfiguration: DraftPipelineConfiguration, inputs: WizardInputs): Promise; + preSteps(provisioningConfiguration: ProvisioningConfiguration, inputs: WizardInputs): Promise; + browseQueuedWorkflow(): Promise; +} diff --git a/Source/configure/configurers/ResourceSelectorFactory.ts b/Source/configure/configurers/ResourceSelectorFactory.ts new file mode 100644 index 00000000..cd4b5f32 --- /dev/null +++ b/Source/configure/configurers/ResourceSelectorFactory.ts @@ -0,0 +1,34 @@ +import { ServiceClientCredentials } from "ms-rest"; +import { AzureEnvironment } from "ms-rest-azure"; +import { AppServiceClient } from "../clients/azure/appServiceClient"; +import { AzureResourceClient } from "../clients/azure/azureResourceClient"; +import { TargetResourceType } from "../model/models"; +import { Messages } from "../resources/messages"; +import { AksAzureResourceSelector } from "./AksAzureResourceSelector"; +import { IAzureResourceSelector } from "./IAzureResourceSelector"; +import { WebAppAzureResourceSelector } from "./WebAppAzureResourceSelector"; + +export class ResourceSelectorFactory { + + public static getAzureResourceSelector(targetType: TargetResourceType): IAzureResourceSelector { + switch (targetType) { + case TargetResourceType.WebApp: + return new WebAppAzureResourceSelector(); + case TargetResourceType.AKS: + return new AksAzureResourceSelector(); + default: + throw new Error(Messages.ResourceNotSupported); + } + } + + public static getAzureResourceClient(targetType: TargetResourceType, credentials: ServiceClientCredentials, environment: AzureEnvironment, tenantId: string, subscriptionId: string): AzureResourceClient { + switch (targetType) { + case TargetResourceType.WebApp: + return new AppServiceClient(credentials, environment, tenantId, subscriptionId); + case TargetResourceType.AKS: + return new AzureResourceClient(credentials, subscriptionId); + default: + throw new Error(Messages.ResourceNotSupported); + } + } +} \ No newline at end of file diff --git a/Source/configure/configurers/WebAppAzureResourceSelector.ts b/Source/configure/configurers/WebAppAzureResourceSelector.ts new file mode 100644 index 00000000..fc4de851 --- /dev/null +++ b/Source/configure/configurers/WebAppAzureResourceSelector.ts @@ -0,0 +1,31 @@ +import { GenericResource } from "azure-arm-resource/lib/resource/models"; +import { AppServiceClient } from "../clients/azure/appServiceClient"; +import { openBrowseExperience } from "../configure"; +import { ControlProvider } from "../helper/controlProvider"; +import { QuickPickItemWithData, WizardInputs } from "../model/models"; +import { Messages } from "../resources/messages"; +import { TelemetryKeys } from "../resources/telemetryKeys"; +import { IAzureResourceSelector } from "./IAzureResourceSelector"; + +export class WebAppAzureResourceSelector implements IAzureResourceSelector { + + async getAzureResource(inputs: WizardInputs): Promise { + let controlProvider = new ControlProvider(); + let appServiceClient = new AppServiceClient(inputs.azureSession.credentials, inputs.azureSession.environment, inputs.azureSession.tenantId, inputs.subscriptionId); + + let webAppKinds = inputs.potentialTemplates.map((template) => template.targetKind); + let selectedResource: QuickPickItemWithData = await controlProvider.showQuickPick( + Messages.selectTargetResource, + appServiceClient.GetAppServices(webAppKinds) + .then((webApps) => webApps.map(x => { return { label: x.name, data: x }; })), + { placeHolder: Messages.selectTargetResource }, + TelemetryKeys.AzureResourceListCount); + if (await appServiceClient.isScmTypeSet((selectedResource.data).id)) { + await openBrowseExperience((selectedResource.data).id); + throw new Error(Messages.setupAlreadyConfigured); + } + return selectedResource.data; + } + +} + diff --git a/Source/configure/configurers/azurePipelineConfigurer.ts b/Source/configure/configurers/azurePipelineConfigurer.ts new file mode 100644 index 00000000..692e591d --- /dev/null +++ b/Source/configure/configurers/azurePipelineConfigurer.ts @@ -0,0 +1,390 @@ +import { GenericResource } from 'azure-arm-resource/lib/resource/models'; +import * as path from 'path'; +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { UserCancelledError } from 'vscode-azureextensionui'; +import { AppServiceClient, VSTSDeploymentMessage } from '../clients/azure/appServiceClient'; +import { AzureResourceClient } from '../clients/azure/azureResourceClient'; +import { AzureDevOpsClient } from "../clients/devOps/azureDevOpsClient"; +import { UniqueResourceNameSuffix } from '../configure'; +import { generateDevOpsOrganizationName, generateDevOpsProjectName } from '../helper/commonHelper'; +import { ControlProvider } from '../helper/controlProvider'; +import { AzureDevOpsHelper } from "../helper/devOps/azureDevOpsHelper"; +import { ServiceConnectionHelper } from '../helper/devOps/serviceConnectionHelper'; +import { GraphHelper } from '../helper/graphHelper'; +import { LocalGitRepoHelper } from '../helper/LocalGitRepoHelper'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { TemplateParameterHelper } from '../helper/templateParameterHelper'; +import { Build } from '../model/azureDevOps'; +import { AzureConnectionType, AzureSession, RepositoryProvider, TargetResourceType, WizardInputs } from "../model/models"; +import { LocalPipelineTemplate, TemplateAssetType } from '../model/templateModels'; +import * as constants from '../resources/constants'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from '../resources/telemetryKeys'; +import { TracePoints } from '../resources/tracePoints'; +import { WhiteListedError } from '../utilities/utilities'; +import { Configurer } from "./configurerBase"; +import Q = require('q'); + +const Layer = 'AzurePipelineConfigurer'; + +export class AzurePipelineConfigurer implements Configurer { + private azureDevOpsHelper: AzureDevOpsHelper; + private azureDevOpsClient: AzureDevOpsClient; + private queuedPipeline: Build; + private controlProvider: ControlProvider; + + constructor(azureSession: AzureSession) { + this.azureDevOpsClient = new AzureDevOpsClient(azureSession.credentials); + this.azureDevOpsHelper = new AzureDevOpsHelper(this.azureDevOpsClient); + this.controlProvider = new ControlProvider(); + this.azureDevOpsClient.listOrganizations(true); + } + + public async getInputs(inputs: WizardInputs): Promise { + try { + inputs.isNewOrganization = false; + + if (!inputs.sourceRepository.remoteUrl || inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github) { + let devOpsOrganizations = await this.azureDevOpsClient.listOrganizations(); + + if (devOpsOrganizations && devOpsOrganizations.length > 0) { + let selectedOrganization = await this.controlProvider.showQuickPick( + constants.SelectOrganization, + devOpsOrganizations.map(x => { return { label: x.accountName }; }), + { placeHolder: Messages.selectOrganization }, + TelemetryKeys.OrganizationListCount); + inputs.organizationName = selectedOrganization.label; + + let selectedProject = await this.controlProvider.showQuickPick( + constants.SelectProject, + this.azureDevOpsClient.listProjects(inputs.organizationName) + .then((projects) => projects.map(x => { return { label: x.name, data: x }; })), + { placeHolder: Messages.selectProject }, + TelemetryKeys.ProjectListCount); + inputs.project = selectedProject.data; + } + else { + inputs.isNewOrganization = true; + let userName = inputs.azureSession.userId.substring(0, inputs.azureSession.userId.indexOf("@")); + let organizationName = generateDevOpsOrganizationName(userName, inputs.sourceRepository.repositoryName); + + let validationErrorMessage = await this.azureDevOpsClient.validateOrganizationName(organizationName); + if (validationErrorMessage) { + inputs.organizationName = await this.controlProvider.showInputBox( + constants.EnterOrganizationName, + { + placeHolder: Messages.enterAzureDevOpsOrganizationName, + validateInput: (organizationName) => this.azureDevOpsClient.validateOrganizationName(organizationName) + }); + } + else { + inputs.organizationName = organizationName; + } + inputs.project = { + id: "", + name: generateDevOpsProjectName(inputs.sourceRepository.repositoryName) + }; + } + } + else { + let repoDetails = AzureDevOpsHelper.getRepositoryDetailsFromRemoteUrl(inputs.sourceRepository.remoteUrl); + inputs.organizationName = repoDetails.organizationName; + await this.azureDevOpsClient.getRepository(inputs.organizationName, repoDetails.projectName, inputs.sourceRepository.repositoryName) + .then((repository) => { + inputs.sourceRepository.repositoryId = repository.id; + inputs.project = { + id: repository.project.id, + name: repository.project.name + }; + telemetryHelper.setTelemetry(TelemetryKeys.RepoId, inputs.sourceRepository.repositoryId); + }); + } + + telemetryHelper.setTelemetry(TelemetryKeys.NewOrganization, inputs.isNewOrganization.toString()); + } + catch (error) { + if ((error.typeKey as string).toUpperCase() === constants.ExceptionType.UnauthorizedRequestException) { + throw new WhiteListedError((error.message as string).concat(Messages.AdoDifferentTenantError), error); + } + telemetryHelper.logError(Layer, TracePoints.GetAzureDevOpsDetailsFailed, error); + throw error; + } + } + + public async validatePermissions(): Promise { + return; + } + + public async createPreRequisites(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise { + if (inputs.isNewOrganization) { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.creatingAzureDevOpsOrganization + }, + async () => { + try { + // Create DevOps organization + await this.azureDevOpsClient.createOrganization(inputs.organizationName); + this.azureDevOpsClient.listOrganizations(true); + + // Create DevOps project + await this.azureDevOpsClient.createProject(inputs.organizationName, inputs.project.name); + inputs.project.id = await this.azureDevOpsClient.getProjectIdFromName(inputs.organizationName, inputs.project.name); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CreateNewOrganizationAndProjectFailure, error); + throw error; + } + }); + } + let serviceConnectionHelper = new ServiceConnectionHelper(inputs.organizationName, inputs.project.name, this.azureDevOpsClient); + + if (inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github) { + // Create GitHub service connection in Azure DevOps + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.creatingGitHubServiceConnection + }, + async () => { + try { + let serviceConnectionName = `${inputs.sourceRepository.repositoryName}-${UniqueResourceNameSuffix}`; + inputs.sourceRepository.serviceConnectionId = await serviceConnectionHelper.createGitHubServiceConnection(serviceConnectionName, inputs.githubPATToken); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.GitHubServiceConnectionError, error); + throw error; + } + }); + } + + if (!!inputs.targetResource.resource) { + // TODO: show notification while setup is being done. + // ?? should SPN created be scoped to resource group of target azure resource. + inputs.targetResource.serviceConnectionId = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, inputs.subscriptionId) + }, + async () => { + try { + let serviceConnectionName = `${inputs.targetResource.resource.name}-${UniqueResourceNameSuffix}`; + switch ((inputs.pipelineConfiguration.template as LocalPipelineTemplate).azureConnectionType) { + case AzureConnectionType.None: + return ''; + case AzureConnectionType.AzureRMPublishProfile: + return await this.createAzurePublishProfileEndpoint(serviceConnectionHelper, azureResourceClient, serviceConnectionName, inputs); + case AzureConnectionType.AzureRMServicePrincipal: + default: + return await this.createAzureSPNServiceEndpoint(serviceConnectionHelper, serviceConnectionName, inputs); + } + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzureServiceConnectionCreateFailure, error); + throw error; + } + }); + } + } + + public async createAsset( + name: string, + // tslint:disable-next-line:no-reserved-keywords + type: TemplateAssetType, + data: any, + inputs: WizardInputs): Promise { + let serviceConnectionHelper = new ServiceConnectionHelper(inputs.organizationName, inputs.project.name, this.azureDevOpsClient); + + switch (type) { + case TemplateAssetType.AzureARMServiceConnection: + return await serviceConnectionHelper.createAzureSPNServiceConnection(name, inputs.azureSession.tenantId, inputs.subscriptionId, data.scope, data.aadApp); + case TemplateAssetType.AzureARMPublishProfileServiceConnection: + let targetWebApp = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.WebApp); + return await serviceConnectionHelper.createAzurePublishProfileServiceConnection(name, inputs.azureSession.tenantId, targetWebApp.id, data); + case TemplateAssetType.AKSKubeConfigServiceConnection: + let targetAks = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.AKS); + let serverUrl = targetAks.properties ? targetAks.properties.fqdn : ''; + serverUrl = !!serverUrl && !serverUrl.startsWith('https://') ? 'https://' + serverUrl : serverUrl; + return await serviceConnectionHelper.createKubeConfigServiceConnection(name, data, serverUrl); + case TemplateAssetType.ACRServiceConnection: + let targetAcr = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.ACR); + let registryUrl: string = targetAcr.properties ? targetAcr.properties.loginServer : ''; + registryUrl = !!registryUrl && !registryUrl.startsWith('https://') ? 'https://' + registryUrl : registryUrl; + let password = !!data.passwords && data.passwords.length > 0 ? data.passwords[0].value : null; + return await serviceConnectionHelper.createContainerRegistryServiceConnection(name, registryUrl, data.username, password); + default: + throw new Error(utils.format(Messages.assetOfTypeNotSupportedForAzurePipelines, type)); + } + } + + public async getPathToPipelineFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper): Promise { + let rootDirectory = await localGitRepoHelper.getGitRootDirectory(); + return path.join(rootDirectory, await LocalGitRepoHelper.GetAvailableFileName('azure-pipelines.yml', rootDirectory)); + } + + public async getPathToManifestFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper, fileName: string): Promise { return null; } + + public async checkInPipelineFilesToRepository(filesToCommit: string[], inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper): Promise { + + let commitMessage: string; + let initializeGitRepository = !inputs.sourceRepository.remoteUrl; + + if (!inputs.sourceRepository.remoteUrl) { + commitMessage = Messages.modifyAndCommitFileWithGitInitialization; + telemetryHelper.setTelemetry(TelemetryKeys.NewDevOpsRepository, 'true'); + } + else { + commitMessage = utils.format(Messages.modifyAndCommitFile, Messages.commitAndPush, inputs.sourceRepository.branch, inputs.sourceRepository.remoteName); + telemetryHelper.setTelemetry(TelemetryKeys.NewDevOpsRepository, 'false'); + } + + while (!inputs.sourceRepository.commitId) { + let commitOrDiscard = await vscode.window.showInformationMessage( + commitMessage, + Messages.commitAndPush, + Messages.discardPipeline); + + if (!!commitOrDiscard && commitOrDiscard.toLowerCase() === Messages.commitAndPush.toLowerCase()) { + inputs.sourceRepository.commitId = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.configuringPipelineAndDeployment }, async () => { + try { + if (!inputs.sourceRepository.remoteUrl) { + let repositoryName = path.basename(inputs.sourceRepository.localPath).trim().replace(/[^a-zA-Z0-9-]/g, ''); + repositoryName = !!repositoryName ? (repositoryName + UniqueResourceNameSuffix) : "codetoazure"; + let repository = await this.azureDevOpsClient.createRepository(inputs.organizationName, inputs.project.id, repositoryName); + inputs.sourceRepository.repositoryName = repository.name; + inputs.sourceRepository.repositoryId = repository.id; + inputs.sourceRepository.remoteUrl = repository.remoteUrl; + } + + if (initializeGitRepository) { + await localGitRepoHelper.initializeGitRepository(inputs.sourceRepository.remoteName, inputs.sourceRepository.remoteUrl, inputs.pipelineConfiguration.filePath); + + let branchDetails = await localGitRepoHelper.getGitBranchDetails(); + inputs.sourceRepository.branch = branchDetails.branch; + initializeGitRepository = false; + } + + // handle when the branch is not upto date with remote branch and push fails + return await localGitRepoHelper.commitAndPushPipelineFile(filesToCommit, inputs.sourceRepository, Messages.addAzurePipelinesYmlFile); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CheckInPipelineFailure, error); + vscode.window.showErrorMessage(utils.format(Messages.commitFailedErrorMessage, error.message)); + return null; + } + }); + } + else { + telemetryHelper.setTelemetry(TelemetryKeys.PipelineDiscarded, 'true'); + throw new UserCancelledError(Messages.operationCancelled); + } + } + + return inputs.sourceRepository.commitId; + } + + public async createAndQueuePipeline(inputs: WizardInputs): Promise { + this.queuedPipeline = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.configuringPipelineAndDeployment }, async () => { + try { + let targetResource = AzurePipelineConfigurer.getTargetResource(inputs); + + let pipelineName = `${(targetResource ? targetResource.name : inputs.pipelineConfiguration.template.label)}-${UniqueResourceNameSuffix}`; + return await this.azureDevOpsHelper.createAndRunPipeline(pipelineName, inputs); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CreateAndQueuePipelineFailed, error); + throw error; + } + }); + + return this.queuedPipeline._links.web.href; + } + + public async executePostPipelineCreationSteps(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise { + if (inputs.pipelineConfiguration.template.targetType === TargetResourceType.WebApp) { + try { + // update SCM type + let targetResource: GenericResource = AzurePipelineConfigurer.getTargetResource(inputs); + + let updateScmPromise = (azureResourceClient as AppServiceClient).updateScmType(targetResource.id); + + let buildDefinitionUrl = this.azureDevOpsClient.getOldFormatBuildDefinitionUrl(inputs.organizationName, inputs.project.id, this.queuedPipeline.definition.id); + let buildUrl = this.azureDevOpsClient.getOldFormatBuildUrl(inputs.organizationName, inputs.project.id, this.queuedPipeline.id); + + // update metadata of app service to store information about the pipeline deploying to web app. + let updateMetadataPromise = new Promise(async (resolve) => { + let metadata = await (azureResourceClient as AppServiceClient).getAppServiceMetadata(targetResource.id); + metadata["properties"] = metadata["properties"] ? metadata["properties"] : {}; + metadata["properties"]["VSTSRM_ProjectId"] = `${inputs.project.id}`; + metadata["properties"]["VSTSRM_AccountId"] = await this.azureDevOpsClient.getOrganizationIdFromName(inputs.organizationName); + metadata["properties"]["VSTSRM_BuildDefinitionId"] = `${this.queuedPipeline.definition.id}`; + metadata["properties"]["VSTSRM_BuildDefinitionWebAccessUrl"] = `${buildDefinitionUrl}`; + metadata["properties"]["VSTSRM_ConfiguredCDEndPoint"] = ''; + metadata["properties"]["VSTSRM_ReleaseDefinitionId"] = ''; + + (azureResourceClient as AppServiceClient).updateAppServiceMetadata(targetResource.id, metadata); + resolve(); + }); + + // send a deployment log with information about the setup pipeline and links. + let deploymentMessage = JSON.stringify({ + type: constants.DeploymentMessageType, + message: Messages.deploymentLogMessage, + VSTSRM_BuildDefinitionWebAccessUrl: `${buildDefinitionUrl}`, + VSTSRM_ConfiguredCDEndPoint: '', + VSTSRM_BuildWebAccessUrl: `${buildUrl}`, + }); + + let updateDeploymentLogPromise = (azureResourceClient as AppServiceClient).publishDeploymentToAppService( + targetResource.id, deploymentMessage); + + Q.all([updateScmPromise, updateMetadataPromise, updateDeploymentLogPromise]) + .then(() => { + telemetryHelper.setTelemetry(TelemetryKeys.UpdatedWebAppMetadata, 'true'); + }); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.PostDeploymentActionFailed, error); + } + } + } + + public async browseQueuedPipeline(): Promise { + vscode.window.showInformationMessage(Messages.pipelineSetupSuccessfully, Messages.browsePipeline) + .then((action: string) => { + if (action && action.toLowerCase() === Messages.browsePipeline.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.BrowsePipelineClicked, 'true'); + vscode.env.openExternal(vscode.Uri.parse(this.queuedPipeline._links.web.href)); + } + }); + } + + private static getTargetResource(inputs: WizardInputs): GenericResource { + let targetResource = !!inputs.targetResource.resource ? inputs.targetResource.resource : null; + if (!targetResource) { + let targetParam = TemplateParameterHelper.getParameterForTargetResourceType((inputs.pipelineConfiguration.template as LocalPipelineTemplate).parameters, inputs.pipelineConfiguration.template.targetType); + if (!!targetParam && !!inputs.pipelineConfiguration.params[targetParam.name]) { + targetResource = inputs.pipelineConfiguration.params[targetParam.name]; + } + else { + throw new Error(utils.format(Messages.couldNotFindTargetResourceValueInParams, inputs.pipelineConfiguration.template.targetType)); + } + } + + return targetResource; + } + + private async createAzurePublishProfileEndpoint(serviceConnectionHelper: ServiceConnectionHelper, azureResourceClient: AzureResourceClient, serviceConnectionName: string, inputs: WizardInputs): Promise { + let publishProfile = await (azureResourceClient as AppServiceClient).getWebAppPublishProfileXml(inputs.targetResource.resource.id); + return await serviceConnectionHelper.createAzurePublishProfileServiceConnection(serviceConnectionName, inputs.azureSession.tenantId, inputs.targetResource.resource.id, publishProfile); + } + + private async createAzureSPNServiceEndpoint(serviceConnectionHelper: ServiceConnectionHelper, serviceConnectionName: string, inputs: WizardInputs): Promise { + let scope = inputs.targetResource.resource.id; + let aadAppName = GraphHelper.generateAadApplicationName(inputs.organizationName, inputs.project.name); + let aadApp = await GraphHelper.createSpnAndAssignRole(inputs.azureSession, aadAppName, scope); + return await serviceConnectionHelper.createAzureSPNServiceConnection(serviceConnectionName, inputs.azureSession.tenantId, inputs.subscriptionId, scope, aadApp); + } +} diff --git a/Source/configure/configurers/configurerBase.ts b/Source/configure/configurers/configurerBase.ts new file mode 100644 index 00000000..db858f3d --- /dev/null +++ b/Source/configure/configurers/configurerBase.ts @@ -0,0 +1,22 @@ +import { AzureResourceClient } from "../clients/azure/azureResourceClient"; +import { LocalGitRepoHelper } from "../helper/LocalGitRepoHelper"; +import { WizardInputs } from "../model/models"; +import { TemplateAssetType } from "../model/templateModels"; + +export interface Configurer { + validatePermissions(): Promise; + getInputs(inputs: WizardInputs): Promise; + createPreRequisites(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise; + createAsset( + name: string, + // tslint:disable-next-line:no-reserved-keywords + type: TemplateAssetType, + data: any, + inputs: WizardInputs): Promise; + getPathToPipelineFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper): Promise; + getPathToManifestFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper, fileName: string): Promise; + checkInPipelineFilesToRepository(filesToCommit: string[], inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper): Promise; + createAndQueuePipeline(inputs: WizardInputs): Promise; + executePostPipelineCreationSteps(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise; + browseQueuedPipeline(): Promise; +} diff --git a/Source/configure/configurers/configurerFactory.ts b/Source/configure/configurers/configurerFactory.ts new file mode 100644 index 00000000..d6e61011 --- /dev/null +++ b/Source/configure/configurers/configurerFactory.ts @@ -0,0 +1,24 @@ +import { LocalGitRepoHelper } from '../helper/LocalGitRepoHelper'; +import { AzureSession, extensionVariables, GitRepositoryParameters, RepositoryProvider } from '../model/models'; +import { TemplateType } from '../model/templateModels'; +import { Messages } from '../resources/messages'; +import { AzurePipelineConfigurer } from './azurePipelineConfigurer'; +import { Configurer } from './configurerBase'; +import { LocalGitHubWorkflowConfigurer } from './localGithubWorkflowConfigurer'; +import { RemoteGitHubWorkflowConfigurer } from './remoteGitHubWorkflowConfigurer'; + +export class ConfigurerFactory { + public static GetConfigurer(sourceRepositoryDetails: GitRepositoryParameters, azureSession: AzureSession, templateType: TemplateType, localGitRepoHelper: LocalGitRepoHelper): Configurer { + switch (sourceRepositoryDetails.repositoryProvider) { + case RepositoryProvider.Github: + if (extensionVariables.enableGitHubWorkflow) { + return templateType === TemplateType.LOCAL ? new LocalGitHubWorkflowConfigurer(localGitRepoHelper) : new RemoteGitHubWorkflowConfigurer(azureSession, localGitRepoHelper); + } + return new AzurePipelineConfigurer(azureSession); + case RepositoryProvider.AzureRepos: + return new AzurePipelineConfigurer(azureSession); + default: + throw new Error(Messages.cannotIdentifyRespositoryDetails); + } + } +} diff --git a/Source/configure/configurers/localGithubWorkflowConfigurer.ts b/Source/configure/configurers/localGithubWorkflowConfigurer.ts new file mode 100644 index 00000000..89da8d49 --- /dev/null +++ b/Source/configure/configurers/localGithubWorkflowConfigurer.ts @@ -0,0 +1,314 @@ +import { GenericResource } from 'azure-arm-resource/lib/resource/models'; +import * as fs from 'fs'; +import * as ymlconfig from 'js-yaml'; +import * as path from 'path'; +import * as Q from 'q'; +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { UserCancelledError } from 'vscode-azureextensionui'; +import { AppServiceClient, DeploymentMessage } from '../clients/azure/appServiceClient'; +import { ApiVersions, AzureResourceClient } from '../clients/azure/azureResourceClient'; +import { GithubClient } from '../clients/github/githubClient'; +import { generateGitHubRepository } from "../helper/commonHelper"; +import { ControlProvider } from '../helper/controlProvider'; +import { GitHubProvider } from '../helper/gitHubHelper'; +import { GraphHelper } from '../helper/graphHelper'; +import { LocalGitRepoHelper } from '../helper/LocalGitRepoHelper'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { TemplateParameterHelper } from '../helper/templateParameterHelper'; +import { AzureConnectionType, extensionVariables, GitHubRepo, TargetResourceType, WizardInputs } from "../model/models"; +import { LocalPipelineTemplate, TemplateAssetType } from '../model/templateModels'; +import * as constants from '../resources/constants'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from '../resources/telemetryKeys'; +import { TracePoints } from '../resources/tracePoints'; +import { Configurer } from "./configurerBase"; + + +const uuid = require('uuid/v4'); +const Layer = 'LocalGitHubWorkflowConfigurer'; + +export class LocalGitHubWorkflowConfigurer implements Configurer { + protected githubClient: GithubClient; + private queuedPipelineUrl: string; + private controlProvider: ControlProvider; + private localGitRepoHelper: LocalGitRepoHelper; + + constructor(localgitRepoHelper: LocalGitRepoHelper) { + this.controlProvider = new ControlProvider(); + this.localGitRepoHelper = localgitRepoHelper; + } + + public async getInputs(inputs: WizardInputs): Promise { + this.githubClient = new GithubClient(inputs.githubPATToken, inputs.sourceRepository.remoteUrl); + inputs.isNewOrganization = false; + if (!inputs.sourceRepository.remoteUrl) { + const githubOrganizations = await this.githubClient.listOrganizations(); + + if (githubOrganizations && githubOrganizations.length > 0) { + const selectedOrganization = await this.controlProvider.showQuickPick( + constants.SelectGitHubOrganization, + githubOrganizations.map(x => { return { label: x.login }; }), + { placeHolder: Messages.selectGitHubOrganizationName }, + TelemetryKeys.OrganizationListCount); + inputs.organizationName = selectedOrganization.label; + const isUserAccount = githubOrganizations.find((x) => x.login === selectedOrganization.label).isUserAccount; + + try { + const newGitHubRepo = await generateGitHubRepository(inputs.organizationName, inputs.sourceRepository.localPath, this.githubClient, isUserAccount) as GitHubRepo; + this.githubClient.setRepoUrl(newGitHubRepo.html_url); + inputs.sourceRepository.remoteName = newGitHubRepo.name; + inputs.sourceRepository.remoteUrl = newGitHubRepo.html_url + ".git"; + inputs.sourceRepository.repositoryId = GitHubProvider.getRepositoryIdFromUrl(inputs.sourceRepository.remoteUrl); + await this.localGitRepoHelper.initializeGitRepository(inputs.sourceRepository.remoteName, inputs.sourceRepository.remoteUrl); + telemetryHelper.setTelemetry(TelemetryKeys.RepoId, inputs.sourceRepository.repositoryId); + telemetryHelper.setTelemetry(TelemetryKeys.GitHubRepoCreated, 'true'); + vscode.window.showInformationMessage(utils.format(Messages.newGitHubRepositoryCreated, newGitHubRepo.name)); + } + catch (error) { + vscode.window.showErrorMessage(error.message); + throw error; + } + } + else { + vscode.window.showErrorMessage(Messages.createGitHubOrganization); + const error = new Error(Messages.createGitHubOrganization); + telemetryHelper.logError(Layer, TracePoints.NoGitHubOrganizationExists, error); + throw error; + } + } + } + + public async validatePermissions(): Promise { + return; + } + + public async createPreRequisites(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise { + if (inputs.targetResource && inputs.targetResource.resource && inputs.targetResource.resource.type.toLowerCase() === TargetResourceType.WebApp.toLowerCase()) { + let azureConnectionSecret: string = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, inputs.subscriptionId) + }, + async () => { + try { + switch ((inputs.pipelineConfiguration.template as LocalPipelineTemplate).azureConnectionType) { + case AzureConnectionType.None: + return null; + case AzureConnectionType.AzureRMPublishProfile: + return await (azureResourceClient as AppServiceClient).getWebAppPublishProfileXml(inputs.targetResource.resource.id); + case AzureConnectionType.AzureRMServicePrincipal: + default: + return await this.getAzureSPNSecret(inputs); + } + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzureServiceConnectionCreateFailure, error); + throw error; + } + }); + + if (!!azureConnectionSecret) { + inputs.targetResource.serviceConnectionId = 'AZURE_CREDENTIALS_' + uuid().substr(0, 8); + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.settingUpGithubSecrets + }, + async () => { + await this.createAsset(inputs.targetResource.serviceConnectionId, TemplateAssetType.GitHubARM, azureConnectionSecret, inputs); + }); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzureServiceConnectionCreateFailure, error); + throw error; + } + } + } + } + + public async createAsset( + name: string, + // tslint:disable-next-line:no-reserved-keywords + type: TemplateAssetType, + data: any, + inputs: WizardInputs): Promise { + let secret: string = null; + switch (type) { + case TemplateAssetType.GitHubARM: + case TemplateAssetType.GitHubAKSKubeConfig: + case TemplateAssetType.GitHubRegistryUsername: + case TemplateAssetType.GitHubRegistryPassword: + secret = data; + break; + default: + throw new Error(utils.format(Messages.assetOfTypeNotSupportedForGitHub, type)); + } + + if (secret) { + await this.githubClient.createOrUpdateGithubSecret(name, secret); + } + + return name; + } + + public async getPathToPipelineFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper, pipelineFileName?: string): Promise { + // Create .github directory + let workflowDirectoryPath = path.join('.github', 'workflows'); + if (!pipelineFileName) { + pipelineFileName = 'workflow.yml'; + } + return await this.getPathToFile(localGitRepoHelper, pipelineFileName, workflowDirectoryPath); + } + + public async getPathToManifestFile(inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper, fileName: string): Promise { + // Create manifests directory + let manifestsDirectoryPath: string = path.join(inputs.pipelineConfiguration.workingDirectory, 'manifests'); + try { + return await this.getPathToFile(localGitRepoHelper, fileName, manifestsDirectoryPath); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.ManifestsFolderCreationFailed, error); + throw error; + } + } + + public async getPathToFile(localGitRepoHelper: LocalGitRepoHelper, fileName: string, directory: string) { + let dirList = directory.split(path.sep); + let directoryPath: string = ""; + directoryPath = await localGitRepoHelper.getGitRootDirectory(); + dirList.forEach((dir) => { + try { + directoryPath = path.join(directoryPath, dir); + if (!fs.existsSync(directoryPath)) { + fs.mkdirSync(directoryPath); + } + } + catch (error) { + throw error; + } + }); + fileName = await LocalGitRepoHelper.GetAvailableFileName(fileName, directoryPath); + telemetryHelper.setTelemetry(TelemetryKeys.WorkflowFileName, fileName); + return path.join(directoryPath, fileName); + } + + public async checkInPipelineFilesToRepository(filesToCommit: string[], inputs: WizardInputs, localGitRepoHelper: LocalGitRepoHelper): Promise { + + while (!inputs.sourceRepository.commitId) { + + let displayMessage = Messages.modifyAndCommitFile; + if (filesToCommit.length > 1) { + displayMessage = Messages.modifyAndCommitMultipleFiles; + } + + let commitOrDiscard = await vscode.window.showInformationMessage( + utils.format(displayMessage, Messages.commitAndPush, inputs.sourceRepository.branch, inputs.sourceRepository.remoteName), + Messages.commitAndPush, + Messages.discardPipeline); + + if (!!commitOrDiscard && commitOrDiscard.toLowerCase() === Messages.commitAndPush.toLowerCase()) { + inputs.sourceRepository.commitId = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.configuringPipelineAndDeployment }, async () => { + try { + // handle when the branch is not upto date with remote branch and push fails + return await localGitRepoHelper.commitAndPushPipelineFile(filesToCommit, inputs.sourceRepository, extensionVariables.enableGitHubWorkflow ? Messages.addGitHubWorkflowYmlFile : Messages.addAzurePipelinesYmlFile); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CheckInPipelineFailure, error); + vscode.window.showErrorMessage(utils.format(Messages.commitFailedErrorMessage, error.message)); + return null; + } + }); + } + else { + telemetryHelper.setTelemetry(TelemetryKeys.PipelineDiscarded, 'true'); + throw new UserCancelledError(Messages.operationCancelled); + } + } + + return inputs.sourceRepository.commitId; + } + + public async createAndQueuePipeline(inputs: WizardInputs): Promise { + this.queuedPipelineUrl = `https://github.com/${inputs.sourceRepository.repositoryId}/commit/${inputs.sourceRepository.commitId}/checks`; + return this.queuedPipelineUrl; + } + + public async executePostPipelineCreationSteps(inputs: WizardInputs, azureResourceClient: AzureResourceClient): Promise { + try { + if (inputs.targetResource && inputs.targetResource.resource && inputs.targetResource.resource.type === TargetResourceType.WebApp) { + // Update web app metadata + await new Promise(async (resolve) => { + let metadata = await (azureResourceClient as AppServiceClient).getAppServiceMetadata(inputs.targetResource.resource.id); + metadata["properties"] = metadata["properties"] ? metadata["properties"] : {}; + + let repositoryPath = await LocalGitRepoHelper.GetHelperInstance(inputs.sourceRepository.localPath).getGitRootDirectory(); + let configPath = path.relative(repositoryPath, inputs.pipelineConfiguration.filePath); + + const doc = ymlconfig.safeLoad(fs.readFileSync(inputs.pipelineConfiguration.filePath, 'utf8')); + if (!!doc["name"]) { + metadata["properties"]["configName"] = `${doc["name"]}`; + } + metadata["properties"]["configPath"] = `${configPath}`; + metadata["properties"]["repoUrl"] = `https://github.com/${inputs.sourceRepository.repositoryId}`; + metadata["properties"]["branch"] = inputs.sourceRepository.branch; + + await (azureResourceClient as AppServiceClient).updateAppServiceMetadata(inputs.targetResource.resource.id, metadata); + resolve(); + }); + + // Update web app sourceControls as GitHubAction + const sourceControlProperties = { + "isGitHubAction": true, + "repoUrl": `https://github.com/${inputs.sourceRepository.repositoryId}`, + "branch": inputs.sourceRepository.branch, + }; + let sourceControlPromise = (azureResourceClient as AppServiceClient).setSourceControl(inputs.targetResource.resource.id, sourceControlProperties); + + // send a deployment log with information about the setup pipeline and links. + let deploymentMessage = JSON.stringify({ + type: constants.DeploymentMessageType, + message: Messages.deploymentLogMessage + }); + + let authorName = await LocalGitRepoHelper.GetHelperInstance(inputs.sourceRepository.localPath).getUsername(); + let deployerName = 'GITHUBACTION'; + let updateDeploymentLogPromise = (azureResourceClient as AppServiceClient).publishDeploymentToAppService(inputs.targetResource.resource.id, deploymentMessage, authorName, deployerName); + Q.all([sourceControlPromise, updateDeploymentLogPromise]) + .then(() => { + telemetryHelper.setTelemetry(TelemetryKeys.UpdatedWebAppMetadata, 'true'); + }); + } + else if (TemplateParameterHelper.getParameterForTargetResourceType((inputs.pipelineConfiguration.template as LocalPipelineTemplate).parameters, TargetResourceType.AKS)) { + let aksResource: GenericResource = inputs.pipelineConfiguration.params[TemplateParameterHelper.getParameterForTargetResourceType((inputs.pipelineConfiguration.template as LocalPipelineTemplate).parameters, TargetResourceType.AKS).name]; + let workflowFileName = path.basename(inputs.pipelineConfiguration.filePath); + await azureResourceClient.updateCdSetupResourceTag(aksResource, inputs.sourceRepository.repositoryId, inputs.sourceRepository.branch, workflowFileName, inputs.sourceRepository.commitId, inputs.pipelineConfiguration.params['namespace'], ApiVersions.get(TargetResourceType.AKS)); + } + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.PostDeploymentActionFailed, error); + } + } + + public async browseQueuedPipeline(): Promise { + vscode.window.showInformationMessage(Messages.githubWorkflowSetupSuccessfully, Messages.browseWorkflow) + .then((action: string) => { + if (action && action.toLowerCase() === Messages.browseWorkflow.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.BrowsePipelineClicked, 'true'); + vscode.env.openExternal(vscode.Uri.parse(this.queuedPipelineUrl)); + } + }); + } + + protected async getAzureSPNSecret(inputs: WizardInputs, scope?: string): Promise { + scope = !scope ? inputs.targetResource.resource.id : scope; + let aadAppName = GraphHelper.generateAadApplicationName(inputs.sourceRepository.remoteName, 'github'); + let aadApp = await GraphHelper.createSpnAndAssignRole(inputs.azureSession, aadAppName, scope); + return JSON.stringify({ + "clientId": `${aadApp.appId}`, + "clientSecret": `${aadApp.secret}`, + "subscriptionId": `${inputs.subscriptionId}`, + "tenantId": `${inputs.azureSession.tenantId}`, + }); + } +} diff --git a/Source/configure/configurers/provisioningConfigurer.ts b/Source/configure/configurers/provisioningConfigurer.ts new file mode 100644 index 00000000..a2ee6d74 --- /dev/null +++ b/Source/configure/configurers/provisioningConfigurer.ts @@ -0,0 +1,304 @@ +import * as fse from 'fs-extra'; +import * as os from 'os'; +import * as Path from 'path'; +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { UserCancelledError } from 'vscode-azureextensionui'; +import { ArmRestClient } from '../clients/azure/armRestClient'; +import { IProvisioningServiceClient } from "../clients/IProvisioningServiceClient"; +import { ProvisioningServiceClientFactory } from "../clients/provisioningServiceClientFactory"; +import { sleepForMilliSeconds } from '../helper/commonHelper'; +import { ControlProvider } from '../helper/controlProvider'; +import { GraphHelper } from '../helper/graphHelper'; +import { LocalGitRepoHelper } from '../helper/LocalGitRepoHelper'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { ExtendedInputDescriptor, InputDataType } from "../model/Contracts"; +import { WizardInputs } from "../model/models"; +import { CompletePipelineConfiguration, DraftPipelineConfiguration, File, ProvisioningConfiguration } from "../model/provisioningConfiguration"; +import { RemotePipelineTemplate } from '../model/templateModels'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from '../resources/telemetryKeys'; +import { TracePoints } from '../resources/tracePoints'; +import { InputControl } from '../templateInputHelper/InputControl'; +import { IProvisioningConfigurer } from './IProvisioningConfigurer'; + +// tslint:disable-next-line:interface-name +interface DraftFile { + content: string; + path: string; // This path will be one returned by provisioning service and it will based on linux + absPath: string; // This is absolute path of file for native OS +} + +const Layer: string = "ProvisioningConfigurer"; +export class ProvisioningConfigurer implements IProvisioningConfigurer { + private provisioningServiceClient: IProvisioningServiceClient; + private queuedPipelineUrl: string; + private refreshTime: number = 5 * 1000; + private maxNonStatusRetry: number = 60; // retries for max 5 min + private localGitRepoHelper: LocalGitRepoHelper; + private filesToCommit: DraftFile[] = []; + private committedWorkflow: string; + private tempWorkflowDirPath: string; + + constructor(localGitRepoHelper: LocalGitRepoHelper) { + this.localGitRepoHelper = localGitRepoHelper; + } + + public async queueProvisioningPipelineJob(provisioningConfiguration: ProvisioningConfiguration, wizardInputs: WizardInputs): Promise { + try { + this.provisioningServiceClient = await ProvisioningServiceClientFactory.getClient(); + const OrgAndRepoDetails = wizardInputs.sourceRepository.repositoryId.split('/'); + return await this.provisioningServiceClient.createProvisioningConfiguration(provisioningConfiguration, OrgAndRepoDetails[0], OrgAndRepoDetails[1]); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.UnableToCreateProvisioningPipeline, error); + throw error; + } + } + + public async getProvisioningPipeline(jobId: string, githubOrg: string, repository: string, wizardInputs: WizardInputs): Promise { + try { + this.provisioningServiceClient = await ProvisioningServiceClientFactory.getClient(); + return await this.provisioningServiceClient.getProvisioningConfiguration(jobId, githubOrg, repository); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.UnabletoGetProvisioningPipeline, error); + throw error; + } + } + + public async awaitProvisioningPipelineJob(jobId: string, githubOrg: string, repository: string, wizardInputs: WizardInputs): Promise { + let statusNotFound: number = 0; + const provisioningServiceResponse = await this.getProvisioningPipeline(jobId, githubOrg, repository, wizardInputs); + if (provisioningServiceResponse && provisioningServiceResponse.result) { + if (provisioningServiceResponse.result.status === "Queued" || provisioningServiceResponse.result.status == "InProgress") { + await sleepForMilliSeconds(this.refreshTime); + return await this.awaitProvisioningPipelineJob(jobId, githubOrg, repository, wizardInputs); + } else if (provisioningServiceResponse.result.status === "Failed") { + throw new Error(provisioningServiceResponse.result.message); + } else { + return provisioningServiceResponse; + } + } else { + if (statusNotFound < this.maxNonStatusRetry) { + statusNotFound++; + await sleepForMilliSeconds(this.refreshTime); + return await this.awaitProvisioningPipelineJob(jobId, githubOrg, repository, wizardInputs); + } else { + throw new Error("Failed to receive queued pipeline provisioning job status"); + } + } + } + + public async browseQueuedWorkflow(): Promise { + let displayMessage: string; + if (this.committedWorkflow.length > 1) { + displayMessage = Messages.GithubWorkflowSetupMultiFile; + } else { + displayMessage = Messages.GithubWorkflowSetup; + } + + new ControlProvider().showInformationBox("Browse queued workflow", utils.format(displayMessage, this.committedWorkflow), Messages.browseWorkflow) + .then((action: string) => { + if (action && action.toLowerCase() === Messages.browseWorkflow.toLowerCase()) { + telemetryHelper.setTelemetry(TelemetryKeys.BrowsePipelineClicked, 'true'); + vscode.env.openExternal(vscode.Uri.parse(this.queuedPipelineUrl)); + } + }); + } + + public async postSteps(provisioningConfiguration: ProvisioningConfiguration, draftPipelineConfiguration: DraftPipelineConfiguration, inputs: WizardInputs): Promise { + await this.populateFilesToCommit(draftPipelineConfiguration); + await this.showPipelineFiles(); + const displayMessage = (this.filesToCommit.length > 1) ? Messages.modifyAndCommitMultipleFiles : Messages.modifyAndCommitFile; + const commitOrDiscard = await new ControlProvider().showInformationBox( + "Commit or discard", + utils.format(displayMessage, Messages.commitAndPush, inputs.sourceRepository.branch, inputs.sourceRepository.remoteName), + Messages.commitAndPush, + Messages.discardPipeline); + let provisioningServiceResponse: ProvisioningConfiguration; + if (!!commitOrDiscard && commitOrDiscard.toLowerCase() === Messages.commitAndPush.toLowerCase()) { + telemetryHelper.setCurrentStep('ConfiguringPreRequisiteParamsForCompleteMode'); + if (this.getInputDescriptor(inputs, "azureAuth")) { + await this.createSPN(inputs); + } + provisioningServiceResponse = await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.ConfiguringGithubWorkflowAndDeployment }, + async () => { + try { + telemetryHelper.setCurrentStep('QueuedCompleteProvisioiningPipeline'); + provisioningConfiguration.pipelineConfiguration = await this.createFilesToCheckin(draftPipelineConfiguration.id, draftPipelineConfiguration.type); + const completeProvisioningSvcResp = await this.queueProvisioningPipelineJob(provisioningConfiguration, inputs); + if (completeProvisioningSvcResp.id != "") { + const OrgAndRepoDetails = inputs.sourceRepository.repositoryId.split('/'); + telemetryHelper.setCurrentStep('AwaitCompleteProvisioningPipeline'); + return await this.awaitProvisioningPipelineJob(completeProvisioningSvcResp.id, OrgAndRepoDetails[0], OrgAndRepoDetails[1], inputs); + } else { + throw new Error("Failed to configure pipeline"); + } + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.RemotePipelineConfiguringFailed, error); + vscode.window.showErrorMessage(utils.format(Messages.ConfiguringGitubWorkflowFailed, error.message)); + return null; + } + }); + } else { + telemetryHelper.setTelemetry(TelemetryKeys.PipelineDiscarded, 'true'); + await this.moveWorkflowFilesToLocalRepo(); + throw new UserCancelledError(Messages.operationCancelled); + } + + fse.removeSync(this.tempWorkflowDirPath); + if (provisioningServiceResponse != undefined) { + this.setQueuedPipelineUrl(provisioningServiceResponse, inputs); + } else { + throw new Error("Failed to configure provisoining pipeline"); + } + } + + public async preSteps(provisioningConfiguration: ProvisioningConfiguration, inputs: WizardInputs): Promise { + return await vscode.window.withProgress({ location: vscode.ProgressLocation.Notification, title: Messages.GeneratingWorkflowFiles }, async () => { + try { + telemetryHelper.setCurrentStep('QueuedDraftProvisioningPipeline'); + // Initially send the provisioning request in draft mode to get workflow files + const provisioningServiceDraftModeResponse: ProvisioningConfiguration = await this.queueProvisioningPipelineJob(provisioningConfiguration, inputs); + if (provisioningServiceDraftModeResponse.id != "") { + // Monitor the provisioning pipeline + telemetryHelper.setCurrentStep('AwaitDraftProvisioningPipeline'); + const OrgAndRepoDetails = inputs.sourceRepository.repositoryId.split('/'); + return await this.awaitProvisioningPipelineJob(provisioningServiceDraftModeResponse.id, OrgAndRepoDetails[0], OrgAndRepoDetails[1], inputs); + } else { + throw new Error("Failed to configure provisioning pipeline"); + } + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.ConfiguringDraftPipelineFailed, error); + throw error; + } + }); + } + + public async showPipelineFiles(): Promise { + this.filesToCommit.forEach(async (file) => { + await this.localGitRepoHelper.writeFileContent(file.content, file.absPath); + await vscode.window.showTextDocument(vscode.Uri.file(file.absPath), { preview: false }); + }); + } + + public setQueuedPipelineUrl(provisioningConfiguration: ProvisioningConfiguration, inputs: WizardInputs) { + const commitId = (provisioningConfiguration.result.pipelineConfiguration as CompletePipelineConfiguration).commitId; + this.queuedPipelineUrl = `https://github.com/${inputs.sourceRepository.repositoryId}/commit/${commitId}/checks`; + this.committedWorkflow = `https://github.com/${inputs.sourceRepository.repositoryId}/commit/${commitId}`; + } + + public async populateFilesToCommit(draftPipelineConfiguration: DraftPipelineConfiguration): Promise { + let destination: string; + this.tempWorkflowDirPath = fse.mkdtempSync(os.tmpdir().concat(Path.sep)); + for (const file of draftPipelineConfiguration.files) { + const pathList = file.path.split("/"); + const filePath: string = pathList.join(Path.sep); + destination = await this.getPathToFile(Path.basename(filePath), Path.dirname(filePath)); + const decodedData = new Buffer(file.content, 'base64').toString('utf-8'); + this.filesToCommit.push({ absPath: destination, content: decodedData, path: file.path } as DraftFile); + } + } + + public async createPreRequisiteParams(wizardInputs: WizardInputs): Promise { + // Create armAuthToken + wizardInputs.pipelineConfiguration.params["armAuthToken"] = "Bearer " + await GraphHelper.getAccessToken(wizardInputs.azureSession); + } + + public async createSPN(wizardInputs: WizardInputs): Promise { + // Create SPN and ACRResource group for reuseACR flow set to false + const inputDescriptor = this.getInputDescriptor(wizardInputs, "azureAuth"); + const createResourceGroup = InputControl.getInputDescriptorProperty(inputDescriptor, "resourceGroup", wizardInputs.pipelineConfiguration.params); + const location = InputControl.getInputDescriptorProperty(inputDescriptor, "location", wizardInputs.pipelineConfiguration.params); + if (createResourceGroup && createResourceGroup.length > 0 && createResourceGroup[0] != "" && location && location.length > 0 && location[0] != "") { + await vscode.window.withProgress( + { location: vscode.ProgressLocation.Notification, title: Messages.CreatingResourceGroup }, + async () => { + try { + // TODO: Add support for multiple resource group + return await new ArmRestClient(wizardInputs.azureSession).createResourceGroup(wizardInputs.subscriptionId, createResourceGroup[0], location[0]); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.ResourceGroupCreationFailed, error); + throw error; + } + }); + } + const scope = InputControl.getInputDescriptorProperty(inputDescriptor, "scope", wizardInputs.pipelineConfiguration.params); + if (scope && scope.length > 0 && scope[0] != "") { + wizardInputs.pipelineConfiguration.params["azureAuth"] = await vscode.window.withProgress( + { location: vscode.ProgressLocation.Notification, title: Messages.CreatingSPN }, + async () => { + try { + // TODO: Need to add support for array of scope + return await this.getAzureSPNSecret(wizardInputs, scope[0]); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.SPNCreationFailed, error); + throw error; + } + }); + } + } + + private getInputDescriptor(wizardInputs: WizardInputs, inputId: string): ExtendedInputDescriptor { + const template = wizardInputs.pipelineConfiguration.template as RemotePipelineTemplate; + let inputDataType: InputDataType; + switch (inputId) { + case "azureAuth": + inputDataType = InputDataType.Authorization; + break; + default: + return undefined; + } + + return template.parameters.inputs.find((value) => (value.type === inputDataType && value.id === inputId)); + } + + private async getAzureSPNSecret(inputs: WizardInputs, scope?: string): Promise { + const aadAppName = GraphHelper.generateAadApplicationName(inputs.sourceRepository.remoteName, 'github'); + const aadApp = await GraphHelper.createSpnAndAssignRole(inputs.azureSession, aadAppName, scope); + return JSON.stringify({ + scheme: 'ServicePrincipal', + parameters: { + serviceprincipalid: `${aadApp.appId}`, + serviceprincipalkey: `${aadApp.secret}`, + subscriptionId: `${inputs.subscriptionId}`, + tenantid: `${inputs.azureSession.tenantId}`, + } + }); + } + + // tslint:disable-next-line:no-reserved-keywords + private async createFilesToCheckin(id: string, type: string): Promise { + const files: File[] = []; + for (const file of this.filesToCommit) { + const fileContent = await this.localGitRepoHelper.readFileContent(file.absPath); + const encodedContent = new Buffer(fileContent, 'utf-8').toString('base64'); + files.push({ path: file.path, content: encodedContent }); + } + + return { + id, + type, + files, + } as DraftPipelineConfiguration; + } + + private async moveWorkflowFilesToLocalRepo(): Promise { + const gitRootDirectory: string = await this.localGitRepoHelper.getGitRootDirectory(); + for (const file of this.filesToCommit) { + const filePathList = file.path.split("/"); + const filePath: string = filePathList.join(Path.sep); + const filePathToLocalRepo: string = Path.join(gitRootDirectory, filePath); + fse.moveSync(file.absPath, filePathToLocalRepo); + await vscode.window.showTextDocument(vscode.Uri.file(filePathToLocalRepo), { preview: false }); + } + fse.removeSync(this.tempWorkflowDirPath); + } + + private async getPathToFile(fileName: string, directory: string) { + const dirList = directory.split("/"); // Hardcoded as provisioning service is running on linux and we cannot use Path.sep as it is machine dependent + const directoryPath: string = Path.join(this.tempWorkflowDirPath, dirList.join(Path.sep)); + fse.mkdirpSync(directoryPath); + telemetryHelper.setTelemetry(TelemetryKeys.WorkflowFileName, fileName); + return Path.join(directoryPath, fileName); + } +} diff --git a/Source/configure/configurers/remoteGitHubWorkflowConfigurer.ts b/Source/configure/configurers/remoteGitHubWorkflowConfigurer.ts new file mode 100644 index 00000000..253569f8 --- /dev/null +++ b/Source/configure/configurers/remoteGitHubWorkflowConfigurer.ts @@ -0,0 +1,247 @@ +import * as Path from 'path'; +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { AppServiceClient } from '../clients/azure/appServiceClient'; +import { AzureResourceClient } from "../clients/azure/azureResourceClient"; +import { GithubClient } from '../clients/github/githubClient'; +import { ITemplateServiceClient } from '../clients/ITemplateServiceClient'; +import { TemplateServiceClientFactory } from '../clients/TemplateServiceClientFactory'; +import { LocalGitRepoHelper } from '../helper/LocalGitRepoHelper'; +import { MustacheHelper } from '../helper/mustacheHelper'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { Asset, ConfigurationStage, InputDataType } from "../model/Contracts"; +import { AzureSession, ParsedAzureResourceId, StringMap, WizardInputs } from "../model/models"; +import { RemotePipelineTemplate } from "../model/templateModels"; +import { Messages } from '../resources/messages'; +import { TracePoints } from '../resources/tracePoints'; +import { InputControl } from '../templateInputHelper/InputControl'; +import * as templateConverter from '../utilities/templateConverter'; +import * as nodeVersionConverter from '../utilities/webAppNodeVersionConverter'; +import { LocalGitHubWorkflowConfigurer } from './localGithubWorkflowConfigurer'; + +export interface File { + path: string; + content: string; +} + +const Layer: string = "RemoteGitHubWorkflowConfigurer"; + +export class RemoteGitHubWorkflowConfigurer extends LocalGitHubWorkflowConfigurer { + private azureSession: AzureSession; + private assets: { [key: string]: any } = {}; + private inputs: WizardInputs; + private secrets: { [key: string]: any } = {}; + private variables: { [key: string]: any } = {}; + private system: { [key: string]: any } = {}; + private filesToCommit: File[] = []; + private mustacheContext: StringMap; + private template: RemotePipelineTemplate; + private localGitHelper: LocalGitRepoHelper; + private templateServiceClient: ITemplateServiceClient; + + constructor(azureSession: AzureSession, localGitHelper: LocalGitRepoHelper) { + super(localGitHelper); + this.azureSession = azureSession; + this.localGitHelper = localGitHelper; + } + + public async getInputs(wizardInputs: WizardInputs): Promise { + this.inputs = wizardInputs; + this.githubClient = new GithubClient(wizardInputs.githubPATToken, wizardInputs.sourceRepository.remoteUrl); + this.templateServiceClient = await TemplateServiceClientFactory.getClient(); + this.template = wizardInputs.pipelineConfiguration.template as RemotePipelineTemplate; + let extendedPipelineTemplate; + try { + extendedPipelineTemplate = await this.templateServiceClient.getTemplateConfiguration(this.template.id, wizardInputs.pipelineConfiguration.params); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.UnableToGetTemplateConfiguration, error); + throw error; + + } + this.template.configuration = templateConverter.convertToLocalMustacheExpression(extendedPipelineTemplate.configuration); + + this.template.configuration.assets.forEach((asset: Asset) => { + if (!asset.stage) { + asset.stage = ConfigurationStage.Pre; + } + }); + + this.system["sourceRepository"] = wizardInputs.sourceRepository; + + this.mustacheContext = { + inputs: wizardInputs.pipelineConfiguration.params, + variables: this.variables, + assets: this.assets, + secrets: this.secrets, + system: this.system + }; + + for (let variable of this.template.configuration.variables) { + let expression = variable.value; + let value = MustacheHelper.render(expression, this.mustacheContext); + this.variables[variable.id] = value; + } + } + + public async createPreRequisites(inputs: WizardInputs, azureResourceClient: AzureResourceClient) { + return null; + } + + public async createAssets(stage: ConfigurationStage = ConfigurationStage.Pre) { + let assets = this.template.configuration.assets; + if (!!assets && assets.length > 0) { + for (let asset of assets) { + if (asset.stage === stage) { + asset = MustacheHelper.renderObject(asset, this.mustacheContext); + try { + await this.createAssetInternal(asset); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AssetCreationFailure, error); + throw error; + } + } + } + } + } + + public async getPipelineFilesToCommit(inputs: WizardInputs): Promise { + let workflowFile = await this.getWorkflowFile(inputs); + this.filesToCommit.push(workflowFile); + this.filesToCommit.forEach(async (file) => { + await this.localGitHelper.addContentToFile(file.content, file.path); + await vscode.window.showTextDocument(vscode.Uri.file(file.path)); + }); + return this.filesToCommit.map(x => x.path); + } + + private async createAssetInternal(asset: Asset): Promise { + if (!!asset) { + switch (asset.type) { + case "AzureCredentials:SPN": + const subscriptionId = this.inputs.subscriptionId; + this.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, subscriptionId) + }, + async () => { + const inputDescriptor = this.template.parameters.inputs.find((value) => (value.type === InputDataType.Authorization && value.id === "azureAuth")); + const scope = InputControl.getInputDescriptorProperty(inputDescriptor, "scope", this.inputs.pipelineConfiguration.params); + return this.getAzureSPNSecret(this.inputs, scope); + }); + break; + case "SetGHSecret": + let secretKey: string = asset.inputs["secretKey"]; + let secretValue: string = asset.inputs["secretvalue"]; + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.settingUpGithubSecrets + }, + async () => { + await this.githubClient.createOrUpdateGithubSecret(secretKey, secretValue); + } + ); + this.secrets[asset.id] = "{{ secrets." + secretKey + " }}"; + break; + case "commitFile:Github": + let source: string = asset.inputs["source"]; + let destination: string = asset.inputs["destination"]; + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.gettingTemplateFileAsset + }, + async () => { + destination = await this.getPathToFile(this.localGitHelper, Path.basename(destination), Path.dirname(destination)); + + let fileContent = await this.getTemplateFile(source); + this.filesToCommit.push({ path: destination, content: fileContent }); + } + ); + + this.assets[asset.id] = destination; + break; + case "LinuxWebAppNodeVersionConverter": + { + let nodeVersion: string = asset.inputs["webAppRuntimeNodeVersion"]; + const armUri = "/providers/Microsoft.Web/availableStacks?osTypeSelected=Linux&api-version=2019-08-01"; + this.assets[asset.id] = await nodeVersionConverter.webAppRuntimeNodeVersionConverter(nodeVersion, armUri, this.azureSession); + } + break; + case "WindowsWebAppNodeVersionConverter": + { + let nodeVersion: string = asset.inputs["webAppRuntimeNodeVersion"]; + const armUri = "/providers/Microsoft.Web/availableStacks?osTypeSelected=Windows&api-version=2019-08-01"; + this.assets[asset.id] = await nodeVersionConverter.webAppRuntimeNodeVersionConverter(nodeVersion, armUri, this.azureSession); + } + break; + case "AzureCredentials:PublishProfile": + { + let resourceId = asset.inputs["resourceId"]; + let parsedResourceId = new ParsedAzureResourceId(resourceId); + let subscriptionId = parsedResourceId.subscriptionId; + + this.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, subscriptionId) + }, + async () => { + try { + // find LCS of all azure resource params + let appServiceClient = new AppServiceClient(this.azureSession.credentials, this.azureSession.environment, this.azureSession.tenantId, subscriptionId); + return await appServiceClient.getWebAppPublishProfileXml(resourceId); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzurePublishProfileCreationFailure, error); + throw error; + } + }); + } + break; + default: + throw new Error(utils.format(Messages.assetOfTypeNotSupported, asset.type)); + } + } + } + + private async getWorkflowFile(inputs: WizardInputs): Promise { + let pipelineDefinition = MustacheHelper.renderObject(this.template.configuration.pipelineDefinition, this.mustacheContext); + let workflowFileContent: string; + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.gettingWorkflowFile + }, + async () => { + workflowFileContent = await this.getTemplateFile(pipelineDefinition.templateFile); + } + ); + let workFlowFileName: string = pipelineDefinition.destinationFileName; + workFlowFileName = await this.getPathToPipelineFile(inputs, this.localGitHelper, workFlowFileName); + inputs.pipelineConfiguration.filePath = workFlowFileName; + return { + path: workFlowFileName, + content: workflowFileContent + }; + } + + private async getTemplateFile(fileName: string): Promise { + try { + let result = await this.templateServiceClient.getTemplateFile(this.template.id, fileName); + if (result.length > 0) { + let templateFile = result.find((value) => value.id === fileName); + if (templateFile) { + let content = templateConverter.convertToLocalMustacheExpression(templateFile.content); + return MustacheHelper.render(content, this.mustacheContext); + } + } + throw new Error(utils.format(Messages.templateFileNotFound, fileName)); + } catch (error) { + telemetryHelper.logError(Layer, TracePoints.UnableToGetTemplateFile, error); + throw error; + } + } +} \ No newline at end of file diff --git a/Source/configure/helper/AssetHandler.ts b/Source/configure/helper/AssetHandler.ts new file mode 100644 index 00000000..ede21d64 --- /dev/null +++ b/Source/configure/helper/AssetHandler.ts @@ -0,0 +1,169 @@ +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { AppServiceClient } from '../clients/azure/appServiceClient'; +import { ArmRestClient } from '../clients/azure/armRestClient'; +import { UniqueResourceNameSuffix } from '../configure'; +import { TargetResourceType, WizardInputs } from "../model/models"; +import { LocalPipelineTemplate, TemplateAsset, TemplateAssetType, TemplateParameterType } from '../model/templateModels'; +import { Messages } from '../resources/messages'; +import { TracePoints } from '../resources/tracePoints'; +import { GraphHelper } from './graphHelper'; +import { SodiumLibHelper } from './sodium/SodiumLibHelper'; +import { telemetryHelper } from './telemetryHelper'; +import { TemplateParameterHelper } from './templateParameterHelper'; + +const Layer = "AssetCreationHandler"; + +export class AssetHandler { + // tslint:disable-next-line:no-reserved-keywords + public async createAssets(assets: TemplateAsset[], inputs: WizardInputs, createAsset: (name: string, type: TemplateAssetType, data: any, inputs: WizardInputs) => Promise): Promise { + if (inputs.pipelineConfiguration.template.label === "Containerized application to AKS") { + if (!!assets && assets.length > 0) { + for (let asset of assets) { + await this.createAssetInternal(asset, inputs, createAsset); + } + } + } + } + + // tslint:disable-next-line:no-reserved-keywords + private async createAssetInternal(asset: TemplateAsset, inputs: WizardInputs, createAsset: (name: string, type: TemplateAssetType, data: any, inputs: WizardInputs) => Promise): Promise { + if (!!asset) { + switch (asset.type) { + case TemplateAssetType.AzureARMServiceConnection: + inputs.pipelineConfiguration.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, inputs.subscriptionId) + }, + async () => { + try { + // find LCS of all azure resource params + let scope = inputs.pipelineConfiguration.params["targetResource"].id; + let aadAppName = GraphHelper.generateAadApplicationName(inputs.organizationName, inputs.project.name); + let aadApp = await GraphHelper.createSpnAndAssignRole(inputs.azureSession, aadAppName, scope); + // Use param name for first azure resource param + let serviceConnectionName = `${inputs.pipelineConfiguration.params[(inputs.pipelineConfiguration.template as LocalPipelineTemplate).parameters.find((parameter) => parameter.type === TemplateParameterType.GenericAzureResource).name]}-${UniqueResourceNameSuffix}`; + return await createAsset(serviceConnectionName, asset.type, { "aadApp": aadApp, "scope": scope }, inputs); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzureServiceConnectionCreateFailure, error); + throw error; + } + }); + break; + case TemplateAssetType.AzureARMPublishProfileServiceConnection: + let targetWebAppResource = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.WebApp); + inputs.pipelineConfiguration.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingAzureServiceConnection, inputs.subscriptionId) + }, + async () => { + try { + // find LCS of all azure resource params + let appServiceClient = new AppServiceClient(inputs.azureSession.credentials, inputs.azureSession.environment, inputs.azureSession.tenantId, inputs.subscriptionId); + let publishProfile = await appServiceClient.getWebAppPublishProfileXml(inputs.targetResource.resource.id); + let serviceConnectionName = `${targetWebAppResource.name}-${UniqueResourceNameSuffix}`; + return await createAsset(serviceConnectionName, asset.type, publishProfile, inputs); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AzureServiceConnectionCreateFailure, error); + throw error; + } + }); + break; + // uses azure resource client to get the required details, and then calls into configurer.createServiceConnection(serviceConnectionType, properties: property bag with all the required information that are needed/available to create service connection.) + case TemplateAssetType.AKSKubeConfigServiceConnection: + case TemplateAssetType.GitHubAKSKubeConfig: + let targetAksResource = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.AKS); + inputs.pipelineConfiguration.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingKubernetesConnection, targetAksResource.name) + }, + async () => { + try { + let armClient = new ArmRestClient(inputs.azureSession); + let base64EncodedKubeConfig: { kubeconfigs: Array<{ name: string, value: string }> } = await armClient.getAksKubeConfig(targetAksResource.id); + let assetName = AssetHandler.getSanitizedUniqueAssetName(targetAksResource.name); + return await createAsset(assetName, asset.type, SodiumLibHelper.decodeFromBase64(JSON.stringify(base64EncodedKubeConfig.kubeconfigs[0].value)), inputs); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AssetCreationFailure, error); + throw error; + } + }); + break; + case TemplateAssetType.ACRServiceConnection: + let targetAcrResource = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.ACR); + inputs.pipelineConfiguration.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingContainerRegistryConnection, targetAcrResource.name) + }, + async () => { + try { + let armClient = new ArmRestClient(inputs.azureSession); + let registryCreds: { username: string, passwords: Array<{ name: string, value: string }> } = await armClient.getAcrCredentials(targetAcrResource.id); + let assetName = AssetHandler.getSanitizedUniqueAssetName(targetAcrResource.name); + return await createAsset(assetName, asset.type, registryCreds, inputs); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AssetCreationFailure, error); + throw error; + } + }); + break; + case TemplateAssetType.GitHubRegistryUsername: + case TemplateAssetType.GitHubRegistryPassword: + let acrResource = TemplateParameterHelper.getParameterValueForTargetResourceType(inputs.pipelineConfiguration, TargetResourceType.ACR); + inputs.pipelineConfiguration.assets[asset.id] = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: utils.format(Messages.creatingContainerRegistryConnection, acrResource.name) + }, + async () => { + try { + let armClient = new ArmRestClient(inputs.azureSession); + let registryCreds: { username: string, passwords: Array<{ name: string, value: string }> } = await armClient.getAcrCredentials(acrResource.id); + let assetName = AssetHandler.getSanitizedUniqueAssetName(acrResource.name); + if (asset.type === TemplateAssetType.GitHubRegistryUsername) { + return await createAsset(assetName + "_username", asset.type, registryCreds.username, inputs); + } + else { + let password = !!registryCreds.passwords && registryCreds.passwords.length > 0 ? registryCreds.passwords[0].value : null; + if (!password) { + throw Messages.unableToFetchPasswordOfContainerRegistry; + } + + return await createAsset(assetName + "_password", asset.type, password, inputs); + } + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.AssetCreationFailure, error); + throw error; + } + }); + break; + + case TemplateAssetType.File: + break; + case TemplateAssetType.GitHubARM: + case TemplateAssetType.GitHubARMPublishProfile: + default: + throw new Error(utils.format(Messages.assetOfTypeNotSupported, asset.type)); + } + } + } + + /** + * @param assetName : the asset name you need sanitized + * @returns sanitized asset name and makes it unique by appending 5 digit random alpha numeric string to asset name. + */ + public static getSanitizedUniqueAssetName(assetName: string): string { + assetName = assetName + "_" + UniqueResourceNameSuffix; + assetName = assetName.replace(/\W/g, ''); + return assetName; + } +} \ No newline at end of file diff --git a/Source/configure/helper/DataSourceHandler.ts b/Source/configure/helper/DataSourceHandler.ts new file mode 100644 index 00000000..b00363b8 --- /dev/null +++ b/Source/configure/helper/DataSourceHandler.ts @@ -0,0 +1,29 @@ +import { RestClient } from "../clients/restClient"; +import { ParsedAzureResourceId, WizardInputs } from "../model/models"; +import { LocalPipelineTemplate, PreDefinedDataSourceIds } from "../model/templateModels"; + +export class DataSourceHandler { + public getResultFromPreDefinedDataSourceId(dataSourceId: string, inputs: WizardInputs): Promise { + switch (dataSourceId) { + case PreDefinedDataSourceIds.AKS: + let restClient = new RestClient(inputs.azureSession.credentials); + let parsedResourceId = new ParsedAzureResourceId(inputs.pipelineConfiguration.params[(inputs.pipelineConfiguration.template as LocalPipelineTemplate).parameters.find((param) => { return param.dataSourceId === PreDefinedDataSourceIds.AKS; }).name].id); + let kubeConfig = restClient.sendRequestWithHttpOperationResponse( + { + url: inputs.azureSession.environment.portalUrl + `/subscriptions/${parsedResourceId.subscriptionId}/resourceGroups/${parsedResourceId.resourceGroup}/providers/Microsoft.ContainerService/managedClusters/${parsedResourceId.resourceName}/listClusterAdminCredential?api-version=2020-01-01`, + method: "POST", + deserializationMapper: null, + serializationMapper: null + }); + return kubeConfig; + case PreDefinedDataSourceIds.ACR: + case PreDefinedDataSourceIds.FunctionApp: + case PreDefinedDataSourceIds.LinuxApp: + case PreDefinedDataSourceIds.LinuxContainerApp: + case PreDefinedDataSourceIds.LinuxFunctionApp: + case PreDefinedDataSourceIds.WindowsApp: + default: + return null; + } + } +} \ No newline at end of file diff --git a/Source/configure/helper/LocalGitRepoHelper.ts b/Source/configure/helper/LocalGitRepoHelper.ts new file mode 100644 index 00000000..207bcf9f --- /dev/null +++ b/Source/configure/helper/LocalGitRepoHelper.ts @@ -0,0 +1,210 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as Q from 'q'; +import * as git from 'simple-git/promise'; +import { RemoteWithoutRefs } from 'simple-git/typings/response'; +import * as vscode from 'vscode'; +import { Configurer } from '../configurers/configurerBase'; +import { GitBranchDetails, GitRepositoryParameters, MustacheContext, WizardInputs } from '../model/models'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from "../resources/telemetryKeys"; +import { TracePoints } from '../resources/tracePoints'; +import { AzureDevOpsHelper } from './devOps/azureDevOpsHelper'; +import { GitHubProvider } from './gitHubHelper'; +import { telemetryHelper } from "./telemetryHelper"; +import * as templateHelper from './templateHelper'; + +const Layer = 'LocalGitRepoHelper'; + +export class LocalGitRepoHelper { + + public static GetHelperInstance(repositoryPath: string): LocalGitRepoHelper { + var repoService = new LocalGitRepoHelper(); + repoService.initialize(repositoryPath); + + let gitFolderExists = fs.existsSync(path.join(repositoryPath, ".git")); + telemetryHelper.setTelemetry(TelemetryKeys.GitFolderExists, gitFolderExists.toString()); + + return repoService; + } + + public static async GetAvailableFileName(fileName: string, repoPath: string): Promise { + let deferred: Q.Deferred = Q.defer(); + fs.readdir(repoPath, (err, files: string[]) => { + if (files.indexOf(fileName) < 0) { + deferred.resolve(fileName); + } + else { + for (let i = 1; i < 100; i++) { + let increamentalFileName = LocalGitRepoHelper.getIncreamentalFileName(fileName, i); + if (files.indexOf(increamentalFileName) < 0) { + deferred.resolve(increamentalFileName); + } + } + } + }); + + return deferred.promise; + } + + private static getIncreamentalFileName(fileName: string, count: number): string { + return fileName.substr(0, fileName.indexOf('.')).concat(` (${count})`, fileName.substr(fileName.indexOf('.'))); + } + + private gitReference: git.SimpleGit; + private constructor() { + } + + public async getUsername(): Promise { + let username = await this.gitReference.raw([ + 'config', + 'user.name' + ]); + + return username; + } + + public async isGitRepository(): Promise { + try { + await this.gitReference.status(); + return true; + } + catch (error) { + return false; + } + } + + public async getGitBranchDetails(): Promise { + let status = await this.gitReference.status(); + let branch = status.current; + let remote = status.tracking ? status.tracking.substr(0, status.tracking.indexOf(branch) - 1) : null; + + return { + branch: branch, + remoteName: remote + }; + } + + public async getGitRemotes(): Promise { + return this.gitReference.getRemotes(false); + } + + public async getGitRemoteUrl(remoteName: string): Promise { + let remoteUrl = await this.gitReference.remote(["get-url", remoteName]); + if (remoteUrl) { + remoteUrl = (remoteUrl).trim(); + if (remoteUrl[remoteUrl.length - 1] === '/') { + remoteUrl = remoteUrl.substr(0, remoteUrl.length - 1); + } + } + + if (AzureDevOpsHelper.isAzureReposUrl(remoteUrl)) { + remoteUrl = AzureDevOpsHelper.getFormattedRemoteUrl(remoteUrl); + } + else if (GitHubProvider.isGitHubUrl(remoteUrl)) { + remoteUrl = GitHubProvider.getFormattedRemoteUrl(remoteUrl); + } + return remoteUrl; + } + + /** + * + * @param pathToFile : local path of yaml pipeline in the extension + * @param content: inputs required to be filled in the yaml pipelines + * @returns: thenable object which resolves once all files are added to the repository + */ + public async addContentToFile(content: string, pathToFile: string): Promise { + fs.writeFileSync(pathToFile, content); + await vscode.workspace.saveAll(true); + return pathToFile; + } + + /** + * commits yaml pipeline file into the local repo and pushes the commit to remote branch. + * @param files : array of local path of yaml files in the repository + * @returns: thenable string which resolves to commitId once commit is pushed to remote branch, and failure message if unsuccessful + */ + public async commitAndPushPipelineFile(files: string[], repositoryDetails: GitRepositoryParameters, commitMessage: string): Promise { + try { + await this.gitReference.add(files); + await this.gitReference.commit(commitMessage, files); + let gitLog = await this.gitReference.log(); + + if (repositoryDetails.remoteName && repositoryDetails.branch) { + await this.gitReference.push(repositoryDetails.remoteName, repositoryDetails.branch, { + "--set-upstream": null + }); + } + else { + throw new Error(Messages.cannotAddFileRemoteMissing); + } + + return gitLog.latest.hash; + + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.CommitAndPushPipelineFileFailed, error); + throw error; + } + } + + public async getGitRootDirectory(): Promise { + let gitRootDir = await this.gitReference.revparse(["--show-toplevel"]); + return path.normalize(gitRootDir.trim()); + } + + public async initializeGitRepository(remoteName: string, remoteUrl: string, filesToExcludeRegex?: string): Promise { + try { + let isGitRepository = await this.isGitRepository(); + + if (!isGitRepository) { + await this.gitReference.init(); + } + + try { + // Try to see if there are any commits + await this.gitReference.log(); + } + catch (error) { + // Commit all files if there are not commits on this branch + await this.gitReference.add(`:!${filesToExcludeRegex}`); + await this.gitReference.commit("Initialized git repository"); + } + + await this.gitReference.addRemote(remoteName, remoteUrl); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.InitializeGitRepositoryFailed, error); + throw error; + } + } + + public async createAndDisplayManifestFile(manifestFile: string, pipelineConfigurer: Configurer, filesToCommit: string[], inputs: WizardInputs, targetfileName: string = null) { + let targetFile: string = targetfileName ? targetfileName : manifestFile; + let manifestPath: string = path.join(path.dirname(__dirname), "/templates/dependencies/"); + let mustacheContext = new MustacheContext(inputs); + let manifestFilePath: string; + + manifestFilePath = await pipelineConfigurer.getPathToManifestFile(inputs, this, targetFile + '.yml'); + inputs.pipelineConfiguration.assets[manifestFile] = path.relative(await this.getGitRootDirectory(), manifestFilePath).split(path.sep).join('/'); + filesToCommit.push(manifestFilePath); + await this.addContentToFile( + await templateHelper.renderContent(manifestPath + manifestFile + '.yml', mustacheContext), + manifestFilePath); + await vscode.window.showTextDocument(vscode.Uri.file(manifestFilePath)); + } + + public async readFileContent(pathToFile: string): Promise { + const buf = fs.readFileSync(pathToFile); + return buf.toString(); + } + + public async writeFileContent(content: string, pathToFile: string): Promise { + fs.writeFileSync(pathToFile, content); + return pathToFile; + } + + private initialize(repositoryPath: string): void { + this.gitReference = git(repositoryPath); + } +} diff --git a/Source/configure/helper/azureSessionHelper.ts b/Source/configure/helper/azureSessionHelper.ts new file mode 100644 index 00000000..127bacef --- /dev/null +++ b/Source/configure/helper/azureSessionHelper.ts @@ -0,0 +1,35 @@ +import { SubscriptionModels } from "azure-arm-resource"; +import { isNullOrUndefined } from "util"; +import { AzureSession, extensionVariables } from "../model/models"; +import { Messages } from "../resources/messages"; +import { WhiteListedError } from "../utilities/utilities"; + +export async function getSubscriptionSession(subscriptionId: string): Promise { + if (!(await extensionVariables.azureAccountExtensionApi.waitForSubscriptions())) { + throw new Error(Messages.AzureLoginError); + } + + let currentSubscription: { session: AzureSession, subscription: SubscriptionModels.Subscription } = extensionVariables.azureAccountExtensionApi.subscriptions + .find((subscription) => + subscription.subscription.subscriptionId.toLowerCase() === subscriptionId.toLowerCase()); + + // Fallback to first element + if (!currentSubscription) { + currentSubscription = extensionVariables.azureAccountExtensionApi.subscriptions[0]; + } + + return currentSubscription.session; +} + +export async function getAzureSession(): Promise { + if (!(await extensionVariables.azureAccountExtensionApi.waitForSubscriptions())) { + throw new Error(Messages.AzureLoginError); + } + + const currentSubscription = extensionVariables.azureAccountExtensionApi.subscriptions[0]; + if (isNullOrUndefined(currentSubscription)) { + throw new WhiteListedError(Messages.NoAzureSubscriptionFound); + } + + return currentSubscription.session; +} \ No newline at end of file diff --git a/Source/configure/helper/commonHelper.ts b/Source/configure/helper/commonHelper.ts new file mode 100644 index 00000000..ae0dacd9 --- /dev/null +++ b/Source/configure/helper/commonHelper.ts @@ -0,0 +1,110 @@ +import Q = require('q'); +import * as util from 'util'; +import * as logger from '../../logger'; +import { GithubClient } from '../clients/github/githubClient'; +import { GitHubRepo } from '../model/models'; +import { Messages } from '../resources/messages'; + +const uuid = require('uuid/v4'); + +export async function sleepForMilliSeconds(timeInMs: number): Promise { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, timeInMs); + }); +} + +export async function generateGitHubRepository(orgName: string, localPath: string, githubClient: GithubClient, isUserAccount: boolean = false): Promise { + let repoName = localPath.substring(localPath.lastIndexOf("\\") + 1).substring(localPath.lastIndexOf("/") + 1); + let repoDetails = await githubClient.createGithubRepo(orgName, repoName, isUserAccount) as GitHubRepo; + // Case : GitHub Repository name is same as the local repo + if (repoDetails) { + return repoDetails; + } + //Case: Local repository name is not available, therefore organization name has been appended + repoName = repoName + "_" + orgName; + repoDetails = await githubClient.createGithubRepo(orgName, repoName, isUserAccount) as GitHubRepo; + if (repoDetails) { + return repoDetails; + } + //Case: If the above two repository names are not available, uuid is also appended + return await githubClient.createGithubRepo(orgName, repoName + "_" + uuid().substr(0, 5), isUserAccount); +} + +export function generateDevOpsOrganizationName(userName: string, repositoryName: string): string { + let repositoryNameSuffix = repositoryName.replace("/", "-").trim(); + let organizationName = `${userName}-${repositoryNameSuffix}`; + + // Name cannot start or end with whitespaces, cannot start with '-', cannot contain characters other than a-z|A-Z|0-9 + organizationName = organizationName.trim().replace(/^[-]+/, '').replace(/[^a-zA-Z0-9-]/g, ''); + if (organizationName.length > 50) { + organizationName = organizationName.substr(0, 50); + } + + return organizationName; +} + +export function generateDevOpsProjectName(repositoryName?: string): string { + if (!repositoryName) { + return "AzurePipelines"; + } + + let repoParts = repositoryName.split("/"); + let suffix = repoParts[repoParts.length - 1]; + suffix = suffix.trim(); + // project name cannot end with . or _ + suffix = suffix.replace(/\.[\.]*$/, '').replace(/^_[_]*$/, ''); + + let projectName = `AzurePipelines-${suffix}`; + if (projectName.length > 64) { + projectName = projectName.substr(0, 64); + } + + return projectName; +} + +export function generateRandomPassword(length: number = 20): string { + var characters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#%^*()-+"; + var charTypeSize = new Array(26, 26, 10, 10); + var charTypeStartIndex = new Array(0, 26, 52, 62); + var password = ""; + for (var x = 0; x < length; x++) { + var i = Math.floor(Math.random() * charTypeSize[x % 4]); + password += characters.charAt(i + charTypeStartIndex[x % 4]); + } + return password; +} + +export function stringCompareFunction(a: string, b: string): number { + a = a && a.toLowerCase(); + b = b && b.toLowerCase(); + if (a < b) { + return -1; + } + else if (a > b) { + return 1; + } + return 0; +} + +export async function executeFunctionWithRetry( + func: () => Promise, + retryCount: number = 20, + retryIntervalTimeInSec: number = 2, + errorMessage?: string): Promise { + let internalError = null; + for (; retryCount > 0; retryCount--) { + try { + let result = await func(); + return result; + } + catch (error) { + internalError = error; + logger.log(JSON.stringify(error)); + await Q.delay((resolve) => { resolve(); }, retryIntervalTimeInSec * 1000); + } + } + + throw errorMessage ? errorMessage.concat(util.format(Messages.retryFailedMessage, retryCount, JSON.stringify(internalError))) : util.format(Messages.retryFailedMessage, retryCount, JSON.stringify(internalError)); +} \ No newline at end of file diff --git a/Source/configure/helper/controlProvider.ts b/Source/configure/helper/controlProvider.ts new file mode 100644 index 00000000..706b6ce5 --- /dev/null +++ b/Source/configure/helper/controlProvider.ts @@ -0,0 +1,46 @@ +import { InputBoxOptions, QuickPickItem, window } from 'vscode'; +import { IAzureQuickPickOptions, UserCancelledError } from 'vscode-azureextensionui'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { extensionVariables } from '../model/models'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from '../resources/telemetryKeys'; + +export class ControlProvider { + public async showQuickPick(listName: string, listItems: T[] | Thenable, options: IAzureQuickPickOptions, itemCountTelemetryKey?: string): Promise { + try { + telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, listName); + return await extensionVariables.ui.showQuickPick(listItems, options); + } + finally { + if (itemCountTelemetryKey) { + telemetryHelper.setTelemetry(itemCountTelemetryKey, (await listItems).length.toString()); + } + } + } + + public async showInputBox(inputName: string, options: InputBoxOptions): Promise { + telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, inputName); + return await extensionVariables.ui.showInputBox(options); + } + + public async showInformationBox(informationIdentifier: string, informationMessage: string, ...actions: string[]): Promise { + telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, informationIdentifier); + if (!!actions && actions.length > 0) { + let result = await window.showInformationMessage(informationMessage, ...actions); + if (!result) { + throw new UserCancelledError(Messages.userCancelledExcecption); + } + + return result; + } + else { + return await window.showInformationMessage(informationMessage, ...actions); + } + + } + + public async showErrorMessage(informationIdentifier: string, errorMessage: string): Promise { + telemetryHelper.setTelemetry(TelemetryKeys.CurrentUserInput, informationIdentifier); + await window.showErrorMessage(errorMessage); + } +} diff --git a/Source/configure/helper/devOps/azureDevOpsHelper.ts b/Source/configure/helper/devOps/azureDevOpsHelper.ts new file mode 100644 index 00000000..14a5caa4 --- /dev/null +++ b/Source/configure/helper/devOps/azureDevOpsHelper.ts @@ -0,0 +1,171 @@ +import * as path from 'path'; +import * as util from 'util'; +import { AzureDevOpsClient } from '../../clients/devOps/azureDevOpsClient'; +import { Build, BuildDefinition, BuildDefinitionRepositoryProperties } from '../../model/azureDevOps'; +import { RepositoryProvider, WizardInputs } from '../../model/models'; +import { HostedVS2017QueueName } from '../../resources/constants'; +import { Messages } from '../../resources/messages'; +import { TracePoints } from '../../resources/tracePoints'; +import { telemetryHelper } from '../telemetryHelper'; + +const Layer: string = 'azureDevOpsHelper'; + +export class AzureDevOpsHelper { + private static AzureReposUrl = 'dev.azure.com/'; + private static SSHAzureReposUrl = 'ssh.dev.azure.com:v3/'; + private static VSOUrl = '.visualstudio.com/'; + private static SSHVsoReposUrl = 'vs-ssh.visualstudio.com:v3/'; + + private azureDevOpsClient: AzureDevOpsClient; + + constructor(azureDevOpsClient: AzureDevOpsClient) { + this.azureDevOpsClient = azureDevOpsClient; + } + + public static isAzureReposUrl(remoteUrl: string): boolean { + return (remoteUrl.indexOf(AzureDevOpsHelper.AzureReposUrl) >= 0 || remoteUrl.indexOf(AzureDevOpsHelper.VSOUrl) >= 0 || remoteUrl.indexOf(AzureDevOpsHelper.SSHAzureReposUrl) >= 0 || remoteUrl.indexOf(AzureDevOpsHelper.SSHVsoReposUrl) >= 0); + } + + public static getFormattedRemoteUrl(remoteUrl: string): string { + // Convert SSH based url to https based url as pipeline service doesn't accept SSH based URL + if (remoteUrl.indexOf(AzureDevOpsHelper.SSHAzureReposUrl) >= 0 || remoteUrl.indexOf(AzureDevOpsHelper.SSHVsoReposUrl) >= 0) { + let details = AzureDevOpsHelper.getRepositoryDetailsFromRemoteUrl(remoteUrl); + return `https://${details.organizationName}${AzureDevOpsHelper.VSOUrl}/${details.projectName}/_git/${details.repositoryName}`; + } + + return remoteUrl; + } + + public static getRepositoryDetailsFromRemoteUrl(remoteUrl: string): { organizationName: string, projectName: string, repositoryName: string } { + if (remoteUrl.indexOf(AzureDevOpsHelper.AzureReposUrl) >= 0) { + let part = remoteUrl.substr(remoteUrl.indexOf(AzureDevOpsHelper.AzureReposUrl) + AzureDevOpsHelper.AzureReposUrl.length); + let parts = part.split('/').filter((value) => !!value); + if (parts.length !== 4) { + telemetryHelper.logError(Layer, TracePoints.GetRepositoryDetailsFromRemoteUrlFailed, new Error(`RemoteUrlFormat: ${AzureDevOpsHelper.AzureReposUrl}, Parts: ${parts.slice(2).toString()}, Length: ${parts.length}`)); + throw new Error(Messages.failedToDetermineAzureRepoDetails); + } + return { organizationName: parts[0].trim(), projectName: parts[1].trim(), repositoryName: parts[3].trim() }; + } + else if (remoteUrl.indexOf(AzureDevOpsHelper.VSOUrl) >= 0) { + let part = remoteUrl.substr(remoteUrl.indexOf(AzureDevOpsHelper.VSOUrl) + AzureDevOpsHelper.VSOUrl.length); + let organizationName = remoteUrl.substring(remoteUrl.indexOf('https://') + 'https://'.length, remoteUrl.indexOf('.visualstudio.com')); + let parts = part.split('/').filter((value) => !!value); + + if (parts.length === 4 && parts[0].toLowerCase() === 'defaultcollection') { + // Handle scenario where part is 'DefaultCollection//_git/' + parts = parts.slice(1); + } + + if (parts.length !== 3) { + telemetryHelper.logError(Layer, TracePoints.GetRepositoryDetailsFromRemoteUrlFailed, new Error(`RemoteUrlFormat: ${AzureDevOpsHelper.VSOUrl}, Parts: ${parts.slice(1).toString()}, Length: ${parts.length}`)); + throw new Error(Messages.failedToDetermineAzureRepoDetails); + } + return { organizationName: organizationName, projectName: parts[0].trim(), repositoryName: parts[2].trim() }; + } + else if (remoteUrl.indexOf(AzureDevOpsHelper.SSHAzureReposUrl) >= 0 || remoteUrl.indexOf(AzureDevOpsHelper.SSHVsoReposUrl) >= 0) { + let urlFormat = remoteUrl.indexOf(AzureDevOpsHelper.SSHAzureReposUrl) >= 0 ? AzureDevOpsHelper.SSHAzureReposUrl : AzureDevOpsHelper.SSHVsoReposUrl; + let part = remoteUrl.substr(remoteUrl.indexOf(urlFormat) + urlFormat.length); + let parts = part.split('/').filter((value) => !!value); + if (parts.length !== 3) { + telemetryHelper.logError(Layer, TracePoints.GetRepositoryDetailsFromRemoteUrlFailed, new Error(`RemoteUrlFormat: ${urlFormat}, Parts: ${parts.slice(2).toString()}, Length: ${parts.length}`)); + throw new Error(Messages.failedToDetermineAzureRepoDetails); + } + return { organizationName: parts[0].trim(), projectName: parts[1].trim(), repositoryName: parts[2].trim() }; + } + else { + throw new Error(Messages.notAzureRepoUrl); + } + } + + public async createAndRunPipeline(pipelineName: string, inputs: WizardInputs): Promise { + try { + let buildDefinitionPayload = await this.getBuildDefinitionPayload(pipelineName, inputs); + let definition = await this.azureDevOpsClient.createBuildDefinition(inputs.organizationName, buildDefinitionPayload); + let build = await this.azureDevOpsClient.queueBuild(inputs.organizationName, this.getQueueBuildPayload(inputs, definition.id, definition.project.id)); + return build; + } + catch (error) { + throw new Error(util.format(Messages.failedToCreateAzurePipeline, error.message)); + } + } + + private async getBuildDefinitionPayload(pipelineName: string, inputs: WizardInputs): Promise { + let queueId = await this.getAgentQueueId(inputs.organizationName, inputs.project.name, HostedVS2017QueueName); + let repositoryProperties: BuildDefinitionRepositoryProperties = null; + let properties = { 'source': 'ms-azure-devops.azure-pipelines' }; + + if (inputs.sourceRepository.repositoryProvider === RepositoryProvider.Github) { + repositoryProperties = { + apiUrl: `https://api.github.com/repos/${inputs.sourceRepository.repositoryId}`, + branchesUrl: `https://api.github.com/repos/${inputs.sourceRepository.repositoryId}/branches`, + cloneUrl: inputs.sourceRepository.remoteUrl, + connectedServiceId: inputs.sourceRepository.serviceConnectionId, + defaultBranch: inputs.sourceRepository.branch, + fullName: inputs.sourceRepository.repositoryName, + refsUrl: `https://api.github.com/repos/${inputs.sourceRepository.repositoryId}/git/refs` + }; + } + + return { + name: pipelineName, + type: 2, //YAML process type + quality: 1, // Defintion=1, Draft=0 + path: "\\", //Folder path of build definition. Root folder in this case + project: { + id: inputs.project.id, + name: inputs.project.name + }, + process: { + type: 2, + yamlFileName: path.basename(inputs.pipelineConfiguration.filePath) // As required, it will be at the root location and should be same as committed pipeline file + }, + queue: { + id: queueId // Default queue Hosted VS 2017. This value is overriden by queue specified in YAML + }, + triggers: [ + { + triggerType: 2, // Continuous integration trigger type + settingsSourceType: 2, // Use trigger source as specified in YAML + batchChanges: false + } + ], + repository: { + id: inputs.sourceRepository.repositoryId, + name: inputs.sourceRepository.repositoryName, + type: inputs.sourceRepository.repositoryProvider, + defaultBranch: inputs.sourceRepository.branch, + url: inputs.sourceRepository.remoteUrl, + properties: repositoryProperties + }, + properties: properties + }; + } + + private async getAgentQueueId(organizationName: string, projectName: string, poolName: string): Promise { + let queues = await this.azureDevOpsClient.getAgentQueues(organizationName, projectName); + let queueId: number = queues.length > 0 ? queues[0].id : null; + + for (let queue of queues) { + if (queue.pool && queue.pool.name && queue.pool.name.toLowerCase() === poolName.toLowerCase()) { + queueId = queue.id; + break; + } + } + + if (queueId !== null) { + return queueId; + } + + throw new Error(util.format(Messages.noAgentQueueFound, poolName)); + } + + private getQueueBuildPayload(inputs: WizardInputs, buildDefinitionId: number, projectId: string): Build { + return { + id: '', + definition: { id: buildDefinitionId }, + project: { id: projectId }, + sourceBranch: inputs.sourceRepository.branch, + sourceVersion: inputs.sourceRepository.commitId + }; + } +} \ No newline at end of file diff --git a/Source/configure/helper/devOps/serviceConnectionHelper.ts b/Source/configure/helper/devOps/serviceConnectionHelper.ts new file mode 100644 index 00000000..757573c4 --- /dev/null +++ b/Source/configure/helper/devOps/serviceConnectionHelper.ts @@ -0,0 +1,123 @@ +import * as util from 'util'; +import { AzureDevOpsClient } from '../../clients/devOps/azureDevOpsClient'; +import { ServiceConnectionClient } from '../../clients/devOps/serviceConnectionClient'; +import { AadApplication } from '../../model/models'; +import { Messages } from '../../resources/messages'; + +export class ServiceConnectionHelper { + private serviceConnectionClient: ServiceConnectionClient; + + public constructor(organizationName: string, projectName: string, azureDevOpsClient: AzureDevOpsClient) { + this.serviceConnectionClient = new ServiceConnectionClient(organizationName, projectName, azureDevOpsClient); + } + + public async createGitHubServiceConnection(name: string, gitHubPat: string): Promise { + let response = await this.serviceConnectionClient.createGitHubServiceConnection(name, gitHubPat); + let endpointId: string = response.id; + await this.waitForGitHubEndpointToBeReady(endpointId); + await this.serviceConnectionClient.authorizeEndpointForAllPipelines(endpointId) + .then((response) => { + if (response.allPipelines.authorized !== true) { + throw new Error(Messages.couldNotAuthorizeEndpoint); + } + }); + + return endpointId; + } + + public async createAzureSPNServiceConnection(name: string, tenantId: string, subscriptionId: string, scope: string, aadApp: AadApplication): Promise { + let response = await this.serviceConnectionClient.createAzureSPNServiceConnection(name, tenantId, subscriptionId, scope, aadApp); + let endpointId = response.id; + await this.waitForEndpointToBeReady(endpointId); + await this.serviceConnectionClient.authorizeEndpointForAllPipelines(endpointId) + .then((response) => { + if (response.allPipelines.authorized !== true) { + throw new Error(Messages.couldNotAuthorizeEndpoint); + } + }); + return endpointId; + } + + public async createAzurePublishProfileServiceConnection(name: string, tenantId: string, resourceId: string, publishProfile: string): Promise { + let response = await this.serviceConnectionClient.createAzurePublishProfileServiceConnection(name, tenantId, resourceId, publishProfile); + let endpointId = response.id; + await this.waitForEndpointToBeReady(endpointId); + await this.serviceConnectionClient.authorizeEndpointForAllPipelines(endpointId) + .then((response) => { + if (response.allPipelines.authorized !== true) { + throw new Error(Messages.couldNotAuthorizeEndpoint); + } + }); + return endpointId; + } + + public async createKubeConfigServiceConnection(name: string, kubeConfig: string, apiServerAddress: string): Promise { + let response = await this.serviceConnectionClient.createKubernetesServiceConnectionWithKubeConfig(name, kubeConfig, apiServerAddress); + let endpointId = response.id; + await this.serviceConnectionClient.authorizeEndpointForAllPipelines(endpointId) + .then((response) => { + if (response.allPipelines.authorized !== true) { + throw new Error(Messages.couldNotAuthorizeEndpoint); + } + }); + return endpointId; + } + + public async createContainerRegistryServiceConnection(name: string, registryUrl: string, registryUsername: string, registryPassword?: string): Promise { + let response = await this.serviceConnectionClient.createContainerRegistryServiceConnection(name, registryUrl, registryUsername, registryPassword); + let endpointId = response.id; + await this.serviceConnectionClient.authorizeEndpointForAllPipelines(endpointId) + .then((response) => { + if (response.allPipelines.authorized !== true) { + throw new Error(Messages.couldNotAuthorizeEndpoint); + } + }); + return endpointId; + } + + private async waitForEndpointToBeReady(endpointId: string): Promise { + let retryCount = 1; + while (1) { + let response = await this.serviceConnectionClient.getEndpointStatus(endpointId); + let operationStatus = response.operationStatus; + + if (response.isReady) { + break; + } + + if (!(retryCount < 30) || operationStatus.state.toLowerCase() === "failed") { + throw Error(util.format(Messages.unableToCreateAzureServiceConnection, operationStatus.state, operationStatus.statusMessage)); + } + + await this.sleepForMilliSeconds(2000); + retryCount++; + } + } + + private async waitForGitHubEndpointToBeReady(endpointId: string): Promise { + let retryCount = 1; + while (1) { + let response = await this.serviceConnectionClient.getEndpointStatus(endpointId); + let isReady: boolean = response.isReady; + + if (isReady === true) { + break; + } + + if (!(retryCount < 40)) { + throw Error(util.format(Messages.unableToCreateGitHubServiceConnection, isReady)); + } + + await this.sleepForMilliSeconds(2000); + retryCount++; + } + } + + private async sleepForMilliSeconds(timeInMs: number) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(); + }, timeInMs); + }); + } +} diff --git a/Source/configure/helper/gitHubHelper.ts b/Source/configure/helper/gitHubHelper.ts new file mode 100644 index 00000000..32112ca7 --- /dev/null +++ b/Source/configure/helper/gitHubHelper.ts @@ -0,0 +1,43 @@ +export class GitHubProvider { + // private gitHubPatToken: string; + private static GitHubUrl = 'https://github.com/'; + private static SSHGitHubUrl = 'git@github.com:'; + + // constructor(gitHubPat: string) { + // this.gitHubPatToken = gitHubPat; + // } + + public static isGitHubUrl(remoteUrl: string): boolean { + return remoteUrl.startsWith(GitHubProvider.GitHubUrl) || remoteUrl.startsWith(GitHubProvider.SSHGitHubUrl); + } + + public static getRepositoryIdFromUrl(remoteUrl: string): string { + // Is SSH based URL + if (remoteUrl.startsWith(GitHubProvider.SSHGitHubUrl)) { + return remoteUrl.substring(GitHubProvider.SSHGitHubUrl.length); + } + + let endCount: number = remoteUrl.indexOf('.git'); + if (endCount < 0) { + endCount = remoteUrl.length; + } + + return remoteUrl.substring(GitHubProvider.GitHubUrl.length, endCount); + } + + public static getFormattedRemoteUrl(remoteUrl: string): string { + // Is SSH based URL + if (remoteUrl.startsWith(GitHubProvider.SSHGitHubUrl)) { + return `https://github.com/${remoteUrl.substring(GitHubProvider.SSHGitHubUrl.length)}`; + } + + return remoteUrl; + } + + public static getFormattedGitHubApiUrlBase(remoteUrl: string): string { + let params: string[] = GitHubProvider.getRepositoryIdFromUrl(remoteUrl).split('/'); + let accountName: string = params[0]; + let repoName: string = params[1]; + return `https://api.github.com/repos/${accountName}/${repoName}`; + } +} diff --git a/Source/configure/helper/graphHelper.ts b/Source/configure/helper/graphHelper.ts new file mode 100644 index 00000000..a8d529b7 --- /dev/null +++ b/Source/configure/helper/graphHelper.ts @@ -0,0 +1,229 @@ +const uuid = require('uuid/v1'); +import { AuthenticationContext, MemoryCache, TokenResponse } from 'adal-node'; +import { ServiceClientCredentials, TokenCredentials, UrlBasedRequestPrepareOptions } from 'ms-rest'; +import { AzureEnvironment } from 'ms-rest-azure'; +import * as util from 'util'; +import { RestClient } from '../clients/restClient'; +import { AadApplication, AzureSession, Token } from '../model/models'; +import { Messages } from '../resources/messages'; +import { executeFunctionWithRetry, generateRandomPassword } from './commonHelper'; + +export class GraphHelper { + + public static async createSpnAndAssignRole(session: AzureSession, aadAppName: string, scope: string): Promise { + let graphCredentials = await this.getGraphToken(session); + let tokenCredentials = new TokenCredentials(graphCredentials.accessToken); + let graphClient = new RestClient(tokenCredentials); + let tenantId = session.tenantId; + var aadApp: AadApplication; + + return this.createAadApp(graphClient, aadAppName, tenantId) + .then((aadApplication) => { + aadApp = aadApplication; + return this.createSpn(graphClient, aadApp.appId, tenantId); + }) + .then((spn) => { + aadApp.objectId = spn.objectId; + return this.createRoleAssignment(session.credentials, scope, aadApp.objectId); + }) + .then(() => { + return aadApp; + }) + .catch((error) => { + let errorMessage = ""; + + if (typeof error == "string") { + errorMessage = error; + } + else { + errorMessage = !!error && error.message; + if (!errorMessage && error["odata.error"]) { + if (typeof error["odata.error"]["message"] === "object") { + errorMessage = error["odata.error"]["message"].value; + } + else { + errorMessage = error["odata.error"]["message"]; + } + } + if (!errorMessage) { + try { + errorMessage = JSON.stringify(error); + } + catch (err) { + + } + } + } + throw new Error(errorMessage); + }); + } + + public static generateAadApplicationName(accountName: string, projectName: string): string { + var spnLengthAllowed = 92; + var guid = uuid(); + var projectName = projectName.replace(/[^a-zA-Z0-9_-]/g, ""); + var accountName = accountName.replace(/[^a-zA-Z0-9_-]/g, ""); + var spnName = accountName + "-" + projectName + "-" + guid; + if (spnName.length <= spnLengthAllowed) { + return spnName; + } + + // 2 is subtracted for delimiter '-' + spnLengthAllowed = spnLengthAllowed - guid.length - 2; + if (accountName.length > spnLengthAllowed / 2 && projectName.length > spnLengthAllowed / 2) { + accountName = accountName.substr(0, spnLengthAllowed / 2); + projectName = projectName.substr(0, spnLengthAllowed - accountName.length); + } + else if (accountName.length > spnLengthAllowed / 2 && accountName.length + projectName.length > spnLengthAllowed) { + accountName = accountName.substr(0, spnLengthAllowed - projectName.length); + } + else if (projectName.length > spnLengthAllowed / 2 && accountName.length + projectName.length > spnLengthAllowed) { + projectName = projectName.substr(0, spnLengthAllowed - accountName.length); + } + + return accountName + "-" + projectName + "-" + guid; + } + + public static async getAccessToken(session: AzureSession): Promise { + const token = await this.getRefreshToken(session); + return token.accessToken; + } + + private static contributorRoleId = "b24988ac-6180-42a0-ab88-20f7382dd24c"; + private static retryTimeIntervalInSec = 2; + private static retryCount = 20; + + private static async getGraphToken(session: AzureSession): Promise { + let refreshTokenResponse = await this.getRefreshToken(session); + return this.getResourceTokenFromRefreshToken(session.environment, refreshTokenResponse.refreshToken, session.tenantId, (session.credentials).clientId, session.environment.activeDirectoryGraphResourceId); + } + + private static async getRefreshToken(session: AzureSession): Promise { + return new Promise((resolve, reject) => { + const credentials: any = session.credentials; + const environment = session.environment; + credentials.context.acquireToken(environment.activeDirectoryResourceId, credentials.username, credentials.clientId, function (err: any, result: any) { + if (err) { + reject(err); + } else { + resolve({ + session, + accessToken: result.accessToken, + refreshToken: result.refreshToken + }); + } + }); + }); + } + + private static async getResourceTokenFromRefreshToken(environment: AzureEnvironment, refreshToken: string, tenantId: string, clientId: string, resource: string): Promise { + return new Promise((resolve, reject) => { + const tokenCache = new MemoryCache(); + const context = new AuthenticationContext(`${environment.activeDirectoryEndpointUrl}${tenantId}`, true, tokenCache); + context.acquireTokenWithRefreshToken(refreshToken, clientId, resource, (err, tokenResponse) => { + if (err) { + reject(new Error(util.format(Messages.acquireTokenFromRefreshTokenFailed, err.message))); + } else if (tokenResponse.error) { + reject(new Error(util.format(Messages.acquireTokenFromRefreshTokenFailed, tokenResponse.error))); + } else { + resolve(tokenResponse); + } + }); + }); + } + + private static async createAadApp(graphClient: RestClient, name: string, tenantId: string): Promise { + let secret = generateRandomPassword(20); + let startDate = new Date(Date.now()); + + return graphClient.sendRequest({ + url: `https://graph.windows.net/${tenantId}/applications`, + queryParameters: { + "api-version": "1.6" + }, + headers: { + "Content-Type": "application/json", + }, + method: "POST", + body: { + "availableToOtherTenants": false, + "displayName": name, + "homepage": "https://" + name, + "passwordCredentials": [ + { + "startDate": startDate, + "endDate": new Date(startDate.getFullYear() + 1, startDate.getMonth()), + "value": secret + } + ] + }, + deserializationMapper: null, + serializationMapper: null + }) + .then((data) => { + return { + appId: data.appId, + secret: secret + }; + }); + } + + private static async createSpn(graphClient: RestClient, appId: string, tenantId: string): Promise { + let createSpnPromise = () => { + return graphClient.sendRequest({ + url: `https://graph.windows.net/${tenantId}/servicePrincipals`, + queryParameters: { + "api-version": "1.6" + }, + headers: { + "Content-Type": "application/json", + }, + method: "POST", + body: { + "appId": appId, + "accountEnabled": "true" + }, + deserializationMapper: null, + serializationMapper: null + }); + }; + + return executeFunctionWithRetry( + createSpnPromise, + GraphHelper.retryCount, + GraphHelper.retryTimeIntervalInSec, + Messages.azureServicePrincipalFailedMessage); + } + + private static async createRoleAssignment(credentials: ServiceClientCredentials, scope: string, objectId: string): Promise { + let restClient = new RestClient(credentials); + let roleDefinitionId = `${scope}/providers/Microsoft.Authorization/roleDefinitions/${this.contributorRoleId}`; + let guid = uuid(); + let roleAssignementFunction = () => { + return restClient.sendRequest({ + url: `https://management.azure.com/${scope}/providers/Microsoft.Authorization/roleAssignments/${guid}`, + queryParameters: { + "api-version": "2015-07-01" + }, + headers: { + "Content-Type": "application/json", + }, + method: "PUT", + body: { + "properties": { + "roleDefinitionId": roleDefinitionId, + "principalId": objectId + } + }, + deserializationMapper: null, + serializationMapper: null + }); + }; + + return executeFunctionWithRetry( + roleAssignementFunction, + GraphHelper.retryCount, + GraphHelper.retryTimeIntervalInSec, + Messages.roleAssignmentFailedMessage); + } +} \ No newline at end of file diff --git a/Source/configure/helper/mustacheHelper.ts b/Source/configure/helper/mustacheHelper.ts new file mode 100644 index 00000000..ff8def2c --- /dev/null +++ b/Source/configure/helper/mustacheHelper.ts @@ -0,0 +1,288 @@ +import * as Mustache from 'mustache'; +import * as Semver from 'semver'; + +export class MustacheHelper { + public static getHelperMethods(): any { + return { + + "equals": function () { + /* + * Usage: {{#equals}}value1 value2 true/false(ignorecase) returnIfTrue returnIfFalse(optional){{/regexReplace}} + * {{#equals}}{{{inputs.Input1}}} value1 true value1 'input 1 has invalid input'{{/regexReplace}} + */ + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length < 4) { + return ""; + } + + var ignoreCase = parts[2]; + if (ignoreCase) { + if (parts[0].toLowerCase() === parts[1].toLowerCase()) { + return parts[3]; + } + } else { + if (parts[0] === parts[1]) { + return parts[3]; + } + } + if (parts.length >= 5) { + return parts[4]; + } else { + return ""; + } + }; + }, + + "if": function () { + /* + * if returns first parameter if given clause is positive, otherwise second parameter + * Usage: {{#if}}clause trueValue falseValue{{/if}} + */ + return function (text: string, render: any) { + let parts = MustacheHelper.getParts(text); + if (parts.length > 1) { + if (render(parts[0]) === "true") { + return render(parts[1]); + } + else { + if (parts[2]) { + return render(parts[2]); + } + + return ""; + } + } + }; + }, + + "toLower": function () { + /* + * converts the string to lower case + * Usage: {{#toLower}} String to convert to lower case {{/toLower}} + */ + return function (text: string, render: any) { + return render(text).toLowerCase(); + }; + }, + + "tinyguid": function () { + /* + * Generates 4 character random string + * Usage: {{#tinyguid}} {{/tinyguid}} + */ + return function () { + return "yxxx".replace(/[xy]/g, function (c) { + var r = Math.random() * 16 | 0; + var v = c === "x" ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); + }; + }, + + "sanitizeString": function () { + /* + * Converts string to alphanumeric + * Usage: {{#sanitizeString}} String to convert to alphanumeric {{/sanitizeString}} + */ + return function (text: string, render: any) { + return render(text).replace(/[^a-zA-Z0-9]/g, ''); + }; + }, + + "substring": function () { + /* + * Trims given string to specified length + * Usage: {{#substring}}'text' from length{{/substring}} + * from, length are integers + */ + return function (text: string, render: any) { + var renderedText = render(text); + var parts = MustacheHelper.getParts(renderedText); + + if (parts.length < 3) { + return render(""); + } + else { + var start = +parts[1]; + var length = +parts[2]; + return render(parts[0].substr(start, length)); + } + }; + }, + + "parseAzureResourceId": function () { + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length !== 2) { + return ""; + } + + var splitResourceId = parts[0].split("/"); + var urlResourcePartIndex = parseInt(parts[1]); + if (splitResourceId && urlResourcePartIndex && splitResourceId.length > urlResourcePartIndex) { + return splitResourceId[urlResourcePartIndex]; + } + + return ""; + }; + }, + + /* + * Checks if a string begins with some string + * Usage: {{#beginsWith}} StringToCheck BeginningPart true/false(for case-sensitivity) IfTrueValue IfFalseValue(optional){{/beginsWith}} + */ + "beginsWith": function () { + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length < 4) { + return ""; + } + + var ignoreCase = parts[2]; + if (ignoreCase) { + if (parts[0].toLowerCase().startsWith(parts[1].toLowerCase())) { + return parts[3]; + } + } else { + if (parts[0].startsWith(parts[1])) { + return parts[3]; + } + } + if (parts.length >= 5) { + return parts[4]; + } else { + return ""; + } + }; + }, + + /* + * If some variable is the environment variable, adding {{ }} + * Usage: {{#EnvironmentVariable}} VariableName {{/EnvironmentVariable}} + */ + "environmentVariable": function () { + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length < 1) { + return ""; + } + return "{{ " + parts[0] + " }}"; + }; + }, + + /* + * Sort an integer array + * Usage: {{#intSorter}} IntegerArrayToBeSorted asc/dsc(order in which it is to be sorted) {{/intSorter}} + */ + "intSorter": function () { + return function (text: string, render: any) { + var parts = MustacheHelper.getParts(text); + if (parts.length < 2) { + return ""; + } + + var order = parts[1].toLowerCase(); + var arr = render(parts[0]).split(","); + var sorter = 1; + if (order === "dsc") { + sorter = -1; + } + arr = arr.sort((a, b) => { + if (a > b) { return 1 * sorter; } + else { return -1 * sorter; } + }); + return arr; + }; + }, + + /* + * Replaces substring of the main string with new value + * Usage: {{#stringReplace}} OldString Old New {{/stringReplace}} -> 'NewString' + */ + "stringReplace": function () { + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length != 3) { + return ""; + } + return parts[0].replace(parts[1], parts[2]); + } + }, + + /* + * Evaluates if the semver condition is satified and return output accordingly + * Usage: {{#semverSatisfy}} 'given.version' 'condition' {{/semverSatisfy}} + */ + + "semverSatisfy": function () { + return function (text: string, render: any) { + var renderedText: string = render(text); + var parts = MustacheHelper.getParts(renderedText); + if (parts.length != 4) { + return ""; + } + var givenVersion = parts[0].trim(); + var semverCondition = parts[1].trim(); + if (givenVersion.length == 0 || semverCondition.length == 0) + return parts[3]; + givenVersion = Semver.minVersion(givenVersion); + if (Semver.satisfies(givenVersion, semverCondition)) + return parts[2]; + return parts[3]; + } + } + }; + } + + public static render(mustacheExpression: string, view: any): string { + view = { ...this.getHelperMethods(), ...view }; + return Mustache.render(mustacheExpression, view); + } + + public static renderObject(mustacheObject: Object, view: any): any { + if (typeof (mustacheObject) === "string") { + return MustacheHelper.render(mustacheObject, view); + } + + var resultArray: any[] = []; + if (Array.isArray(mustacheObject)) { + mustacheObject.forEach(item => { + resultArray.push(MustacheHelper.renderObject(item, view)); + }); + return resultArray; + } + + var result: Object = {}; + if (mustacheObject) { + Object.keys(mustacheObject).forEach(key => { + if (!!key && !!mustacheObject[key]) { + result[key] = MustacheHelper.renderObject(mustacheObject[key], view); + } + }); + + } + return result; + } + + public static getParts(text: string): Array { + var parts: Array = []; + /* Following regex is to fetch different parts in the text e.g. "test 'hello world' output" => test, 'hello world', output*/ + var fetchPartsRegex = new RegExp(/[\'](.*?)[\']|[^ ]+/g); + var resultArray; + while ((resultArray = fetchPartsRegex.exec(text)) !== null) { + var part = (resultArray[1] === undefined || resultArray[1] === null) ? resultArray[0] : resultArray[1]; + if (part === "''") { + part = ""; + } + parts.push(part); + } + + return parts; + } +} diff --git a/Source/configure/helper/remoteServiceUrlHelper.ts b/Source/configure/helper/remoteServiceUrlHelper.ts new file mode 100644 index 00000000..a581595f --- /dev/null +++ b/Source/configure/helper/remoteServiceUrlHelper.ts @@ -0,0 +1,88 @@ +import { RestClient } from "typed-rest-client"; +import { TracePoints } from "../resources/tracePoints"; +import { telemetryHelper } from "./telemetryHelper"; + +export enum ServiceFramework { + Moda, + Vssf +} + +export interface IServiceUrlDefinition { + serviceFramework: ServiceFramework; + serviceUrl: string; +} + +export class RemoteServiceUrlHelper { + public static repoAnalysisRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2127646"; + public static templateServiceRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2133849"; + public static provisioningServiceRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2142042"; + public static repoAnalysisStagingRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2156682"; + public static templateServiceStagingRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2156978"; + public static provisioningServiceStagingRedirectUrl: string = "https://go.microsoft.com/fwlink/?linkid=2156977"; + + public static async getTemplateServiceDefinition(): Promise { + const deployment = process.env["DEPLOY_TO_AZURE_EXT_ENVIRONMENT"]; + if (deployment != undefined && deployment === "development") { + return { + serviceFramework: ServiceFramework.Moda, + serviceUrl: process.env["PROXY_URL"] + } as IServiceUrlDefinition; + } + + if (deployment != undefined && deployment === "staging") { + return this.getServiceurlDefinition(this.templateServiceStagingRedirectUrl); + } + + return this.getServiceurlDefinition(this.templateServiceRedirectUrl); + } + + public static async getRepositoryAnalysisDefinition(): Promise { + const deployment = process.env["DEPLOY_TO_AZURE_EXT_ENVIRONMENT"]; + if (deployment != undefined && deployment === "staging") { + return this.getServiceurlDefinition(this.repoAnalysisStagingRedirectUrl); + } + + return this.getServiceurlDefinition(this.repoAnalysisRedirectUrl); + } + + public static async getProvisioningServiceDefinition(): Promise { + const deployment = process.env["DEPLOY_TO_AZURE_EXT_ENVIRONMENT"]; + if (deployment != undefined && deployment === "development") { + return { + serviceFramework: ServiceFramework.Moda, + serviceUrl: process.env["PROXY_URL"] + "repos/" + } as IServiceUrlDefinition; + } + + if (deployment != undefined && deployment === "staging") { + return this.getServiceurlDefinition(this.provisioningServiceStagingRedirectUrl); + } + + return this.getServiceurlDefinition(this.provisioningServiceRedirectUrl); + } + + private static async getServiceurlDefinition(redirectUrl: string) { + const result = { + serviceFramework: ServiceFramework.Vssf + }; + try { + const requestOptions = { + allowRedirects: false + }; + const restClient = new RestClient("deploy-to-azure", "", [], requestOptions); + const response = await restClient.client.get(redirectUrl, requestOptions); + if ((response.message.statusCode === 301 || response.message.statusCode === 302)) { + result.serviceUrl = response.message.headers["location"]; + } else { + throw Error("Invalid response from url " + redirectUrl); + } + if (!result.serviceUrl.includes("portalext.visualstudio.com")) { + result.serviceFramework = ServiceFramework.Moda; + } + } catch (error) { + telemetryHelper.logError('configure', TracePoints.RemoteServiceUrlFetchFailed, error); + } + return result; + + } +} diff --git a/Source/configure/helper/repoAnalysisHelper.ts b/Source/configure/helper/repoAnalysisHelper.ts new file mode 100644 index 00000000..1a9723a8 --- /dev/null +++ b/Source/configure/helper/repoAnalysisHelper.ts @@ -0,0 +1,122 @@ +import { ApplicationSettings, CodeRepository, RepositoryAnalysis, SourceRepository } from "azureintegration-repoanalysis-client-internal"; +import * as path from 'path'; +import { ModaRepositoryAnalysisClient } from '../clients/modaRepositoryAnalysisClient'; +import { PortalExtensionRepositoryAnalysisClient } from "../clients/portalExtensionRepositoryAnalysisClient"; +import { IRepositoryAnalysisClient } from '../clients/repositoryAnalyisClient'; +import { AzureSession, GitRepositoryParameters, RepositoryProvider, SupportedLanguage } from "../model/models"; +import { RepoAnalysisConstants } from "../resources/constants"; +import { TracePoints } from "../resources/tracePoints"; +import { IServiceUrlDefinition, RemoteServiceUrlHelper, ServiceFramework } from './remoteServiceUrlHelper'; +import { telemetryHelper } from "./telemetryHelper"; + +const Layer: string = 'repoAnalysisHelper'; +export class RepoAnalysisHelper { + private azureSession: AzureSession; + private githubPatToken?: string; + constructor(azureSession: AzureSession, githubPatToken?: string) { + this.azureSession = azureSession; + this.githubPatToken = githubPatToken; + } + + public async getRepositoryAnalysis(sourceRepositoryDetails: GitRepositoryParameters, workspacePath: string): Promise { + + let repositoryAnalysisResponse; + try { + const serviceDefinition = await RemoteServiceUrlHelper.getRepositoryAnalysisDefinition(); + const client = this.getClient(serviceDefinition); + + const repositoryDetails: CodeRepository = {} as CodeRepository; + repositoryDetails.id = sourceRepositoryDetails.repositoryId; + repositoryDetails.defaultBranch = !!sourceRepositoryDetails.branch ? sourceRepositoryDetails.branch : RepoAnalysisConstants.Master; + repositoryDetails.type = RepositoryProvider.Github; + + let repositoryAnalysisRequestBody = {} as SourceRepository; + repositoryAnalysisRequestBody.repository = repositoryDetails; + repositoryAnalysisRequestBody.workingDirectory = workspacePath; + repositoryAnalysisRequestBody.repository.authorizationInfo = { + scheme: "Token", + parameters: { + accesstoken: this.githubPatToken + } + }; + repositoryAnalysisResponse = await client.getRepositoryAnalysis(repositoryAnalysisRequestBody); + if (!!repositoryAnalysisResponse && repositoryAnalysisResponse.length === 0) { + return null; + } + } + catch (e) { + //Return empty if Repo Analysis fails + telemetryHelper.logError(Layer, TracePoints.RepoAnalysisFailed, e); + return null; + } + + let parameters: RepositoryAnalysis = {} as RepositoryAnalysis; + parameters.applicationSettingsList = []; + repositoryAnalysisResponse.applicationSettingsList.forEach((analysis) => { + + //Process only for VSCode Supported Languages + if (Object.keys(SupportedLanguage).indexOf(analysis.language.toUpperCase()) > -1) { + let applicationSettings: ApplicationSettings = {} as ApplicationSettings; + applicationSettings.language = analysis.language; + + if (!!analysis.settings) { + if (!!analysis.settings.workingDirectory) { + if (!applicationSettings.settings) { + applicationSettings.settings = {}; + } + applicationSettings.settings.workingDirectory = analysis.settings.workingDirectory.split('\\').join('/'); + } + if (!!analysis.buildTargetName) { + applicationSettings.buildTargetName = analysis.buildTargetName; + if (analysis.language === SupportedLanguage.NODE) { + applicationSettings.settings[RepoAnalysisConstants.PackageFilePath] = analysis.settings[RepoAnalysisConstants.PackageFilePath]; + applicationSettings.settings.packageFileDirectory = path.dirname(analysis.settings[RepoAnalysisConstants.PackageFilePath]); + if (analysis.buildTargetName === RepoAnalysisConstants.Gulp && !!analysis.settings[RepoAnalysisConstants.GulpFilePath]) { + applicationSettings.settings[RepoAnalysisConstants.GulpFilePath] = this.GetRelativePath( + applicationSettings.settings.workingDirectory, analysis.settings[RepoAnalysisConstants.GulpFilePath]); + } + else if (analysis.buildTargetName === RepoAnalysisConstants.Grunt && !!analysis.settings[RepoAnalysisConstants.GruntFilePath]) { + applicationSettings.settings[RepoAnalysisConstants.GruntFilePath] = this.GetRelativePath( + applicationSettings.settings.workingDirectory, analysis.settings[RepoAnalysisConstants.GruntFilePath]); + } + } + else if (analysis.language === SupportedLanguage.PYTHON) { + if (!!analysis.settings[RepoAnalysisConstants.RequirementsFilePath]) { + applicationSettings.settings.pythonRequirementsFilePath = this.GetRelativePath( + applicationSettings.settings.workingDirectory, analysis.settings[RepoAnalysisConstants.RequirementsFilePath]); + applicationSettings.settings.pythonRequirementsFileDirectory = path.dirname(analysis.settings[RepoAnalysisConstants.RequirementsFilePath]); + } + } else { + applicationSettings.settings = analysis.settings; + applicationSettings.settings.workingDirectory = applicationSettings.settings.workingDirectory.split('\\').join('/'); + } + } + if (!!analysis.deployTargetName) { + applicationSettings.deployTargetName = analysis.deployTargetName; + if (analysis.deployTargetName === RepoAnalysisConstants.AzureFunctions) { + applicationSettings.settings.azureFunctionsHostFilePath = analysis.settings[RepoAnalysisConstants.HostFilePath]; + applicationSettings.settings.azureFunctionsHostFileDirectory = path.dirname(analysis.settings[RepoAnalysisConstants.HostFilePath]); + } + } + + } + parameters.applicationSettingsList.push(applicationSettings); + } + }); + return parameters; + } + + private GetRelativePath(workingDirectory: string, filePath: string): string { + return path.relative(workingDirectory, filePath).split(path.sep).join('/'); + } + + private getClient(serviceDefinition: IServiceUrlDefinition): IRepositoryAnalysisClient { + let client = null; + if (serviceDefinition.serviceFramework === ServiceFramework.Vssf) { + client = new PortalExtensionRepositoryAnalysisClient(serviceDefinition.serviceUrl, this.azureSession.credentials); + } else { + client = new ModaRepositoryAnalysisClient(serviceDefinition.serviceUrl, this.githubPatToken); + } + return client; + } +} diff --git a/Source/configure/helper/sodium/SodiumLibHelper.ts b/Source/configure/helper/sodium/SodiumLibHelper.ts new file mode 100644 index 00000000..2c1d2beb --- /dev/null +++ b/Source/configure/helper/sodium/SodiumLibHelper.ts @@ -0,0 +1,44 @@ +import * as sodium from 'tweetsodium'; + +export class SodiumLibHelper { + key: Uint8Array; + + constructor(key: Uint8Array | string) { + if (typeof (key) == "string") { + let decodedKey = SodiumLibHelper.decodeFromBase64(key); + this.key = SodiumLibHelper.convertStringToUint8Array(decodedKey); + } else { + this.key = key; + } + } + + public encrypt (message: Uint8Array | string) { + if (typeof (message) == "string") { + return sodium.seal(SodiumLibHelper.convertStringToUint8Array(message), this.key) + } else { + return sodium.seal(message, this.key) + } + } + + public static decodeFromBase64(encoded: string): string { + let decodedbase64 = new Buffer(encoded, 'base64'); + return decodedbase64.toString('binary') + } + + public static encodeToBase64 (decoded: string): string { + return (new Buffer(decoded, 'binary')).toString('base64') + } + + public static convertStringToUint8Array (v: string): Uint8Array { + let body = v.split('') + let _body = body.map((a) => { + return a.charCodeAt(0); + }) + return Uint8Array.from(_body) + } + + public static convertUint8ArrayToString (bytes: Uint8Array): string { + return String.fromCharCode.apply(null, Array.from(bytes)) + } +} + diff --git a/Source/configure/helper/sodium/sodium.d.ts b/Source/configure/helper/sodium/sodium.d.ts new file mode 100644 index 00000000..c691b6e1 --- /dev/null +++ b/Source/configure/helper/sodium/sodium.d.ts @@ -0,0 +1,3 @@ +declare module "tweetsodium" { + export function seal(messageBytes: Uint8Array, keyBytes: Uint8Array) : any +} \ No newline at end of file diff --git a/Source/configure/helper/telemetryHelper.ts b/Source/configure/helper/telemetryHelper.ts new file mode 100644 index 00000000..d35772a4 --- /dev/null +++ b/Source/configure/helper/telemetryHelper.ts @@ -0,0 +1,88 @@ +import { IActionContext, ITelemetryReporter, parseError } from "vscode-azureextensionui"; + +import * as logger from '../../logger'; +import { extensionVariables } from "../model/models"; +import { TelemetryKeys } from '../resources/telemetryKeys'; + +const uuid = require('uuid/v4'); + +class TelemetryHelper { + private actionContext: IActionContext; + private telemetryReporter: ITelemetryReporter; + private journeyId: string; + private command: string; + + public initialize(actionContext: IActionContext, command: string): void { + this.actionContext = actionContext; + this.telemetryReporter = extensionVariables.reporter; + this.journeyId = uuid(); + this.command = command; + this.setTelemetry(TelemetryKeys.JourneyId, this.journeyId); + } + + public getJourneyId(): string { + return this.journeyId; + } + + public setTelemetry(key: string, value: string): void { + if (key) { + this.actionContext.telemetry.properties[key] = value; + } + } + + public setResult(result: Result, error?: Error): void { + this.actionContext.telemetry.properties.result = result; + if (error) { + let parsedError = parseError(error); + this.actionContext.telemetry.properties.error = JSON.stringify(parsedError); + this.actionContext.telemetry.properties.errorMessage = parsedError.message; + } + } + + public setCurrentStep(stepName: string): void { + //current step is the last step till which user reaches + this.actionContext.telemetry.properties["currentStep"] = stepName; + } + + public logError(layer: string, tracePoint: string, error: Error): void { + let parsedError = parseError(error); + this.telemetryReporter.sendTelemetryEvent( + tracePoint, + { + 'journeyId': this.journeyId, + 'command': this.command, + 'layer': layer, + 'error': JSON.stringify(parsedError) + }); + + logger.log(JSON.stringify(parsedError)); + } + + public logInfo(layer: string, tracePoint: string, info: string): void { + this.telemetryReporter.sendTelemetryEvent( + tracePoint, + { + 'journeyId': this.journeyId, + 'command': this.command, + 'layer': layer, + 'info': info + }); + } + + public async executeFunctionWithTimeTelemetry(callback: () => T, telemetryKey: string): Promise { + let startTime = Date.now(); + try { + return await callback(); + } + finally { + this.setTelemetry(telemetryKey, ((Date.now() - startTime) / 1000).toString()); + } + } +} +export let telemetryHelper = new TelemetryHelper(); + +export enum Result { + 'Succeeded' = 'Succeeded', + 'Failed' = 'Failed', + 'Canceled' = 'Canceled' +} diff --git a/Source/configure/helper/templateHelper.ts b/Source/configure/helper/templateHelper.ts new file mode 100644 index 00000000..3b31d50c --- /dev/null +++ b/Source/configure/helper/templateHelper.ts @@ -0,0 +1,1509 @@ +import { GenericResource } from 'azure-arm-resource/lib/resource/models'; +import { RepositoryAnalysis } from 'azureintegration-repoanalysis-client-internal'; +import * as fs from 'fs'; +import * as Mustache from 'mustache'; +import * as path from 'path'; +import * as Q from 'q'; +import { TemplateServiceClientFactory } from '../clients/TemplateServiceClientFactory'; +import { ExtendedPipelineTemplate } from '../model/Contracts'; +import { AzureConnectionType, AzureSession, extensionVariables, GitRepositoryParameters, MustacheContext, PipelineType, RepositoryProvider, SupportedLanguage, TargetKind, TargetResourceType } from '../model/models'; +import { LocalPipelineTemplate, PipelineTemplate, PreDefinedDataSourceIds, RemotePipelineTemplate, TemplateAssetType, TemplateInfo, TemplateParameterType, TemplateType } from '../model/templateModels'; +import { PipelineTemplateLabels, RepoAnalysisConstants } from '../resources/constants'; +import { Messages } from '../resources/messages'; +import { TelemetryKeys } from '../resources/telemetryKeys'; +import { TracePoints } from '../resources/tracePoints'; +import { MustacheHelper } from './mustacheHelper'; +import { telemetryHelper } from './telemetryHelper'; + +const Layer: string = 'templateHelper'; +export async function mergingRepoAnalysisResults(sourceRepository: GitRepositoryParameters, repoAnalysisParameters: RepositoryAnalysis): Promise { + let localRepoAnalysisResult = await analyzeRepo(sourceRepository.localPath); + let analysisResult = localRepoAnalysisResult; + + //If Repo analysis fails then we'll go with the basic existing analysis + if (sourceRepository.repositoryProvider === RepositoryProvider.Github && !!repoAnalysisParameters && !!repoAnalysisParameters.applicationSettingsList) { + analysisResult = new AnalysisResult(); + repoAnalysisParameters.applicationSettingsList.forEach((settings) => { + analysisResult.languages.push(settings.language as SupportedLanguage); + + //Check if Azure:Functions is value of any deployTargetName property + analysisResult.isFunctionApp = + analysisResult.isFunctionApp || settings.deployTargetName === RepoAnalysisConstants.AzureFunctions ? true : false; + }); + + //Languages not supported by RepoAnalysisService should be considered and taken from LocalRepoAnalysis + localRepoAnalysisResult.languages.forEach((language) => { + if (analysisResult.languages.indexOf(language) === -1) { + analysisResult.languages.push(language); + } + }); + + if (analysisResult.languages.length === 0) { + analysisResult.languages.push(SupportedLanguage.NONE); + } + } + return analysisResult; +} + +export function getTargetType(template: TemplateInfo): TargetResourceType { + if (template.attributes.deployTarget.toLowerCase().includes("webapp")) { + return TargetResourceType.WebApp; + } else if (template.attributes.deployTarget === "Azure:AKS") { + return TargetResourceType.AKS; + } + return TargetResourceType.None; +} + +export function getTargetKind(template: TemplateInfo): TargetKind { + var targetKind: TargetKind; + if (template.attributes.deployTarget.toLowerCase().includes("webapp")) { + if (template.attributes.deployTarget.toLowerCase().includes("container")) { + targetKind = TargetKind.LinuxContainerApp; + } else if (template.attributes.deployTarget.toLowerCase().includes("linux")) { + targetKind = TargetKind.LinuxApp; + } else if (template.attributes.deployTarget.toLowerCase().includes("windows")) { + targetKind = TargetKind.WindowsApp; + } + } + else { + targetKind = null; + } + return targetKind; +} + +export function uniqueValues(value, index, self) { + return self.indexOf(value) === index; +} + +export async function analyzeRepoAndListAppropriatePipeline(sourceRepository: GitRepositoryParameters, repoAnalysisParameters: RepositoryAnalysis, targetResource?: GenericResource): Promise { + + let analysisResult = await mergingRepoAnalysisResults(sourceRepository, repoAnalysisParameters); + + let templateList: { [key: string]: LocalPipelineTemplate[] } = {}; + switch (sourceRepository.repositoryProvider) { + case RepositoryProvider.AzureRepos: + templateList = azurePipelineTemplates; + break; + case RepositoryProvider.Github: + templateList = extensionVariables.enableGitHubWorkflow ? githubWorklowTemplates : azurePipelineTemplates; + break; + default: + throw new Error(Messages.cannotIdentifyRespositoryDetails); + } + + + let templateResult: LocalPipelineTemplate[] = []; + let uniqueLanguages = (analysisResult.languages).filter(this.uniqueValues); + + uniqueLanguages.forEach((language) => { + switch (language) { + case SupportedLanguage.DOCKER: + if (templateList[SupportedLanguage.DOCKER] && templateList[SupportedLanguage.DOCKER].length > 0) { + templateResult = templateResult.concat(templateList[SupportedLanguage.DOCKER]); + } + break; + case SupportedLanguage.NODE: + if (templateList[SupportedLanguage.NODE] && templateList[SupportedLanguage.NODE].length > 0) { + templateResult = templateResult.concat(templateList[SupportedLanguage.NODE]); + } + break; + case SupportedLanguage.PYTHON: + if (templateList[SupportedLanguage.PYTHON] && templateList[SupportedLanguage.PYTHON].length > 0) { + templateResult = templateResult.concat(templateList[SupportedLanguage.PYTHON]); + } + break; + case SupportedLanguage.DOTNETCORE: + if (templateList[SupportedLanguage.DOTNETCORE] && templateList[SupportedLanguage.DOTNETCORE].length > 0) { + templateResult = templateResult.concat(templateList[SupportedLanguage.DOTNETCORE]); + } + break; + case SupportedLanguage.NONE: + if (templateList[SupportedLanguage.NONE] && templateList[SupportedLanguage.NONE].length > 0) { + templateResult = templateResult.concat(templateList[SupportedLanguage.NONE]); + } + break; + default: + break; + } + }); + + if (templateResult.length < 1 && templateList[SupportedLanguage.NONE] && templateList[SupportedLanguage.NONE].length > 0) { + templateResult = templateList[SupportedLanguage.NONE]; + } + + if (analysisResult.isFunctionApp) { + switch (sourceRepository.repositoryProvider) { + case RepositoryProvider.AzureRepos: + templateResult = azurePipelineTargetBasedTemplates[AzureTarget.FunctionApp].concat(templateResult); + break; + case RepositoryProvider.Github: + templateResult = extensionVariables.enableGitHubWorkflow ? githubWorkflowTargetBasedTemplates[AzureTarget.FunctionApp].concat(templateResult) : azurePipelineTargetBasedTemplates[AzureTarget.FunctionApp].concat(templateResult); + break; + default: + break; + } + } + + templateResult = targetResource && !!targetResource.type ? templateResult.filter((template) => !template.targetType || template.targetType.toLowerCase() === targetResource.type.toLowerCase()) : templateResult; + templateResult = targetResource && !!targetResource.kind ? templateResult.filter((template) => !template.targetKind || template.targetKind.toLowerCase() === targetResource.kind.toLowerCase()) : templateResult; + templateResult = templateResult.filter((pipelineTemplate) => pipelineTemplate.enabled); + + return templateResult; +} + +async function convertToPipelineTemplate(remoteTemplates: TemplateInfo[]): Promise { + const pipelineTemplates: PipelineTemplate[] = []; + if (!!remoteTemplates) { + remoteTemplates.forEach((templateInfo: TemplateInfo) => { + const remoteTemplate: RemotePipelineTemplate = { + label: templateInfo.templateLabel, + targetType: getTargetType(templateInfo), + targetKind: getTargetKind(templateInfo), + templateType: TemplateType.REMOTE, + language: templateInfo.attributes.language, + id: templateInfo.templateId, + templateWeight: templateInfo.templateWeight, + workingDirectory: templateInfo.workingDirectory, + description: templateInfo.templateDescription, + }; + pipelineTemplates.push(remoteTemplate); + }); + } + return pipelineTemplates; +} + +export async function getFilteredTemplates(resourceType: string): Promise { + + const client = await TemplateServiceClientFactory.getClient(); + let filteredTemplates: TemplateInfo[]; + switch (resourceType) { + case TargetResourceType.AKS: + await telemetryHelper.executeFunctionWithTimeTelemetry(async () => { + filteredTemplates = await client.getTemplatesInfoByFilter("docker", "Azure:AKS"); + }, TelemetryKeys.TemplateServiceDuration); + return filteredTemplates; + default: + return null; + } +} + +export async function getTemplates(repoAnalysisParameters: RepositoryAnalysis) { + const client = await TemplateServiceClientFactory.getClient(); + let templates: TemplateInfo[]; + await telemetryHelper.executeFunctionWithTimeTelemetry(async () => { + templates = await client.getTemplates(repoAnalysisParameters); + }, TelemetryKeys.TemplateServiceDuration); + return templates; +} + +export async function analyzeRepoAndListAppropriatePipeline2(azureSession: AzureSession, sourceRepository: GitRepositoryParameters, pipelineType: PipelineType, repoAnalysisParameters: RepositoryAnalysis, githubPatToken?: string, resource?: GenericResource): Promise { + + let pipelineTemplates: PipelineTemplate[] = []; + let remoteTemplates: TemplateInfo[] = []; + let localPipelineTemplates: LocalPipelineTemplate[] = await this.analyzeRepoAndListAppropriatePipeline(sourceRepository, repoAnalysisParameters); + + if (pipelineType === PipelineType.GitHubPipeline) { + try { + if (!!resource) { + localPipelineTemplates = []; + remoteTemplates = await getFilteredTemplates(resource.type); + } + else if (repoAnalysisParameters && repoAnalysisParameters.applicationSettingsList) { + remoteTemplates = await getTemplates(repoAnalysisParameters); + } + pipelineTemplates = await convertToPipelineTemplate(remoteTemplates); + pipelineTemplates = pipelineTemplates.concat(localPipelineTemplates); + // sorted by weight + pipelineTemplates = pipelineTemplates.sort((a, b) => { + return b.templateWeight - a.templateWeight; + }); + return pipelineTemplates; + } + catch (err) { + pipelineTemplates = []; + telemetryHelper.logError(Layer, TracePoints.TemplateServiceCallFailed, err); + return null; + } + } + else { + return localPipelineTemplates; + } +} + +export async function getTemplateParameters(templateId: string): Promise { + let parameters: ExtendedPipelineTemplate; + try { + let serviceClient = await TemplateServiceClientFactory.getClient(); + parameters = await serviceClient.getTemplateParameters(templateId); + return parameters; + } + catch (e) { + telemetryHelper.logError(Layer, TracePoints.UnableToGetTemplateParameters, e); + throw new Error(Messages.UnableToGetTemplateParameters); + } + +} + +export function getPipelineTemplatesForAllWebAppKind(repositoryProvider: RepositoryProvider, label: string, language: string, targetKind: TargetKind): LocalPipelineTemplate[] { + let pipelineTemplates: LocalPipelineTemplate[] = []; + + if (repositoryProvider === RepositoryProvider.Github && extensionVariables.enableGitHubWorkflow) { + pipelineTemplates = githubWorklowTemplates[language]; + if (isFunctionAppType(targetKind)) { + pipelineTemplates = pipelineTemplates.concat(githubWorkflowTargetBasedTemplates[AzureTarget.FunctionApp]); + } + } + else { + pipelineTemplates = azurePipelineTemplates[language]; + if (isFunctionAppType(targetKind)) { + pipelineTemplates = pipelineTemplates.concat(azurePipelineTargetBasedTemplates[AzureTarget.FunctionApp]); + } + } + + return pipelineTemplates.filter((template) => { + return template.label.toLowerCase() === label.toLowerCase() && template.targetType === TargetResourceType.WebApp && template.language === language; + }); +} + +export async function renderContent(templateFilePath: string, context: MustacheContext): Promise { + let deferred: Q.Deferred = Q.defer(); + fs.readFile(templateFilePath, { encoding: "utf8" }, async (error, data) => { + if (error) { + throw new Error(error.message); + } + else { + let updatedContext: MustacheContext; + updatedContext = { ...MustacheHelper.getHelperMethods(), ...context }; + let fileContent = Mustache.render(data, updatedContext); + deferred.resolve(fileContent); + } + }); + + return deferred.promise; +} + +export function getDockerPort(repoPath: string, relativeDockerFilePath?: string): string { + let dockerfilePath = relativeDockerFilePath; + if (!dockerfilePath) { + let files = fs.readdirSync(repoPath); + files.some((fileName) => { if (fileName.toLowerCase().endsWith('dockerfile')) { dockerfilePath = fileName; return true; } return false; }); + if (!dockerfilePath) { + return null; + } + } + + try { + let dockerContent = fs.readFileSync(path.join(repoPath, dockerfilePath), 'utf8'); + let index = dockerContent.toLowerCase().indexOf('expose '); + if (index !== -1) { + let temp = dockerContent.substring(index + 'expose '.length); + let ports = temp.substr(0, temp.indexOf('\n')).split(' ').filter(Boolean); + if (ports.length) { + return ports[0]; + } + } + return null; + } + catch (err) { + telemetryHelper.logError(Layer, TracePoints.ReadingDockerFileFailed, err); + } + + return null; +} + +async function analyzeRepo(repoPath: string): Promise { + let deferred: Q.Deferred = Q.defer(); + fs.readdir(repoPath, (err, files: string[]) => { + let result: AnalysisResult = new AnalysisResult(); + result.languages = []; + result.languages = isDockerApp(files) ? result.languages.concat(SupportedLanguage.DOCKER) : result.languages; + result.languages = isNodeRepo(files) ? result.languages.concat(SupportedLanguage.NODE) : result.languages; + result.languages = isPythonRepo(files) ? result.languages.concat(SupportedLanguage.PYTHON) : result.languages; + result.languages = isDotnetCoreRepo(files) ? result.languages.concat(SupportedLanguage.DOTNETCORE) : result.languages; + + result.isFunctionApp = err ? true : isFunctionApp(files), + + deferred.resolve(result); + }); + + return deferred.promise; +} + +function isDotnetCoreRepo(files: string[]): boolean { + return files.some((file) => { + return file.toLowerCase().endsWith("sln") || file.toLowerCase().endsWith("csproj") || file.toLowerCase().endsWith("fsproj"); + }); +} + +function isNodeRepo(files: string[]): boolean { + let nodeFilesRegex = '\\.ts$|\\.js$|package\\.json$|node_modules'; + return files.some((file) => { + let result = new RegExp(nodeFilesRegex).test(file.toLowerCase()); + return result; + }); +} + +function isPythonRepo(files: string[]): boolean { + let pythonRegex = '.py$'; + return files.some((file) => { + let result = new RegExp(pythonRegex).test(file.toLowerCase()); + return result; + }); +} + +function isDockerApp(files: string[]): boolean { + return files.some((file) => { + return file.toLowerCase().endsWith("dockerfile"); + }); +} + +function isFunctionApp(files: string[]): boolean { + return files.some((file) => { + return file.toLowerCase().endsWith("host.json"); + }); +} + +export function isFunctionAppType(targetKind: TargetKind): boolean { + return targetKind === TargetKind.FunctionApp || targetKind === TargetKind.FunctionAppLinux || targetKind === TargetKind.FunctionAppLinuxContainer; +} + +export class AnalysisResult { + public languages: SupportedLanguage[] = []; + public isFunctionApp: boolean = false; + // public isContainerized: boolean; +} + +export enum AzureTarget { + FunctionApp = 'Microsoft.Web/sites-functionapp' +} + +let azurePipelineTemplates: { [key in SupportedLanguage]: LocalPipelineTemplate[] } = +{ + 'none': [ + { + label: PipelineTemplateLabels.SimpleApplicationToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/simpleWebApp.yml'), + language: SupportedLanguage.NONE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL, + }, + { + label: PipelineTemplateLabels.SimpleApplicationToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/simpleLinuxWebApp.yml'), + language: SupportedLanguage.NONE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'node': [ + { + label: PipelineTemplateLabels.NodeJSWithNpmToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejs.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGulpToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithGulp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGruntToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithGrunt.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithAngularToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithAngular.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithWebpackToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithWebpack.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithNpmToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGulpToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithGulpLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGruntToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithGruntLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithAngularToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithAngularLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithWebpackToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWithWebpackLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'python': [ + { + label: 'Python to Linux Web App on Azure', + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/pythonLinuxWebApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: 'Build and Test Python Django App', + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/pythonDjango.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.None, + targetKind: null, + enabled: true, + parameters: [], + azureConnectionType: AzureConnectionType.None, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'dotnetcore': [ + { + label: PipelineTemplateLabels.DotNetCoreWebAppToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/dotnetcoreWindowsWebApp.yml'), + language: SupportedLanguage.DOTNETCORE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.DotNetCoreWebAppToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/dotnetcoreLinuxWebApp.yml'), + language: SupportedLanguage.DOTNETCORE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'docker': [ + { + label: 'Containerized application to AKS', + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/AksWithReuseACR.yml'), + language: SupportedLanguage.DOCKER, + targetType: TargetResourceType.AKS, + targetKind: null, + enabled: false, + parameters: [ + { + "name": "aksCluster", + "displayName": "Select Azure Kubernetes cluster to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.AKS + }, + { + "name": "containerRegistry", + "displayName": "Select Azure Container Registry to store docker image", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.ACR + }, + { + "name": "containerPort", + "displayName": null, + "type": TemplateParameterType.String, + "dataSourceId": PreDefinedDataSourceIds.RepoAnalysis, + "defaultValue": '80' + } + ], + assets: [ + { + "id": "kubernetesServiceConnection ", + "type": TemplateAssetType.AKSKubeConfigServiceConnection + }, + { + "id": "containerRegistryServiceConnection", + "type": TemplateAssetType.ACRServiceConnection + } + ], + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'dotnet': [] +}; + +let githubWorklowTemplates: { [key in SupportedLanguage]: LocalPipelineTemplate[] } = { + 'docker': [ + { + label: 'Containerized application to AKS', + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/AksWithReuseACR.yml'), + language: SupportedLanguage.DOCKER, + targetType: TargetResourceType.AKS, + targetKind: null, + enabled: true, + parameters: [ + { + "name": "aksCluster", + "displayName": "Select Azure Kubernetes cluster to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.AKS + }, + { + "name": "containerRegistry", + "displayName": "Select Azure Container Registry to store docker image", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.ACR + }, + { + "name": "containerPort", + "displayName": null, + "type": TemplateParameterType.String, + "dataSourceId": PreDefinedDataSourceIds.RepoAnalysis, + "defaultValue": "80" + }, + { + "name": "namespace", + "displayName": null, + "type": TemplateParameterType.String, + "dataSourceId": "", + "defaultValue": "{{#toLower}}{{#sanitizeString}}{{{inputs.aksCluster.name}}}{{/sanitizeString}}{{/toLower}}{{#tinyguid}}{{/tinyguid}}" + } + ], + assets: [ + { + "id": "kubeConfig", + "type": TemplateAssetType.GitHubAKSKubeConfig + }, + { + "id": "containerRegistryUsername", + "type": TemplateAssetType.GitHubRegistryUsername + }, + { + "id": "containerRegistryPassword", + "type": TemplateAssetType.GitHubRegistryPassword + }, + { + "id": "deployment", + "type": TemplateAssetType.File + }, + { + "id": "service", + "type": TemplateAssetType.File + }, + { + "id": "ingress", + "type": TemplateAssetType.File + }, + { + "id": "service-ingress", + "type": TemplateAssetType.File + } + ], + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'node': [ + { + label: PipelineTemplateLabels.NodeJSWithNpmToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsOnWindows.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithNpmToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsOnLinux.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGulpToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithGulpOnWindowsWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGulpToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithGulpOnLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGruntToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithGruntOnWindowsWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithGruntToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithGruntOnLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithAngularToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithAngularOnWindowsWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithAngularToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithAngularOnLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithWebpackToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnWindowsWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSWithWebpackToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnLinuxWebApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'none': [ + { + label: PipelineTemplateLabels.SimpleApplicationToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/simpleWebApp.yml'), + language: SupportedLanguage.NONE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.WindowsApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.WindowsApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMPublishProfileServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMPublishProfile, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.SimpleApplicationToAppService, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/simpleWebApp.yml'), + language: SupportedLanguage.NONE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ], + 'python': [ + { + label: 'Python to Linux Web App on Azure', + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/pythonLinuxWebApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.LinuxApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure webapp to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + ], + 'dotnetcore': [], + 'dotnet': [] +}; + +const azurePipelineTargetBasedTemplates: { [key in AzureTarget]: LocalPipelineTemplate[] } = +{ + 'Microsoft.Web/sites-functionapp': [ + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsWindowsFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.FunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsLinuxFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinux, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/nodejsLinuxFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinuxContainer, + enabled: true, + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.DotNetCoreFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/dotnetcoreWindowsFunctionApp.yml'), + language: SupportedLanguage.DOTNETCORE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionApp, + enabled: false, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.FunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.DotNetCoreFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/dotnetcoreLinuxFunctionApp.yml'), + language: SupportedLanguage.DOTNETCORE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinux, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.DotNetCoreFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/dotnetcoreLinuxFunctionApp.yml'), + language: SupportedLanguage.DOTNETCORE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinuxContainer, + enabled: true, + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.PythonFunctionAppToLinuxAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/pythonLinuxFunctionApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinux, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.PythonFunctionAppToLinuxAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/azurePipelineTemplates/pythonLinuxFunctionApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinuxContainer, + enabled: true, + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + ] +}; + +const githubWorkflowTargetBasedTemplates: { [key in AzureTarget]: LocalPipelineTemplate[] } = +{ + 'Microsoft.Web/sites-functionapp': [ + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsWindowsFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionApp, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.FunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + //To-DO : weight is greater than the remote templates, to be changed later + templateWeight: 999999, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsLinuxFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinux, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.NodeJSFunctionAppToAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/nodejsLinuxFunctionApp.yml'), + language: SupportedLanguage.NODE, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinuxContainer, + enabled: true, + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.PythonFunctionAppToLinuxAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/pythonLinuxFunctionApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinux, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + }, + { + label: PipelineTemplateLabels.PythonFunctionAppToLinuxAzureFunction, + path: path.join(path.dirname(path.dirname(__dirname)), 'configure/templates/githubWorkflowTemplates/pythonLinuxFunctionApp.yml'), + language: SupportedLanguage.PYTHON, + targetType: TargetResourceType.WebApp, + targetKind: TargetKind.FunctionAppLinuxContainer, + enabled: true, + parameters: [ + { + "name": "webapp", + "displayName": "Select the target Azure Function to deploy your application", + "type": TemplateParameterType.GenericAzureResource, + "dataSourceId": PreDefinedDataSourceIds.LinuxContainerFunctionApp + } + ], + assets: [ + { + "id": "endpoint", + "type": TemplateAssetType.AzureARMServiceConnection + } + ], + azureConnectionType: AzureConnectionType.AzureRMServicePrincipal, + templateWeight: 100, + templateType: TemplateType.LOCAL + } + ] +}; diff --git a/Source/configure/helper/templateParameterHelper.ts b/Source/configure/helper/templateParameterHelper.ts new file mode 100644 index 00000000..35c974fb --- /dev/null +++ b/Source/configure/helper/templateParameterHelper.ts @@ -0,0 +1,278 @@ +import { GenericResource } from "azure-arm-resource/lib/resource/models"; +import * as utils from 'util'; +import { AppServiceClient } from "../clients/azure/appServiceClient"; +import { ArmRestClient } from "../clients/azure/armRestClient"; +import { ApiVersions, AzureResourceClient } from "../clients/azure/azureResourceClient"; +import { openBrowseExperience } from '../configure'; +import * as templateHelper from '../helper/templateHelper'; +import { extensionVariables, MustacheContext, PipelineConfiguration, QuickPickItemWithData, TargetKind, TargetResourceType, WizardInputs } from "../model/models"; +import { LocalPipelineTemplate, PreDefinedDataSourceIds, TemplateParameter, TemplateParameterType } from '../model/templateModels'; +import * as constants from '../resources/constants'; +import { Messages } from "../resources/messages"; +import { TelemetryKeys } from "../resources/telemetryKeys"; +import { Utilities } from "../utilities/utilities"; +import { getSubscriptionSession } from "./azureSessionHelper"; +import { ControlProvider } from "./controlProvider"; +import { MustacheHelper } from "./mustacheHelper"; +import { telemetryHelper } from "./telemetryHelper"; + +export class TemplateParameterHelper { + public static getParameterForTargetResourceType(parameters: TemplateParameter[], targetResourceType: TargetResourceType, targetResourceKind?: TargetKind): TemplateParameter { + let dataSourceIdForResourceType = TemplateParameterHelper.convertAzureResourceToDataSourceId(targetResourceType, targetResourceKind); + return parameters.find((parameter) => { return (parameter.type === TemplateParameterType.GenericAzureResource && parameter.dataSourceId.startsWith(dataSourceIdForResourceType)); }); + } + + public static getParameterValueForTargetResourceType(pipelineConfiguration: PipelineConfiguration, targetResourceType: TargetResourceType, targetResourceKind?: TargetKind): GenericResource { + let dataSourceIdForResourceType = TemplateParameterHelper.convertAzureResourceToDataSourceId(targetResourceType, targetResourceKind); + let resourceTemplateParameter = (pipelineConfiguration.template as LocalPipelineTemplate).parameters.find((parameter) => { return (parameter.type === TemplateParameterType.GenericAzureResource && parameter.dataSourceId.startsWith(dataSourceIdForResourceType)); }); + if (!resourceTemplateParameter) { + throw utils.format(Messages.azureResourceTemplateParameterCouldNotBeFound, targetResourceType); + } + + let parameterValue: GenericResource = pipelineConfiguration.params[resourceTemplateParameter.name]; + if (!parameterValue) { + throw utils.format(Messages.parameterWithNameNotSet, resourceTemplateParameter.name); + } + + return parameterValue; + } + + public static getMatchingAzureResourceTemplateParameter(resource: GenericResource, templateParameters: TemplateParameter[]): TemplateParameter { + if (!resource || !templateParameters) { + return null; + } + + let resourceTargetType = TemplateParameterHelper.convertAzureResourceToDataSourceId(resource.type, resource.kind); + let matchedParam = templateParameters.find((templateParameter) => { return templateParameter.dataSourceId.toLowerCase() === resourceTargetType.toLowerCase(); }); + + if (matchedParam) { + return matchedParam; + } + + return null; + } + + public async setParameters(parameters: TemplateParameter[], inputs: WizardInputs): Promise { + if (!!parameters && parameters.length > 0) { + for (let parameter of parameters) { + if (!inputs.pipelineConfiguration.params[parameter.name]) { + try { + await this.getParameterValue(parameter, inputs); + } + catch (err) { + if (!inputs.pipelineConfiguration.params[parameter.name] && !!parameter.defaultValue) { + inputs.pipelineConfiguration.params[parameter.name] = parameter.defaultValue; + } + else { + throw err; + } + } + } + } + } + } + + private static convertAzureResourceToDataSourceId(targetType: TargetResourceType, targetKind: TargetKind): string { + return targetKind ? targetType + ":" + targetKind : targetType; + } + + private async getParameterValue(parameter: TemplateParameter, inputs: WizardInputs): Promise { + if (!!parameter) { + switch (parameter.type) { + case TemplateParameterType.String: + case TemplateParameterType.Boolean: + await this.getStringParameter(parameter, inputs); + break; + case TemplateParameterType.GenericAzureResource: + await this.getAzureResourceParameter(parameter, inputs); + break; + case TemplateParameterType.SecureString: + default: + throw new Error(utils.format(Messages.parameterOfTypeNotSupported, parameter.type)); + } + } + } + + private async getAzureResourceParameter(parameter: TemplateParameter, inputs: WizardInputs): Promise { + let controlProvider = new ControlProvider(); + + if (!inputs.subscriptionId) { + await this.setSubscription(inputs); + } + + let azureResourceClient = new AzureResourceClient(inputs.azureSession.credentials, inputs.subscriptionId); + if (!!parameter) { + switch (parameter.dataSourceId) { + case PreDefinedDataSourceIds.ACR: + case PreDefinedDataSourceIds.AKS: + let azureResourceListPromise = azureResourceClient.getResourceList(parameter.dataSourceId, true) + .then((list) => list.map(x => { return { label: x.name, data: x }; })); + while (1) { + let selectedResource = await controlProvider.showQuickPick( + parameter.name, + azureResourceListPromise, + { placeHolder: parameter.displayName }, + utils.format(TelemetryKeys.pickListCount, parameter.dataSourceId)); + + let detailedResource = await this.tryGetSelectedResourceById( + selectedResource.data.id, + azureResourceClient, + ApiVersions.get(parameter.dataSourceId === PreDefinedDataSourceIds.ACR ? TargetResourceType.ACR : TargetResourceType.AKS)); + if (!detailedResource) { + throw utils.format(Messages.unableToGetSelectedResource, selectedResource.label); + } + + if (parameter.dataSourceId === PreDefinedDataSourceIds.ACR) { + // dynamic validation for ACR + if (detailedResource.properties.adminUserEnabled === false) { + controlProvider.showErrorMessage(constants.ResourceDynamicValidationFailure, Messages.onlyAdminEnabledRegistriesAreAllowed); + continue; + } + } + else { + + telemetryHelper.setTelemetry(TelemetryKeys.resourceIdHash, Utilities.createSha256Hash(selectedResource.data.id)); + + // handling case senstivity issue of httpApplicationRouting + if (detailedResource.properties.addonProfiles) { + let addonProfiles = JSON.parse(JSON.stringify(detailedResource.properties.addonProfiles).toLowerCase()); + if (addonProfiles.httpapplicationrouting) { + addonProfiles.httpApplicationRouting = addonProfiles.httpapplicationrouting; + delete addonProfiles['httpapplicationrouting']; + detailedResource.properties.addonProfiles = addonProfiles; + } + } + + // dynamic validation for AKS cluster + try { + let armRestClient = new ArmRestClient(inputs.azureSession); + await armRestClient.getAksKubeConfig(detailedResource.id); + } + catch (error) { + controlProvider.showErrorMessage(constants.ResourceDynamicValidationFailure, Messages.unableToGetAksKubeConfig); + continue; + } + } + + inputs.pipelineConfiguration.params[parameter.name] = detailedResource; + break; + } + break; + case PreDefinedDataSourceIds.WindowsApp: + case PreDefinedDataSourceIds.LinuxApp: + case PreDefinedDataSourceIds.FunctionApp: + case PreDefinedDataSourceIds.LinuxFunctionApp: + let selectedPipelineTemplate = inputs.pipelineConfiguration.template; + let matchingPipelineTemplates = templateHelper.getPipelineTemplatesForAllWebAppKind(inputs.sourceRepository.repositoryProvider, + selectedPipelineTemplate.label, selectedPipelineTemplate.language, selectedPipelineTemplate.targetKind); + + let appServiceClient = new AppServiceClient(inputs.azureSession.credentials, inputs.azureSession.environment, inputs.azureSession.tenantId, inputs.subscriptionId); + + let webAppKinds = matchingPipelineTemplates.map((template) => template.targetKind); + let selectedResource: QuickPickItemWithData = await controlProvider.showQuickPick( + Messages.selectTargetResource, + appServiceClient.GetAppServices(webAppKinds) + .then((webApps) => webApps.map(x => { return { label: x.name, data: x }; })), + { placeHolder: Messages.selectTargetResource }, + TelemetryKeys.AzureResourceListCount); + + telemetryHelper.setTelemetry(TelemetryKeys.resourceIdHash, Utilities.createSha256Hash((selectedResource.data).id)); + if (await appServiceClient.isScmTypeSet((selectedResource.data).id)) { + await openBrowseExperience((selectedResource.data).id); + throw Error(Messages.setupAlreadyConfigured); + } + else { + inputs.pipelineConfiguration.params[constants.TargetResource] = selectedResource.data; + inputs.pipelineConfiguration.template = matchingPipelineTemplates.find((template) => template.targetKind === inputs.targetResource.resource.kind); + } + break; + default: + throw new Error(utils.format(Messages.parameterWithDataSourceOfTypeNotSupported, parameter.dataSourceId)); + } + } + } + + private async setSubscription(inputs: WizardInputs): Promise { + // show available subscriptions and get the chosen one + let subscriptionList = extensionVariables.azureAccountExtensionApi.filters.map((subscriptionObject) => { + return { + label: `${subscriptionObject.subscription.displayName}`, + data: subscriptionObject, + description: `${subscriptionObject.subscription.subscriptionId}` + }; + }); + + let selectedSubscription: QuickPickItemWithData = await new ControlProvider().showQuickPick( + constants.SelectSubscription, + subscriptionList, + { placeHolder: Messages.selectSubscription }, + TelemetryKeys.SubscriptionListCount); + inputs.subscriptionId = selectedSubscription.data.subscription.subscriptionId; + inputs.azureSession = await getSubscriptionSession(inputs.subscriptionId); + } + + private async tryGetSelectedResourceById(selectedResourceId: string, azureResourceClient: AzureResourceClient, getResourceApiVersion?: string): Promise { + try { + let detailedResource = null; + if (getResourceApiVersion) { + detailedResource = await azureResourceClient.getResource(selectedResourceId, getResourceApiVersion); + } + else { + detailedResource = await azureResourceClient.getResource(selectedResourceId); + } + + if (detailedResource) { + return detailedResource; + } + } + catch (err) { + console.log(err); + // continue; + } + + return null; + } + + private async getStringParameter(parameter: TemplateParameter, inputs: WizardInputs): Promise { + let controlProvider = new ControlProvider(); + + let mustacheContext = new MustacheContext(inputs); + if (!parameter.dataSourceId) { + if (parameter.defaultValue) { + let renderedDefaultValue = MustacheHelper.render(parameter.defaultValue, mustacheContext); + inputs.pipelineConfiguration.params[parameter.name] = renderedDefaultValue; + } + else { + inputs.pipelineConfiguration.params[parameter.name] = await controlProvider.showInputBox( + parameter.name, + { + placeHolder: parameter.displayName + } + ); + } + } + else { + switch (parameter.dataSourceId) { + case PreDefinedDataSourceIds.RepoAnalysis: + if (parameter.name.toLowerCase() === 'containerport') { + var port = templateHelper.getDockerPort(inputs.sourceRepository.localPath); + port = port ? port : parameter.defaultValue; + + inputs.pipelineConfiguration.params[parameter.name] = port; + } + break; + default: + if (parameter.options) { + inputs.pipelineConfiguration.params[parameter.name] = await controlProvider.showQuickPick( + parameter.name, + parameter.options ? parameter.options.map(x => { return { label: x.key, data: x.value }; }) : [], + { placeHolder: parameter.displayName }, + utils.format(TelemetryKeys.pickListCount, parameter.name)); + } + else if (!inputs.pipelineConfiguration.params[parameter.name]) { + inputs.pipelineConfiguration.params[parameter.name] = MustacheHelper.render(parameter.defaultValue, mustacheContext); + } + } + } + } +} \ No newline at end of file diff --git a/Source/configure/model/Contracts.ts b/Source/configure/model/Contracts.ts new file mode 100644 index 00000000..7db20fae --- /dev/null +++ b/Source/configure/model/Contracts.ts @@ -0,0 +1,294 @@ +export interface ExtendedInputDescriptor extends InputDescriptor { + /** + * Name of the data source which can be used to fetch possible values for this input + */ + dataSourceId: string; + /** + * Default value of the input. If PossibleValues is specified, default value must of one of those + */ + defaultValue: string; + /** + * List of dynamic remote url-based validations which can be performed on the input. + */ + dynamicValidations: InputDynamicValidation[]; + /** + * Name of the group to which this input belongs + */ + groupId: string; + /** + * Mode in which the value of this input should be entered. Currently supported values - TextBox and Combo + */ + inputMode: InputMode; + /** + * Specifies whether a value for this input must be provided + */ + isRequired: boolean; + /** + * Localized name which can be shown as a label for the input + */ + name: string; + /** + * Additional properties for the input group. This can contains UI hints for input placement, decoration, summary, etc + */ + properties: { [key: string]: any; }; + /** + * Static validation which can be performed on the input + */ + staticValidation: InputStaticValidation; + /** + * Specifies the name which can be shown as sublabel for the input control + */ + sublabel: string; + /** + * Defines the visibility rule of the input. + */ + visibleRule: string; +} + + +/** + * Extended version of pipeline template. It will additionally contains UX hints and other information which can be helpful to draw UX for this template + */ +export interface ExtendedPipelineTemplate { + /** + * Description of the CI/CD pipeline which is enabled by this template + */ + description: string; + /** + * Unique id of the pipeline template + */ + id: string; + /** + * List of the inputs required to create CI/CD pipeline + */ + + attributes?: { [key: string]: string; }; + + parameters?: Parameters; + + configuration?: Configuration; +} + +export interface Configuration { + pipelineDefinition?: { [key: string]: string; }; + + assets?: Asset[]; + + variables?: Variable[]; + + imports?: any; +} + +export interface Parameters { + + /** + * List of the inputs required to create CI/CD pipeline + */ + inputs?: ExtendedInputDescriptor[]; + + /** + * List of data sources associated with this template + */ + dataSources?: DataSource[]; + /** + * List of input groups + */ + groups?: InputGroup[]; + +} + +export interface DataSource { + /** + * Stem of the remote URL to which request will be made. URL base is assumed to be Azure ARM URL base. + */ + endpointUrlStem: string; + /** + * HTTP method for request + */ + httpMethod?: string; + id: string; + /** + * Serialized string for request body + */ + requestBody?: string; + /** + * Jsonpath selector to get result from response + */ + resultSelector?: string; + /** + * Result template which will be used to transform the data source result. + */ + resultTemplate?: string; +} + +export interface InputGroup { + /** + * Unique id for a group + */ + id: string; + /** + * Localized name which can be shown as a label for the group + */ + name: string; + /** + * Additional properties for the input group. This can contains UI hints for group placement etc. + */ + properties: { [key: string]: string; }; +} + +/** + * Mode in which an input must be entered in UI + */ +export enum InputMode { + /** + * This input should not be shown in the UI + */ + None = 0, + /** + * An input text box should be shown + */ + TextBox = 10, + /** + * An password input box should be shown + */ + PasswordBox = 20, + /** + * A select/combo control should be shown + */ + Combo = 30, + /** + * Checkbox should be shown(for true/false values) + */ + CheckBox = 40, + /** + * A control for choosing azure subscription should be shown + */ + AzureSubscription = 50, + /** + * A control for choosing AAD tenant + */ + TenantId = 60, + /** + * A control to acquire AAD access token. Can be hidden if acquiring access token is non-interactive + */ + AadAccessToken = 70, + /** + * A control for choosing one of the options from radio buttons shown + */ + RadioButtons = 80, + /** + * A control for choosing virtual machine size + */ + VirtualMachineSizeControl = 90 +} + +export enum InputDataType { + String = 0, + SecureString = 1, + Int = 2, + Bool = 3, + Authorization = 4 +} + +export interface InputDescriptor { + /** + * Description of what this input is used for + */ + description: string; + /** + * Identifier for the input + */ + id: string; + /** + * Possible values that this input can take + */ + possibleValues: InputValue[]; + /** + * Data type of the input + */ + // tslint:disable-next-line:no-reserved-keywords + type: InputDataType; +} + +/** + * A dynamic validation for input value. Validation will done based on response from a data source + */ +export interface InputDynamicValidation { + /** + * Name of the data source to which a HTTP request will be made + */ + dataSourceId: string; + /** + * Error message to show if this dynamic validation fails + */ + errorMessage: string; + /** + * Result template which will transform data source response into success or failure + */ + resultTemplate: string; +} + +export interface InputStaticValidation { + /** + * Error message to show if static validation fails + */ + errorMessage: string; + /** + * Maximum supported length of a string type input + */ + maxLength: number; + /** + * Maximum supported value for a numeric type input + */ + maxValue: number; + /** + * Minimum supported length of a string type input + */ + minLength: number; + /** + * Minimum supported value for a numeric type input + */ + minValue: number; + /** + * Regex pattern against which value will be matched + */ + pattern: string; + /** + * Regex flags for pattern matching like 'i' for ignore case, etc + */ + regexFlags: string; +} + +/** + * Information about a single value for an input + */ +export interface InputValue { + /** + * The text to show for the display of this value + */ + displayValue: string; + /** + * The value to store for this input + */ + value: string; +} + +export interface Variable { + id: string; + value: string; + logTelemetry?: boolean; + hashTelemetryValue?: boolean; +} + +export interface Asset { + id: string; + // tslint:disable-next-line:no-reserved-keywords + type: string; + stage: ConfigurationStage; + inputs: { [key: string]: any }; +} + +export enum ConfigurationStage { + Pre = "Pre", + Post = "Post" +}; diff --git a/Source/configure/model/azureDevOps.ts b/Source/configure/model/azureDevOps.ts new file mode 100644 index 00000000..37bacc89 --- /dev/null +++ b/Source/configure/model/azureDevOps.ts @@ -0,0 +1,62 @@ +import { RepositoryProvider } from "./models"; + +export interface BuildDefinition { + name: string; + path: string; + // tslint:disable-next-line:no-reserved-keywords + type: number; + quality: number; + process: YamlProcess; + project: { id: string, name: string }; + repository: BuildDefinitionRepository; + triggers: Array; + queue: { id: number }; + properties: { [key: string]: string }; +} + +export interface BuildDefinitionRepository { + id: string; + name: string; + // tslint:disable-next-line:no-reserved-keywords + type: RepositoryProvider; + defaultBranch: string; + url: string; + properties?: BuildDefinitionRepositoryProperties; +} + +export interface BuildDefinitionRepositoryProperties { + connectedServiceId: string; + apiUrl: string; + branchesUrl: string; + cloneUrl: string; + defaultBranch: string; + fullName: string; + refsUrl: string; +} + +export interface BuildDefinitionTrigger { + triggerType: number; + settingsSourceType: number; + batchChanges: boolean; +} + +export interface YamlProcess { + // tslint:disable-next-line:no-reserved-keywords + type: number; + yamlFileName: string; +} + +export interface Build { + id: string; + definition: { id: number, url?: string }; + project: { id: string }; + sourceBranch: string; + sourceVersion: string; + _links?: { web: { href: string } }; +} + +export interface Repository { + id: string; + name: string; + remoteUrl: string; +} \ No newline at end of file diff --git a/Source/configure/model/models.ts b/Source/configure/model/models.ts new file mode 100644 index 00000000..dc5055e1 --- /dev/null +++ b/Source/configure/model/models.ts @@ -0,0 +1,298 @@ +import { SubscriptionModels } from 'azure-arm-resource'; +import { GenericResource } from 'azure-arm-resource/lib/resource/models'; +import { ApplicationSettings } from "azureintegration-repoanalysis-client-internal"; +import { ServiceClientCredentials } from 'ms-rest'; +import { AzureEnvironment } from 'ms-rest-azure'; +import { ExtensionContext, OutputChannel, QuickPickItem, workspace } from 'vscode'; +import { IAzureUserInput, ITelemetryReporter, UIExtensionVariables } from 'vscode-azureextensionui'; +import { Messages } from '../resources/messages'; +import { PipelineTemplate } from './templateModels'; + +class ExtensionVariables implements UIExtensionVariables { + public azureAccountExtensionApi: AzureAccountExtensionExports; + public context: ExtensionContext; + public outputChannel: OutputChannel; + public reporter: ITelemetryReporter; + public ui: IAzureUserInput; + public enableGitHubWorkflow: boolean; + public remoteConfigurerEnabled: boolean; + public isLocalRepo: boolean; + + constructor() { + this.enableGitHubWorkflow = !workspace.getConfiguration().get('deployToAzure.UseAzurePipelinesForGithub'); + this.remoteConfigurerEnabled = true; + this.isLocalRepo = false; + } +} + +let extensionVariables = new ExtensionVariables(); +export { extensionVariables }; + +export class WizardInputs { + organizationName: string; + project: DevOpsProject; + isNewOrganization: boolean; + sourceRepository: GitRepositoryParameters; + targetResource: AzureParameters = new AzureParameters(); + repositoryAnalysisApplicationSettings: ApplicationSettings; + pipelineConfiguration: PipelineConfiguration = new PipelineConfiguration(); + azureSession: AzureSession; + subscriptionId: string; + githubPATToken?: string; + potentialTemplates?: PipelineTemplate[]; +} + +export class AzureParameters { + resource: GenericResource; + serviceConnectionId: string; +} + +export class Organization { + accountId: string; + accountName: string; + accountUri: string; + properties: {}; + isMSAOrg: boolean; +} + +export class GitHubOrganization { + login: string; + id: number; + url: string; + repos_url: string; + isUserAccount?: boolean = false; +} + +export class GitHubRepo { + id: string; + name: string; + orgName: string; + html_url: string; + description: string; +} + +export class AzureSession { + environment: AzureEnvironment; + userId: string; + tenantId: string; + credentials: ServiceClientCredentials; +} + +export class PipelineConfiguration { + filePath: string; + template: PipelineTemplate; + workingDirectory: string; + params: { [key: string]: any } = {}; + assets: { [key: string]: any } = {}; +} + +export class MustacheContext { + constructor(inputs: WizardInputs) { + this.inputs = inputs.pipelineConfiguration.params; + this.assets = inputs.pipelineConfiguration.assets; + this.workingDirectory = inputs.pipelineConfiguration.workingDirectory; + this.sourceRepository = inputs.sourceRepository; + this.targetResource = inputs.targetResource; + this.repositoryAnalysisApplicationSettings = inputs.repositoryAnalysisApplicationSettings; + } + + inputs: { [key: string]: any } = {}; + assets: { [key: string]: any } = {}; + // we also need to remove working directory and make it an explicit parameter of template, which will be present as part of inputs. + workingDirectory: string; + // the below two properties will be removed during transition to parameterized templates. + sourceRepository: GitRepositoryParameters; + targetResource: AzureParameters; + repositoryAnalysisApplicationSettings: ApplicationSettings; +} + +export class QuickPickItemWithData implements QuickPickItem { + label: string; + data: any; + description?: string; + detail?: string; +} + +export enum ControlType { + None, + QuickPick, + InputBox +} + +export interface StringMap { + [key: string]: T; +} + +export class ParsedAzureResourceId { + public resourceId: string; + public subscriptionId: string; + public resourceGroup: string; + public resourceType: string; + public resourceProvider: string; + public resourceName: string; + public childResourceType?: string; + public childResource?: string; + + constructor(resourceId: string) { + if (!resourceId) { + throw new Error(Messages.resourceIdMissing); + } + + this.resourceId = resourceId; + this.parseId(); + } + + private parseId() { + // remove all empty parts in the resource to avoid failing in case there are leading/trailing/extra '/' + let parts = this.resourceId.split('/').filter((part) => !!part); + if (!!parts) { + for (let i = 0; i < parts.length; i++) { + switch (i) { + case 1: + this.subscriptionId = parts[i]; + break; + case 3: + this.resourceGroup = parts[i]; + break; + case 5: + this.resourceProvider = parts[i]; + break; + case 6: + this.resourceType = parts[i]; + break; + case 7: + this.resourceName = parts[i]; + break; + case 8: + this.childResourceType = parts[i]; + break; + case 9: + this.childResource = parts[i]; + break; + } + } + } + } +} + +export interface AzureAccountExtensionExports { + sessions: AzureSession[]; + subscriptions: { session: AzureSession, subscription: SubscriptionModels.Subscription }[]; + filters: { session: AzureSession, subscription: SubscriptionModels.Subscription }[]; + waitForLogin: () => Promise; + waitForSubscriptions: () => Promise; +} + +export interface DevOpsProject { + id: string; + name: string; +} + +export interface GitRepositoryParameters { + repositoryProvider: RepositoryProvider; + repositoryName: string; + repositoryId: string; + remoteName: string; + remoteUrl: string; + branch: string; + commitId: string; + localPath: string; + serviceConnectionId?: string; // Id of the service connection in Azure DevOps +} + +export enum AzureConnectionType { + None, + AzureRMServicePrincipal, + AzureRMPublishProfile +} + +export interface Token { + session: AzureSession; + accessToken: string; + refreshToken: string; +} + +export interface AadApplication { + appId: string; + secret: string; + objectId: string; +} + +export interface GitBranchDetails { + remoteName: string; + branch: string; +} + +export interface WebAppSourceControl { + id: string; + name: string; + properties: { + repoUrl: string; + isGitHubAction: boolean; + branch: string; + }; +} + +export enum SourceOptions { + CurrentWorkspace = 'Current workspace', + BrowseLocalMachine = 'Browse local machine', + GithubRepository = 'Github repository' +} + +export enum RepositoryProvider { + Github = 'github', + AzureRepos = 'tfsgit' +} + +export enum TargetResourceType { + None = 'none', + WebApp = 'Microsoft.Web/sites', + AKS = 'Microsoft.ContainerService/ManagedClusters', + ACR = 'Microsoft.ContainerRegistry/registries' +} + +export enum ServiceConnectionType { + GitHub = 'github', + AzureRM = 'arm', + ACR = "containerRegistery", + AKS = 'azureKubernetes' +} + +export enum TargetKind { + WindowsApp = 'app', + FunctionApp = 'functionapp', + FunctionAppLinux = 'functionapp,linux', + FunctionAppLinuxContainer = 'functionapp,linux,container', + LinuxApp = 'app,linux', + LinuxContainerApp = 'app,linux,container' +} + +export enum SupportedLanguage { + NONE = 'none', + NODE = 'node', + PYTHON = 'python', + DOTNETCORE = 'dotnetcore', + DOTNET = 'dotnet', + DOCKER = 'docker' +} + +export interface IPredicate { + inputName: string; + condition: string; + inputValue: string; +} + +export interface IVisibilityRule { + predicateRules: IPredicate[]; + operator: string; +} + +export enum PipelineType { + AzurePipeline, + GitHubPipeline +} + +export interface IResourceNode { + resource: GenericResource; + subscriptionId: string; +} diff --git a/Source/configure/model/provisioningConfiguration.ts b/Source/configure/model/provisioningConfiguration.ts new file mode 100644 index 00000000..d040f395 --- /dev/null +++ b/Source/configure/model/provisioningConfiguration.ts @@ -0,0 +1,53 @@ +export interface Authorization { + scheme: string; + parameters: { [key: string]: string }; +} + +export interface CodeRepository { + id: string; + // tslint:disable-next-line:no-reserved-keywords + type: string; + defaultBranch: string; + authorizationInfo: Authorization; +} + +export interface ProvisioningConfiguration { + id: string; + pipelineTemplateId: string; + pipelineTemplateParameters: { [key: string]: string }; + branch: string; + provisioningMode: provisioningMode; + pipelineConfiguration?: DraftPipelineConfiguration; + result?: Result; +} + +export enum provisioningMode { + draft = "draft", + complete = "complete", +} + +export interface PipelineConfiguration { + id: string; + // tslint:disable-next-line:no-reserved-keywords + type: string; +} + +export interface CompletePipelineConfiguration extends PipelineConfiguration { + path: string; + commitId: string; +} + +export interface DraftPipelineConfiguration extends PipelineConfiguration { + files: File[]; +} + +export interface File { + content: string; + path: string; +} + +export interface Result { + status: string; + message: string; + pipelineConfiguration: PipelineConfiguration; +} diff --git a/Source/configure/model/templateModels.ts b/Source/configure/model/templateModels.ts new file mode 100644 index 00000000..2bc212ac --- /dev/null +++ b/Source/configure/model/templateModels.ts @@ -0,0 +1,102 @@ +import { ExtendedPipelineTemplate } from "./Contracts"; +import { AzureConnectionType, ServiceConnectionType, TargetKind, TargetResourceType } from "./models"; + +export enum TemplateType { + REMOTE, + LOCAL +} + +export interface PipelineTemplate { + label: string; + templateWeight: number; + templateType: TemplateType; + targetType: TargetResourceType; + targetKind: TargetKind; + language: string; +} + +export interface TemplateInfo { + templateId: string; + workingDirectory: string; + templateWeight: number; + templateDescription: string; + templateLabel: string; + attributes: TemplateAttributes; +} + +export interface RemotePipelineTemplate extends PipelineTemplate, ExtendedPipelineTemplate { + workingDirectory: string; + +} + +export interface LocalPipelineTemplate extends PipelineTemplate { + path: string; + enabled: boolean; + parameters?: TemplateParameter[]; + assets?: TemplateAsset[]; + // this should be removed as we will have endpoints/secrets as assets and not a first class property + azureConnectionType?: AzureConnectionType; +} + +export interface TemplateAttributes { + language: string; + buildTarget: string; + deployTarget: string; +} + +export interface TemplateParameter { + name: string; + displayName: string; + // tslint:disable-next-line:no-reserved-keywords + type: TemplateParameterType; + dataSourceId?: string; + defaultValue?: any; + options?: { key: string, value: any }[]; +} + +export interface TemplateAsset { + id: string; + // tslint:disable-next-line:no-reserved-keywords + type: TemplateAssetType; +} + +export enum GitHubSecretType { + AKSKubeConfigSecret = 'aksKubeConfig', + AzureRM = 'arm', + ContainerRegistryUsername = "containerRegistryUsername", + ContainerRegistryPassword = "containerRegistryPassword" +} + +export enum TemplateParameterType { + GenericAzureResource, + Boolean, + SecureString, + String +} + +export enum TemplateAssetType { + AzureARMServiceConnection = "endpoint:" + ServiceConnectionType.AzureRM, + AzureARMPublishProfileServiceConnection = "endpoint:" + ServiceConnectionType.AzureRM + ":publishProfile", + ACRServiceConnection = "endpoint:" + ServiceConnectionType.ACR, + AKSKubeConfigServiceConnection = "endpoint:" + ServiceConnectionType.AKS + ":kubeconfig", + + GitHubARM = "gitHubSecret:" + GitHubSecretType.AzureRM, + GitHubARMPublishProfile = "gitHubSecret:" + GitHubSecretType.AzureRM + ":publishProfile", + GitHubAKSKubeConfig = "gitHubSecret:" + GitHubSecretType.AKSKubeConfigSecret + ":kubeconfig", + GitHubRegistryUsername = "gitHubSecret:" + GitHubSecretType.ContainerRegistryUsername, + GitHubRegistryPassword = "gitHubSecret:" + GitHubSecretType.ContainerRegistryPassword, + File = "file" + ":" +} + +export let PreDefinedDataSourceIds = { + ACR: TargetResourceType.ACR, + AKS: TargetResourceType.AKS, + FunctionApp: TargetResourceType.WebApp + ":" + TargetKind.FunctionApp, + LinuxApp: TargetResourceType.WebApp + ":" + TargetKind.LinuxApp, + LinuxContainerApp: TargetResourceType.WebApp + ":" + TargetKind.LinuxContainerApp, + LinuxFunctionApp: TargetResourceType.WebApp + ":" + TargetKind.FunctionAppLinux, + LinuxContainerFunctionApp: TargetResourceType.WebApp + ":" + TargetKind.FunctionAppLinuxContainer, + WindowsApp: TargetResourceType.WebApp + ":" + TargetKind.WindowsApp, + + RepoAnalysis: 'RepoAnalysis' +}; diff --git a/Source/configure/resources/constants.ts b/Source/configure/resources/constants.ts new file mode 100644 index 00000000..532812bb --- /dev/null +++ b/Source/configure/resources/constants.ts @@ -0,0 +1,203 @@ +export const AzureDevOpsBaseUrl: string = "https://dev.azure.com"; +export const HostedVS2017QueueName: string = "Hosted VS2017"; +export const ReservedHostNames: string[] = [ + // Reserved names from https://dev.azure.com/mseng/AzureDevOps/_git/AzureDevOps?path=%2FMps%2FService%2FNameResolution%2FServicing%2FHost%2FDeployment%2FGroups%2FInstall%2FInstallNameResolutionService.xml&version=GBmaster + // Forbidden + "aisvc", + "cdn1", + "cdn2", + "cdn3", + "developers", + "elstest", + "events", + "hooks", + "integrate", + "launch", + "myname", + "servicefabric", + "sps", + "tfsodata", + "vssh", + "vsrm", + "yourname", + "app", + "deployment", + "services", + "teamfoundation", + "teamfoundationserver", + "tfs", + "ww", + "www", + "wwww", + + // MicrosoftReserved + "alm", + "almsearch", + "api", + "auditservice", + "azchatops", + "azdevchatops", + "azminicom", + "azboards", + "careers", + "cdn1", + "cdn2", + "code", + "codesmarts", + "dev", + "dl", + "docs", + "download", + "e", + "elstest", + "events", + "exchange", + "explore", + "feeds", + "forums", + "githubapp", + "githubapps", + "gdprdel", + "hooks", + "i2", + "i3", + "insightsportal", + "intellitrace", + "internetexplorer", + "jscript", + "launch", + "liveshare", + "lync", + "media", + "my", + "offer", + "orgsearch", + "pipelines", + "pipelinesapp", + "portal", + "premium", + "professional", + "project-cascade", + "promo", + "reg", + "skydrive", + "scaleunits", + "secretscan", + "servicedeployments", + "servicehosts", + "sps", + "sqlazure", + "ssh", , + "start", + "status", , + "statusalt1", + "status-alt1", + "support", + "taco", + "tfs", + "tfsapp", + "tfsodata", + "tutorials", + "ultimate", + "userext", + "video", + "vscatalog", + "vsdevprofile", + "vsdscops", + "vsengsaas", + "vsevidence", + "vskeros", + "vslicense", + "vsnotify", + "vsmps", + "vsrtc", + "vssh", + "vsodata", + "vspolicy", + "vssps", + "vsstage", + "vstmr", + "vstsusers", + "vsworking", + "web", + "webmatrix", + "webtooling", + "www", + "x-boards", + "x-pipes", + "x-ibizacd", + + // Based on past failures + "teamservices", + "java", + "beta" +]; + +export const PipelineTemplateLabels = { + SimpleApplicationToAppService: 'Simple application to App Service', + NodeJSWithNpmToAppService: 'Node.js with npm to App Service', + NodeJSWithGulpToAppService: 'Node.js with Gulp to App Service', + NodeJSWithGruntToAppService: 'Node.js with Grunt to App Service', + NodeJSWithAngularToAppService: 'Node.js with Angular to App Service', + NodeJSWithWebpackToAppService: 'Node.js with Webpack to App Service', + DotNetCoreWebAppToAppService: '.NET Core Web App to App Service', + NodeJSFunctionAppToAzureFunction: 'Node.js Function App to Azure Function', + DotNetCoreFunctionAppToAzureFunction: '.NET Core Function App to Azure Function', + PythonFunctionAppToLinuxAzureFunction: 'Python Function App to Linux Azure Function' +}; + +export const SelectFolderOrRepository = 'selectFolderOrRepository'; +export const SelectOrganization = 'selectOrganization'; +export const SelectProject = 'selectProject'; +export const EnterOrganizationName = 'enterOrganizationName'; +export const SelectPipelineTemplate = 'selectPipelineTemplate'; +export const SelectSubscription = 'selectSubscription'; +export const SelectWorkspace = 'selectWorkspace'; +export const SelectWebApp = 'selectWebApp'; +export const SelectFunctionApp = 'selectFunctionApp'; +export const GitHubPat = 'gitHubPat'; +export const SelectFromMultipleWorkSpace = 'selectFromMultipleWorkSpace'; +export const SelectRemoteForRepo = 'selectRemoteForRepo'; +export const VstsRmScmType = 'VSTSRM'; +export const BrowseNotAvailableConfigurePipeline = 'BrowseNotAvailableConfigurePipeline'; +export const DeploymentMessageType = 'CDDeploymentConfiguration'; +export const SetupAlreadyExists = 'SetupAlreadyExists'; +export const Browse = 'Browse'; +export const TargetResource = 'targetResource'; +export const ResourceDynamicValidationFailure = 'ResourceDynamicValidationFailure'; +export const EnterGithubRepositoryName = 'EnterGithubRepositoryName'; +export const SelectGitHubOrganization = 'selectGitHubOrganization'; + +//RepoAnalysis constants expected in response of Repository Analysis Service +export const RepoAnalysisConstants = { + //Common + Master: 'master', + + //Node + Gulp: 'gulp', + Grunt: 'grunt', + GulpFilePath: 'gulpFilePath', + GruntFilePath: 'gruntFilePath', + PackageFilePath: 'packageFilePath', + + //Python + Django: 'django', + RequirementsFilePath: 'requirementsFilePath', + + //Functions + AzureFunctions: 'azure:functions', + HostFilePath: 'hostFilePath' +}; + +export const deploymentManifest: string = "deployment"; +export const serviceManifest: string = "service"; +export const serviceIngressManifest: string = "service-ingress"; +export const ingressManifest: string = "ingress"; +export const azurePipeline: string = "Azure-pipeline"; +export const githubWorkflow: string = "Github-workflow"; +export const clientPropertyKey: string = "ms.client.vscode"; +export const inputModeProperty: string = "inputMode"; + +export const ExceptionType = { + UnauthorizedRequestException: 'UNAUTHORIZEDREQUESTEXCEPTION' +}; \ No newline at end of file diff --git a/Source/configure/resources/messages.ts b/Source/configure/resources/messages.ts new file mode 100644 index 00000000..e175deb8 --- /dev/null +++ b/Source/configure/resources/messages.ts @@ -0,0 +1,126 @@ +export class Messages { + public static acquireTokenFromRefreshTokenFailed: string = 'Acquiring token with refresh token failed. Error: %s.'; + public static addAzurePipelinesYmlFile: string = 'Added Azure Pipelines YAML definition.'; + public static addGitHubWorkflowYmlFile: string = 'Added GitHub Workflow YAML definition.'; + public static fetchingTemplates: string = 'Fetching templates'; + public static appKindIsNotSupported: string = 'App type "%s" is not yet supported.'; + public static azureResourceIsNull: string = 'ArgumentNullException: resource. The Azure target resource is empty, kindly select a resource and try again.'; + public static azureAccountExntesionUnavailable: string = 'Azure-Account extension could not be fetched. Please ensure it\'s installed and activated.'; + public static azureLoginRequired: string = 'Please sign in to your Azure account first.'; + public static branchRemoteMissing: string = `The current branch doesn't have a tracking branch, and the selected repository has no remotes. We're unable to create a remote tracking branch. Please [set a remote tracking branch](https://git-scm.com/docs/git-branch#Documentation/git-branch.txt---track) first, and then try this again.`; + public static browsePipeline: string = 'Browse Pipeline'; + public static cannotAddFileRemoteMissing: string = 'Couldn\'t add YAML file to your repo because the remote isn\'t set'; + public static cannotIdentifyRespositoryDetails: string = 'Couldn\'t get repository details. Ensure your repo is hosted on [Azure Repos](https://docs.microsoft.com/azure/devops/repos/get-started) or [GitHub](https://guides.github.com/activities/hello-world/).'; + public static commitAndPush: string = 'Commit & push'; + public static commitFailedErrorMessage: string = `Commit failed due to error: %s`; + public static configuringPipelineAndDeployment: string = 'Configuring pipeline and proceeding to deployment...'; + public static couldNotAuthorizeEndpoint: string = 'Couldn\'t authorize endpoint for use in Azure Pipelines.'; + public static creatingAzureDevOpsOrganization: string = 'Creating Azure DevOps organization.'; + public static creatingAzureServiceConnection: string = 'Creating Azure deployment credentials with your subscription: %s'; + public static creatingKubernetesConnection: string = 'Creating connection with kubernetes resource: %s'; + public static creatingContainerRegistryConnection: string = 'Creating connection with container registry resource: %s'; + public static creatingGitHubServiceConnection: string = 'Creating GitHub service connection'; + public static discardPipeline: string = 'Discard pipeline'; + public static enterAzureDevOpsOrganizationName: string = 'Azure DevOps organization name where your pipeline will be hosted'; + public static enterGitHubPat: string = 'Enter GitHub personal access token (PAT), required to populate secrets that are used in the Github workflow'; + public static failedToCreateAzureDevOpsProject: string = 'Couldn\'t create a project in the Azure DevOps organization. Error: %s.'; + public static failedToCreateAzurePipeline: string = 'Couldn\'t configure pipeline. Error: %s'; + public static failedToDetermineAzureRepoDetails: string = 'Failed to determine Azure Repo details from remote url. Please ensure that the remote points to a valid Azure Repos url.'; + public static githubPatTokenErrorMessage: string = 'GitHub PAT token cannot be empty.'; + public static githubPatTokenHelpMessage: string = 'GitHub personal access token (PAT) with following permissions: Update github action workflows, read and write access to all repository data.'; + public static githubPatTokenHelpMessageGithubWorkflow: string = 'GitHub personal access token (PAT) with following permissions: read and write access to all repository data.'; + public static modifyAndCommitFile: string = 'Modify and save your YAML file. %s will commit this file, push the branch \'%s\' to remote \'%s\' and proceed with deployment.'; + public static modifyAndCommitFileWithGitInitialization: string = 'Modify and save your YAML file to proceed with deployment.'; + public static noAgentQueueFound: string = 'No agent pool found named "%s".'; + public static notAGitRepository: string = 'Selected workspace is not a [Git](https://git-scm.com/docs/git) repository. Please select a Git repository.'; + public static notAzureRepoUrl: string = 'The repo isn\'t hosted with Azure Repos.'; + public static noWorkSpaceSelectedError: string = 'Please select a workspace folder to configure pipeline.'; + public static operationCancelled: string = 'Operation cancelled.'; + public static operationTimedOut: string = 'Operation timed out.'; + public static organizationNameReservedMessage: string = 'The organization name %s isn\'t available. Please try another organization name.'; + public static organizationNameStaticValidationMessage: string = 'Organization names must start and end with a letter or number and can contain only letters, numbers, and hyphens.'; + public static pipelineSetupSuccessfully: string = 'Pipeline set up successfully!'; + public static remoteRepositoryNotConfigured: string = 'Remote repository is not configured. This extension is compatible with [Azure Repos](https://docs.microsoft.com/en-us/azure/devops/repos/get-started) or [GitHub](https://guides.github.com/activities/hello-world/).'; + public static resourceIdMissing: string = 'Required argument "resourceId" is missing. Please pass the argument for getting resource.'; + public static resourceTypeIsNotSupported: string = '"%s" resources are not yet supported for configuring pipelines.'; + public static selectFolderLabel: string = 'Select source folder for configuring pipeline'; + public static selectOrganization: string = 'Select an Azure DevOps organization'; + public static selectPipelineTemplate: string = 'Select a pipeline template'; + public static selectProject: string = 'Select an Azure DevOps project'; + public static selectRemoteForBranch: string = 'Select the remote repository where you want to track your current branch'; + public static selectSubscription: string = 'Select the target Azure Subscription to deploy to your application'; + public static selectWorkspace: string = 'Select the working directory to your application'; + public static selectTargetResource: string = 'Select the target Azure Resource to deploy your application'; + public static selectWorkspaceFolder: string = 'Select a folder from your workspace to deploy'; + public static signInLabel: string = 'Sign In'; + public static signUpLabel: string = 'Sign Up'; + public static unableToCreateAzureServiceConnection: string = `Unable to store connection details for Azure subscription.\nOperation Status: %s\nMessage: %s\nService connection is not in ready state.`; + public static unableToCreateGitHubServiceConnection: string = `Unable to store connection details for GitHub.\nOperation Status: %s\nService connection is not in ready state.`; + public static retryFailedMessage: string = `Failed after retrying: %s times. Internal Error: %s`; + public static azureServicePrincipalFailedMessage: string = `Failed while creating Azure service principal.`; + public static roleAssignmentFailedMessage: string = `Failed while role assignement.`; + public static waitForAzureSignIn: string = `Waiting for Azure sign-in...`; + public static userCancelledExcecption = 'User cancelled the action'; + public static cannotFindPipelineUrlInMetaDataException = 'We were unable to find pipeline associated with the Azure Web App. Please click on "Browse Pipeline" to explore.'; + public static cannotFindOrganizationWithName = 'Unable to find organization with name: %s'; + public static browseNotAvailableConfigurePipeline = 'No pipeline is configured for this Azure Web App. Please click on "Deploy to Azure" to setup.'; + public static didNotRecieveAzureResourceNodeToProcess = 'Unable to browse the pipeline for you. Please raise an issue in the [repo](https://github.com/microsoft/vscode-deploy-azure/issues).'; + public static copyAndOpenLabel: string = 'Copy & Open'; + public static nextLabel: string = 'Next'; + public static githubWorkflowSetupSuccessfully: string = 'GitHub workflow set up successfully !'; + public static copyAndCreateSecretMessage: string = 'To deploy to Azure App Service via GitHub workflow, create a new secret with name \'%s\' in your repository. Copy the below secret value to add the secret'; + public static browseWorkflow: string = 'Browse Workflow'; + public static deploymentLogMessage: string = 'Configured from VS Code'; + public static setupAlreadyConfigured = 'Setup is already configured for your web app. Browse to know more about the existing setup.'; + public static settingUpGithubSecrets = 'Setting up GitHub Workflow secrets'; + public static parameterOfTypeNotSupported = 'Parameter of type %s is not supported.'; + public static parameterWithDataSourceOfTypeNotSupported = 'Parameter with data source Id: %s is not supported.'; + public static assetOfTypeNotSupportedForGitHub = 'Asset of type %s is not supported for GitHub workflows.'; + public static assetOfTypeNotSupportedForAzurePipelines = 'Asset of type %s is not supported for Azure Pipelines.'; + public static assetOfTypeNotSupported = 'Asset of type %s is not supported.'; + public static couldNotFindTargetResourceValueInParams = 'Could not find corresponding parameter value for template\'s target resource type: %s.'; + public static assetCreationOfTypeFailedWithError = 'Creation of asset of type: %s, failed with error: %s'; + public static azureResourceTemplateParameterCouldNotBeFound = 'Template has no parameter of type %s.'; + public static parameterWithNameNotSet = 'Parameter with name: %s has not yet been set, hence its value could not be found.'; + public static unableToFetchPasswordOfContainerRegistry = 'Password for container registry is could not be fetched. It is required for setting up AUTH with registry.'; + public static onlyAdminEnabledRegistriesAreAllowed = 'The chosen Container registry doesn\'t have admin user access enabled (allows access to registry with username password). Kindly choose a registry which has admin user access enabled.'; + public static unableToGetSelectedResource = 'Unable to fetch the selected azure resource: %s'; + public static unableToGetAksKubeConfig = 'We are unable to fetch kube config for the AKS cluster: %s, due to permission issues. Kindly choose a different one, over which you have access.'; + public static modifyAndCommitMultipleFiles: string = 'Modify and save your YAML files. %s will commit these files, push the branch \'%s\' to remote \'%s\' and proceed with deployment.'; + public static EmptyTagRowUnavailable = 'There is no space to create a new tag in the resource to store information to '; + public static valueRequired = 'The value cannot be empty'; + public static TemplateNotFound = 'Template not found'; + public static ResourceNotSupported = 'Resource not supported'; + public static minLengthMessage = "The minimum length allowed is %s"; + public static maxLengthMessage = "The maximum length allowed is %s"; + public static minValueMessage = "The value should be greater than or equals to %s"; + public static maxValueMessage = "The value should be less than or equals to %s"; + public static valueShouldBeNumber = "The value %s is not numberic"; + public static regexPatternNotMatchingMessage = "Value should match the following regex pattern: %s"; + public static fetchingInputMessage = "Fetching %s value(s)"; + public static GettingNodeVersion = "Getting Node version to install"; + public static gettingTemplateFileAsset = "Getting template file asset to commit"; + public static gettingWorkflowFile = "Getting workflow file"; + public static templateFileNotFound = "Template file %s not found"; + public static selectGitHubOrganizationName = "Select a GitHub organization"; + public static createGitHubOrganization = "Create a GitHub Organization first to create GitHub repository."; + public static newGitHubRepositoryCreated = "New GitHub repository with the name '%s' has been created."; + public static cannotCreateGitHubRepository = "New GitHub repository could not be created as the repository with the same name already exists"; + public static languageNotSupported = "The language of the repository selected is not supported."; + public static UnableToGetTemplateParameters = "Unable to get parameters for the selected template."; + public static AnalyzingRepo = "Analyzing your repository"; + public static AzureLoginError = "Error in getting Azure login information. Please open 'Command Palette' and select 'Azure: Sign Out' and then invoke 'Deploy to Azure' extension."; + public static AdoDifferentTenantError = " One potential reason can be the AAD tenant of your Azure DevOps repository is different than the one you have been logged-in to VSCode. Open 'Command Palette' and select 'Azure: Sign Out' and then Sign-In to other tenant by invoking 'Deploy to Azure' extension"; + public static ConfiguringPipelineFailed = "Configuring provisioning pipeline failed due to %s"; + public static CreatingSPN = "Creating SPN"; + public static GeneratingWorkflowFiles = "Generating workflow file(s)"; + public static CreatingResourceGroup = "Creating resource group"; + public static ConfiguringGithubWorkflowAndDeployment = 'Configuring github workflow and proceeding to deployment...'; + public static ConfiguringGitubWorkflowFailed = 'Configuring github workflow failed due to %s'; + public static NoAzureSubscriptionFound = 'No Azure Subscription Found.'; + public static GithubRepoRequired = "The selected folder is not a GitHub repository.Please ensure your repository is hosted on GitHub and try again."; + public static GithubWorkflowSetupMultiFile: string = "The workflow files are pushed to your Github repository([commit URL](%s)) and workflow is set up successfully !"; + public static GithubWorkflowSetup: string = "The workflow file is pushed to your Github repository([commit URL](%s)) and workflow is set up successfully !"; + public static GitHubPatInvalid: string = "The GitHub Personal Access token is Invalid. Please retry the command with a valid Personal Access token and ensure that it has permission for required scopes."; + public static UndefinedClientCredentials: string = "Undefined client credentials" +} diff --git a/Source/configure/resources/telemetryKeys.ts b/Source/configure/resources/telemetryKeys.ts new file mode 100644 index 00000000..76f14f6b --- /dev/null +++ b/Source/configure/resources/telemetryKeys.ts @@ -0,0 +1,55 @@ +export class TelemetryKeys { + public static CurrentUserInput: string = 'currentUserInput'; + public static RepoProvider: string = 'repoProvider'; + public static AzureLoginRequired: string = 'azureLoginRequired'; + public static JourneyId: string = 'journeyId'; + public static SourceRepoLocation: string = 'sourceRepoLocation'; + public static NewOrganization: string = 'newOrganization'; + public static ChosenTemplate: string = 'chosenTemplate'; + public static PipelineDiscarded: string = 'pipelineDiscarded'; + public static BrowsePipelineClicked: string = 'browsePipelineClicked'; + public static MultipleWorkspaceFolders: string = 'multipleWorkspaceFolders'; + public static GitFolderExists: string = 'gitFolderExists'; + public static ScmType: string = 'scmType'; + public static BrowsedDeploymentCenter = 'browsedDeploymentCenter'; + public static BrowsedExistingPipeline = 'browsedExistingPipeline'; + public static ClickedConfigurePipeline = 'clickedConfigurePipeline'; + public static UpdatedWebAppMetadata = 'updatedWebAppMetadata'; + public static NewDevOpsRepository = 'newDevOpsRepository'; + public static AzureLoginOption = 'azureLoginOption'; + public static PipelineAlreadyConfigured = 'pipelineAlreadyConfigured'; + public static SubscriptionId = 'subscriptionId'; + public static SelectedCICDProvider = 'selectedCICDProvider'; + public static RepoId = 'repoId'; + public static DisplayWorkflow = 'displayWorkflow'; + public static UnsupportedLanguage = 'unsupportedLanguage'; + public static RepositoryAnalysisFailed = 'repositoryAnalysisFailed'; + public static SelectedTemplate = 'selectedTemplate'; + public static SelectedTemplateType = 'selectedTemplateType'; + public static WorkflowFileName = 'workflowFileName'; + public static GitHubRepoCreated = 'GitHubRepoCreated'; + public static IsErrorWhitelisted = 'IsErrorWhitelisted'; + public static FF_UseGithubForCreatingNewRepository = 'FF_UseGithubForCreatingNewRepository'; + public static FF_UseAzurePipelinesForGithub = 'FF_UseAzurePipelinesForGithub'; + + public static resourceType = 'resourceType'; + public static resourceKind = 'resourceKind'; + public static resourceIdHash = 'resourceIdHash'; + + // Durations + public static ExtensionActivationDuration = 'extensionActivationDuration'; + public static CommandExecutionDuration = 'commandExecutionDuration'; + public static GitHubPatDuration = 'gitHubPatDuration'; + public static RepositoryAnalysisDuration = 'repositoryAnalysisDuration'; + public static TemplateServiceDuration = 'templateServiceDuration'; + + // Count of drop down items + public static OrganizationListCount = 'OrganizationListCount'; + public static ProjectListCount = 'ProjectListCount'; + public static AzureResourceListCount = 'AzureResourceListCount'; + public static WebAppListCount = 'WebAppListCount'; + public static PipelineTempateListCount = 'pipelineTempateListCount'; + public static SubscriptionListCount = 'SubscriptionListCount'; + public static WorkspaceListCount = 'WorkspaceListCount'; + public static pickListCount = 'pickList_%s_Count'; +} diff --git a/Source/configure/resources/tracePoints.ts b/Source/configure/resources/tracePoints.ts new file mode 100644 index 00000000..ed6bcb6f --- /dev/null +++ b/Source/configure/resources/tracePoints.ts @@ -0,0 +1,47 @@ +export class TracePoints { + // Failure trace points + public static AddingContentToPipelineFileFailed = 'AddingContentToPipelineFileFailed'; + public static AzureLoginFailure = 'azureLoginFailure'; + public static AzureServiceConnectionCreateFailure = 'AzureServiceConnectionCreateFailure'; + public static CheckInPipelineFailure = 'checkInPipelineFailure'; + public static CreateAndQueuePipelineFailed = 'createAndBuildPipelineFailed'; + public static CreateNewOrganizationAndProjectFailure = 'CreateNewOrganizationAndProjectFailure'; + public static ExtractAzureResourceFromNodeFailed = 'extractAzureResourceFromNodeFailed'; + public static GetAzureDevOpsDetailsFailed = 'GetAzureDevOpsDetailsFailed'; + public static GetRepositoryDetailsFromRemoteUrlFailed = 'GetRepositoryDetailsFromRemoteUrlFailed'; + public static GetSourceRepositoryDetailsFailed = 'getSourceRepositoryDetailsFailed'; + public static GitHubServiceConnectionError = 'gitHubServiceConnectionError'; + public static PipelineFileCheckInFailed = 'PipelineFileCheckInFailed'; + public static PostDeploymentActionFailed = 'PostDeploymentActionFailed'; + public static CorruptMetadataForVstsRmScmType = 'CorruptMetadataForVstsRmScmType'; + public static GithubWorkflowSettingSecretsFailure = 'GithubWorkflowSettingSecretsFailure'; + public static AssetCreationFailure = 'AssetCreationFailure'; + public static ReadingDockerFileFailed = 'ReadingDockerFileFailed'; + public static CreatingManifestsFailed = 'CreatingManifestsFailed'; + public static ManifestsFolderCreationFailed = 'ManifestsFolderCreationFailed'; + public static RemoteServiceUrlFetchFailed = 'RemoteServiceUrlFetchFailed'; + public static EvaluateDynamicValidation = 'EvalauteDynamicValidation'; + public static InitializeDynamicValidation = 'InitializeDynamicValidation'; + public static SetInputControlValueFromRepoAnalysisResult = 'setInputControlValueFromRepoAnalysisResult'; + public static GetTemplateFile = 'GetTemplateFile'; + public static NoGitHubOrganizationExists = 'NoGitHubOrganizationExists'; + public static TemplateServiceCallFailed = 'TemplateServiceCallFailed'; + public static UnableToGetTemplateFile = 'UnableToGetTemplateFile'; + public static RepoAnalysisFailed = 'RepoAnalysisFailed'; + public static UnableToGetTemplateParameters = 'UnableToGetTemplateParameters'; + public static UnableToGetTemplateConfiguration = 'UnableToGetTemplateConfiguration'; + public static TemplateNotFound = 'TemplateNotFound'; + public static AzurePublishProfileCreationFailure = 'AzurePublishProfileCreationFailure'; + public static GitHubRepositoryCreationFailed = 'GitHubRepositoryCreationFailed'; + public static InitializeGitRepositoryFailed = 'InitializeGitRepositoryFailed'; + public static CommitAndPushPipelineFileFailed = 'CommitAndPushPipelineFileFailed'; + public static LanguageClientActivationFailed = 'LanguageClientActivationFailed'; + public static UnableToCreateProvisioningPipeline = 'UnableToCreateProvisioningPipeline'; + public static UnabletoGetProvisioningPipeline = 'UnabletoGetProvisioningPipeline'; + public static RemotePipelineConfiguringFailed = 'RemotePipelineConfiguringFailed'; + public static SPNCreationFailed = 'SPNCreationFailed'; + public static ConfiguringDraftPipelineFailed = "ConfiguringDraftPipelineFailed"; + public static ResourceGroupCreationFailed = "ResourceGroupCreationFailed"; + public static UndefinedArmAuthToken = "UndefinedArmAuthToken"; + public static GetPathToWorkFlowFileFailed = "GetPathToWorkFlowFileFailed"; +} diff --git a/Source/configure/templateInputHelper/InputControl.ts b/Source/configure/templateInputHelper/InputControl.ts new file mode 100644 index 00000000..1288c851 --- /dev/null +++ b/Source/configure/templateInputHelper/InputControl.ts @@ -0,0 +1,294 @@ +import * as utils from 'util'; +import * as vscode from 'vscode'; +import { ControlProvider } from '../helper/controlProvider'; +import { MustacheHelper } from '../helper/mustacheHelper'; +import { telemetryHelper } from '../helper/telemetryHelper'; +import { DataSource, ExtendedInputDescriptor, InputDataType, InputDynamicValidation, InputMode } from "../model/Contracts"; +import { AzureSession, ControlType, QuickPickItemWithData, StringMap } from '../model/models'; +import { Messages } from '../resources/messages'; +import { TracePoints } from '../resources/tracePoints'; +import { DataSourceExpression } from './utilities/DataSourceExpression'; +import { DataSourceUtility } from './utilities/DataSourceUtility'; +import { StaticValidator } from './utilities/validation/StaticValidator'; + +const Layer: string = 'InputControl'; + +export class InputControl { + public dataSource: DataSourceExpression; + public dataSourceInputControls: Array; + public dataSourceInputs: Map; + public inputDescriptor: ExtendedInputDescriptor; + private value: any; + private controlType: ControlType; + private visible: boolean; + private validationDataSourceToInputsMap: Map; + private azureSession: AzureSession; + private _valuePendingValuation: any; + private controlProvider: ControlProvider; + + constructor(inputDescriptor: ExtendedInputDescriptor, value: any, controlType: ControlType, azureSession: AzureSession) { + this.inputDescriptor = inputDescriptor; + this.value = value; + this.visible = true; + this.controlType = controlType; + this.azureSession = azureSession; + this.dataSourceInputControls = []; + this.dataSourceInputs = new Map(); + this.controlProvider = new ControlProvider(); + } + + public getValue(): any { + return this.value; + } + + public setValue(defaultValue: string) { + this.value = defaultValue; + } + + public getInputDescriptor(): ExtendedInputDescriptor { + return this.inputDescriptor; + } + + public getInputControlId(): string { + return this.inputDescriptor.id; + } + + public getInputGroupId(): string { + return this.inputDescriptor.groupId; + } + + public getInputMode(): InputMode { + return this.inputDescriptor.inputMode; + } + + public getVisibleRule(): string { + return this.inputDescriptor.visibleRule; + } + + public isVisible(): boolean { + return this.visible; + } + + public setVisibility(value: boolean): void { + this.visible = value; + } + + public updateInputDescriptorProperty(key: string, value: any): void { + this.inputDescriptor[key] = value; + } + + public getInputDataType(): InputDataType { + return this.inputDescriptor.type; + } + + public updateControlType(controlType: ControlType): void { + this.controlType = controlType; + } + + public async setInputControlValue(): Promise { + if (!this.isVisible()) { + return; + } + if (!!this.dataSource) { + var dependentInputs = this._getDataSourceInputs(); + if (this.controlType === ControlType.None || this.controlType === ControlType.InputBox) { + this.value = await this.dataSource.evaluateDataSources(dependentInputs, this.azureSession); + let errorMessage = await this.triggerControlValueValidations(this.value); + if (errorMessage) { + vscode.window.showErrorMessage(errorMessage); + this.value = await this.controlProvider.showInputBox(this.getInputControlId(), { + value: this.value, + placeHolder: this.inputDescriptor.name, + validateInput: (value) => this.triggerControlValueValidations(value) + }); + } + } + else if (this.controlType === ControlType.QuickPick) { + let listItems: Array = await vscode.window.withProgress( + { location: vscode.ProgressLocation.Notification, title: utils.format(Messages.fetchingInputMessage, this.inputDescriptor.name) }, + () => this.dataSource.evaluateDataSources(dependentInputs, this.azureSession)); + let selectedItem: QuickPickItemWithData; + while (1) { + selectedItem = await this.controlProvider.showQuickPick(this.getInputControlId(), listItems, { + placeHolder: this.inputDescriptor.name + }); + let errorMessage = await this.triggerControlValueValidations(selectedItem.data); + if (!errorMessage) { + break; + } + vscode.window.showErrorMessage(errorMessage); + } + this.value = selectedItem.data; + } + } + else { + if (this.controlType === ControlType.QuickPick) { + var listItems: Array<{ label: string, data: any }> = []; + if (!!this.inputDescriptor.possibleValues && this.inputDescriptor.possibleValues.length > 0) { + this.inputDescriptor.possibleValues.forEach((item) => { + listItems.push({ label: item.displayValue, data: item.value }); + }); + } + else if (this.inputDescriptor.inputMode === InputMode.RadioButtons) { + listItems.push({ label: "Yes", data: "true" }); + listItems.push({ label: "No", data: "false" }); + } + this.value = (await this.controlProvider.showQuickPick(this.getInputControlId(), listItems, { placeHolder: this.inputDescriptor.name })).data; + } + else if (this.controlType === ControlType.InputBox) { + this.value = await this.controlProvider.showInputBox(this.getInputControlId(), { + value: this.value, + placeHolder: this.inputDescriptor.name, + validateInput: (value) => this.triggerControlValueValidations(value) + }); + } + } + } + + public getPropertyValue(propertyName: string, inputs?: StringMap): string { + return InputControl.getInputDescriptorProperty(this.inputDescriptor, propertyName, inputs); + } + + public static getInputDescriptorProperty(input: ExtendedInputDescriptor, propertyName: string, inputs?: StringMap): any { + var properties = input.properties; + var value: string; + + if (!!properties) { + value = properties[propertyName]; + if (!!value && !!inputs && typeof value === "string") { + return MustacheHelper.render(value, { inputs: inputs }) + } else if (!!value && !!inputs) { + return MustacheHelper.renderObject(value, { inputs: inputs }); + } + } + return value; + + } + + public setValidationDataSources(dataSourceToInputsMap: Map) { + this.validationDataSourceToInputsMap = dataSourceToInputsMap; + } + + public async triggerControlValueValidations(value: string): Promise { + this._valuePendingValuation = value; + let errorMessage = this.getStaticValidationsResult(value); + if (errorMessage) { + return Promise.resolve(errorMessage); + } + + let validationPromises: Promise[] = []; + this.inputDescriptor.dynamicValidations.forEach((validation: InputDynamicValidation) => { + validationPromises.push(this.evaluateDynamicValidation(validation, value)); + }); + + return await Promise.all(validationPromises) + .then((results: string[]) => { + results.forEach((result) => { + if (result) { + errorMessage = errorMessage.concat(result); + } + }); + return errorMessage; + }) + .catch((error) => { + return ""; + }); + } + + private _getDataSourceInputs(): { [key: string]: any } { + var inputs: { [key: string]: any } = {}; + for (var dataSourceInput of this.dataSourceInputControls) { + inputs[dataSourceInput.getInputControlId()] = dataSourceInput.getValue(); + } + return inputs; + } + + private getStaticValidationsResult(value: string): string { + let validationResult = ""; + if (this.inputDescriptor.isRequired) { + let result = StaticValidator.validateRequired(value); + if (result) { + validationResult = validationResult.concat(result); + } + } + if (this.inputDescriptor.staticValidation) { + if (!!this.inputDescriptor.staticValidation.minLength || !!this.inputDescriptor.staticValidation.maxLength) { + let result = StaticValidator.validateLength(value, this.inputDescriptor.staticValidation.minLength, this.inputDescriptor.staticValidation.maxLength); + if (result) { + validationResult = validationResult.concat(result); + } + } + if (!!this.inputDescriptor.staticValidation.pattern) { + var regexPattern = this.inputDescriptor.staticValidation.pattern; + + let result = StaticValidator.validateRegex(value, regexPattern, this.inputDescriptor.staticValidation.regexFlags || ""); + if (result) { + validationResult = this.inputDescriptor.staticValidation.errorMessage ? validationResult : validationResult.concat(result); + } + } + if (this.inputDescriptor.type === InputDataType.Int && (!!this.inputDescriptor.staticValidation.minValue || !!this.inputDescriptor.staticValidation.maxValue)) { + let result = StaticValidator.validateNumberValue(value, this.inputDescriptor.staticValidation.minValue, this.inputDescriptor.staticValidation.maxValue); + if (result) { + validationResult = validationResult.concat(result); + } + } + } + return validationResult; + } + + private async evaluateDynamicValidation(validation: InputDynamicValidation, value: string): Promise { + var requiredDataSource: DataSource = null; + this.validationDataSourceToInputsMap.forEach((inputControlArray: InputControl[], dataSource: DataSource) => { + if (dataSource.id === validation.dataSourceId) { + requiredDataSource = dataSource; + } + }); + + var requiredInputsValueMap: StringMap = {}; + var allRequiredInputValuesAvailable: boolean = true; + this.validationDataSourceToInputsMap.get(requiredDataSource).forEach((descriptor) => { + if (!descriptor.getValue()) { + allRequiredInputValuesAvailable = false; + return; + } + + requiredInputsValueMap[descriptor.getInputControlId()] = descriptor.getValue(); + }); + // setting the value for the current input with the current value passed as the value might have changed since this validation call was invoked, + requiredInputsValueMap[this.inputDescriptor.id] = value; + + if (allRequiredInputValuesAvailable) { + return DataSourceUtility.evaluateDataSource(requiredDataSource, requiredInputsValueMap, this.azureSession) + .then((result) => { + if (value === this._valuePendingValuation) { + if (!result) { + return validation.errorMessage; + } + + var resultObject = JSON.parse(result); + + if (resultObject && resultObject.value === "true") { + return null; + } + else { + return result && resultObject.message || validation.errorMessage; + } + } + else { + // The value of control has been changed, therefore resolve it as the validation of current value will override + return null; + } + }) + .catch((error) => { + if (!(requiredDataSource.resultTemplate && requiredDataSource.resultTemplate.toLowerCase() === "false")) { + telemetryHelper.logError(Layer, TracePoints.EvaluateDynamicValidation, error); + } + return null; + }); + } + else { + // Resolve as valid here as other validation for required input will be shown + return null; + } + } +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/InputControlProvider.ts b/Source/configure/templateInputHelper/InputControlProvider.ts new file mode 100644 index 00000000..fb876d02 --- /dev/null +++ b/Source/configure/templateInputHelper/InputControlProvider.ts @@ -0,0 +1,268 @@ +import { ApplicationSettings } from "azureintegration-repoanalysis-client-internal"; +import { MustacheHelper } from "../helper/mustacheHelper"; +import { telemetryHelper } from "../helper/telemetryHelper"; +import { DataSource, ExtendedInputDescriptor, ExtendedPipelineTemplate, InputDataType, InputDynamicValidation, InputMode } from "../model/Contracts"; +import { AzureSession, ControlType, IPredicate, StringMap } from '../model/models'; +import * as constants from '../resources/constants'; +import { TracePoints } from "../resources/tracePoints"; +import { InputControl } from "./InputControl"; +import { RepoAnalysisSettingInputProvider } from "./RepoAnalysisSettingInputProvider"; +import { DataSourceExpression } from "./utilities/DataSourceExpression"; +import { DataSourceUtility } from "./utilities/DataSourceUtility"; +import { InputControlUtility } from "./utilities/InputControlUtility"; +import { VisibilityHelper } from "./utilities/VisibilityHelper"; + +const Layer: string = "InputControlProvider"; + +export class InputControlProvider { + private _pipelineTemplate: ExtendedPipelineTemplate; + private _inputControlsMap: Map; + private azureSession: AzureSession; + private repoAnalysisSettingInputProvider: RepoAnalysisSettingInputProvider; + private _context: StringMap; + + constructor(azureSession: AzureSession, pipelineTemplate: ExtendedPipelineTemplate, context: StringMap) { + this._pipelineTemplate = pipelineTemplate; + this._inputControlsMap = new Map(); + this.azureSession = azureSession; + this.repoAnalysisSettingInputProvider = new RepoAnalysisSettingInputProvider(context['repoAnalysisSettings'] as ApplicationSettings[]); + this._context = context; + this._createControls(); + } + + public async getAllPipelineTemplateInputs(): Promise<{ [key: string]: any }> { + let parameters: { [key: string]: any } = {}; + for (let inputControl of this._inputControlsMap.values()) { + if (!!inputControl.getPropertyValue(constants.clientPropertyKey)) { + this.overrideParameters(inputControl); + } + this._setInputControlDataSourceInputs(inputControl); + this._initializeDynamicValidations(inputControl); + if (this.repoAnalysisSettingInputProvider.inputFromRepoAnalysisSetting(inputControl)) { + await this.repoAnalysisSettingInputProvider.setInputControlValueFromRepoAnalysisResult(inputControl); + } + else { + this._setInputControlVisibility(inputControl); + this._setupInputControlDefaultValue(inputControl); + await inputControl.setInputControlValue(); + } + parameters[inputControl.getInputControlId()] = inputControl.getValue(); + } + return parameters; + } + + private overrideParameters(inputControl: InputControl): void { + let properties = inputControl.getPropertyValue(constants.clientPropertyKey); + let arrayofProperties = Object.keys(properties); + + arrayofProperties.forEach(element => { + let key = element.split(".", 2)[1]; + let newValue: any; + if (properties[element] !== null) { + let dependentInputControlArray = this._getInputDependencyArray(inputControl, [properties[element]], false); + let dependentClientInputMap = this._getClientDependencyMap(inputControl, [properties[element]]); + newValue = this._computeMustacheValue(properties[element], dependentInputControlArray, dependentClientInputMap); + } + inputControl.updateInputDescriptorProperty(key, newValue); + if (key === constants.inputModeProperty) { + let newInputMode: InputMode = (typeof newValue === "string") ? InputMode[newValue] : newValue; + let updatedControlType = InputControlUtility.getInputControlType(newInputMode); + inputControl.updateControlType(updatedControlType); + } + }); + } + + private _createControls(): void { + for (let input of this._pipelineTemplate.parameters.inputs) { + let inputControl: InputControl = null; + let inputControlValue = this._getInputControlValue(input); + if (input.type !== InputDataType.Authorization) { + let controlType: ControlType = InputControlUtility.getInputControlType(input.inputMode); + inputControl = new InputControl(input, inputControlValue, controlType, this.azureSession); + if (inputControl) { + this._inputControlsMap.set(input.id, inputControl); + } + } + } + } + + private _setInputControlDataSourceInputs(inputControl: InputControl): void { + let inputDes = inputControl.getInputDescriptor(); + if (!!inputDes.dataSourceId) { + var inputControl = this._inputControlsMap.get(inputDes.id); + inputControl.dataSource = DataSourceExpression.parse(inputDes.dataSourceId, this._pipelineTemplate.parameters.dataSources); + + if (inputControl.dataSource) { + var dependentInputControlArray = this._getInputDependencyArray(inputControl, inputControl.dataSource.getInputDependencyArray()); + if (dependentInputControlArray) { + inputControl.dataSourceInputControls.push(...dependentInputControlArray); + } + } else { + throw new Error(`Data source ${inputDes.dataSourceId} specified for input ${inputDes.id} is not present in pipeline template ${this._pipelineTemplate.id}`); + } + } + } + + private _initializeDynamicValidations(inputControl: InputControl): void { + var inputDes = inputControl.getInputDescriptor(); + + if (!!inputControl && !!inputDes.dynamicValidations && inputDes.dynamicValidations.length > 0) { + var dataSourceToInputsMap = new Map(); + inputDes.dynamicValidations.forEach((validation: InputDynamicValidation) => { + var validationDataSource = DataSourceUtility.getDataSourceById(this._pipelineTemplate.parameters.dataSources, validation.dataSourceId); + if (validationDataSource) { + dataSourceToInputsMap.set(validationDataSource, []); + + let dynamicValidationRequiredInputIds = DataSourceUtility.getDependentInputIdList(validationDataSource.endpointUrlStem); + dynamicValidationRequiredInputIds = dynamicValidationRequiredInputIds.concat(DataSourceUtility.getDependentInputIdList(validationDataSource.requestBody)); + dynamicValidationRequiredInputIds = dynamicValidationRequiredInputIds.concat(DataSourceUtility.getDependentInputIdList(validationDataSource.resultTemplate)); + dynamicValidationRequiredInputIds = dynamicValidationRequiredInputIds.concat(DataSourceUtility.getDependentInputIdList(validationDataSource.resultSelector)); + + dynamicValidationRequiredInputIds = Array.from(new Set(dynamicValidationRequiredInputIds)); + dynamicValidationRequiredInputIds.forEach((dynamicValidationRequiredInputId) => { + var dependentInput = this._inputControlsMap.get(dynamicValidationRequiredInputId); + if (dependentInput) { + dataSourceToInputsMap.get(validationDataSource).push(dependentInput); + } else { + let error: Error = new Error(`Dependent input ${dynamicValidationRequiredInputId} specified for input ${inputDes.id} is not present in pipeline template ${this._pipelineTemplate.id}`); + telemetryHelper.logError(Layer, TracePoints.InitializeDynamicValidation, error); + } + }); + } else { + let error = new Error(`validation data source ${validation.dataSourceId} specified for input ${inputDes.id} is not present in pipeline template ${this._pipelineTemplate.id}`); + telemetryHelper.logError(Layer, TracePoints.InitializeDynamicValidation, error); + } + }); + inputControl.setValidationDataSources(dataSourceToInputsMap); + } + } + + private _setupInputControlDefaultValue(inputControl: InputControl): void { + var inputDes = inputControl.getInputDescriptor(); + var defaultValue = inputDes.defaultValue; + if (!defaultValue) { + return; + } + if (InputControlUtility.doesExpressionContainsDependency(defaultValue)) { + var dependentInputControlArray = this._getInputDependencyArray(inputControl, [defaultValue], false); + var dependentClientInputMap = this._getClientDependencyMap(inputControl, [defaultValue]); + defaultValue = this._computeMustacheValue(defaultValue, dependentInputControlArray, dependentClientInputMap); + } + if (defaultValue !== inputControl.getValue()) { + inputControl.setValue(defaultValue); + } + } + + private _computeMustacheValue(mustacheExpression: string, dependentInputControlArray: InputControl[], dependentClientInputMap: StringMap): any { + var dependentInputValues = this._getInputParameterValueIfAllSet(dependentInputControlArray); + if (dependentInputControlArray && dependentInputControlArray.length > 0 && !dependentInputValues) { + return ""; + } else { + return MustacheHelper.renderObject(mustacheExpression, { inputs: dependentInputValues, client: dependentClientInputMap }); + } + } + + private _getInputControlValue(inputDes: ExtendedInputDescriptor): string { + if (!!this._context && !!this._context + [inputDes.id]) { + return this._context[inputDes.id]; + } else { + return !inputDes.defaultValue + || InputControlUtility.doesExpressionContainsDependency(inputDes.defaultValue) + ? "" : inputDes.defaultValue; + } + } + + private _getClientDependencyMap(inputControl: InputControl, dependencyExpressionArray: string[]): StringMap { + var dependentClientControlMap: StringMap = {}; + var dependentClientInputs: string[] = []; + for (var dependencyExpression of dependencyExpressionArray) { + if (dependencyExpression) { + dependentClientInputs = dependentClientInputs.concat(InputControlUtility.getDependentClientIdList(dependencyExpression)); + } + } + + if (dependentClientInputs.length === 0) { + return null; + } + + var uniqueDependentClientInputs = dependentClientInputs.filter(function (item, pos) { + return dependentClientInputs.indexOf(item) === pos; + }); + + for (var clientInput of uniqueDependentClientInputs) { + var dependentInputControl = this._context[clientInput]; + if (dependentInputControl !== undefined) { + dependentClientControlMap[clientInput] = dependentInputControl; + } else { + throw new Error(`Dependent client input ${clientInput} specified is not present in client context.`); + } + } + return dependentClientControlMap; + } + + private _getInputDependencyArray(inputControl: InputControl, dependencyExpressionArray: string[], allowSelfDependency: boolean = true): InputControl[] { + var dependentInputControlArray: InputControl[] = []; + var dependentInputIds: string[] = []; + for (var dependencyExpression of dependencyExpressionArray) { + if (dependencyExpression) { + dependentInputIds = dependentInputIds.concat(InputControlUtility.getDependentInputIdList(dependencyExpression)); + } + } + + if (dependentInputIds.length === 0) { + return null; + } + + if (!allowSelfDependency && dependentInputIds.indexOf(inputControl.getInputControlId()) >= 0) { + throw new Error(`Input ${inputControl.getInputControlId()} has dependency on its own in pipeline template ${this._pipelineTemplate.id}.`); + } + + var uniqueDependentInputIds = dependentInputIds.filter(function (item, pos) { + return dependentInputIds.indexOf(item) === pos; + }); + + for (var inputId of uniqueDependentInputIds) { + var dependentInputControl = this._inputControlsMap.get(inputId); + if (dependentInputControl) { + dependentInputControlArray.push(dependentInputControl); + } else { + throw new Error(`Dependent input ${inputId} specified for input ${inputControl.getInputControlId()} is not present in pipeline template ${this._pipelineTemplate.id}`); + } + } + return dependentInputControlArray; + } + + private _getInputParameterValueIfAllSet(dependentInputControlArray: InputControl[], useDisplayValue: boolean = false): StringMap { + var dependentInputValuesMap: StringMap = {}; + if (!dependentInputControlArray) { + return null; + } + + for (var dependentInputControl of dependentInputControlArray) { + if (!dependentInputControl.getValue()) { + //Value of the parameter is not available, so rest of the input values will be useless. + throw new Error("Unable to get the input value, inputs order may not be correct"); + } + dependentInputValuesMap[dependentInputControl.getInputControlId()] = dependentInputControl.getValue(); + } + return dependentInputValuesMap; + } + + private _setInputControlVisibility(inputControl: InputControl): void { + var visibilityRule = VisibilityHelper.parseVisibleRule(inputControl.getVisibleRule()); + if (visibilityRule !== null && visibilityRule.predicateRules !== null && visibilityRule.predicateRules.length >= 0) { + var requiredInputControls: InputControl[] = []; + + visibilityRule.predicateRules.forEach((predicateRule: IPredicate) => { + try { + requiredInputControls = requiredInputControls.concat([this._inputControlsMap.get(predicateRule.inputName)]); + } + catch (exception) { + throw new Error("Input defined in visiblity rule doesn't exist"); + } + }); + inputControl.setVisibility(VisibilityHelper.evaluateVisibility(visibilityRule, requiredInputControls)); + } + } +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/RepoAnalysisSettingInputProvider.ts b/Source/configure/templateInputHelper/RepoAnalysisSettingInputProvider.ts new file mode 100644 index 00000000..ceafd520 --- /dev/null +++ b/Source/configure/templateInputHelper/RepoAnalysisSettingInputProvider.ts @@ -0,0 +1,109 @@ +import { ApplicationSettings } from "azureintegration-repoanalysis-client-internal"; +import { ControlProvider } from "../helper/controlProvider"; +import { telemetryHelper } from "../helper/telemetryHelper"; +import { InputDataType } from "../model/Contracts"; +import { TracePoints } from "../resources/tracePoints"; +import { InputControl } from "./InputControl"; + +const Layer: string = "RepoAnalysisSettingInputProvider"; + +export class RepoAnalysisSettingInputProvider { + private readonly repoAnalysisSettingKey: string = "repoAnalysisSettingKey"; + private repoAnalysisSettings: ApplicationSettings[]; + private selectedRepoAnalysisSettingIndex: number; + + constructor(repoAnalysisSettings: ApplicationSettings[]) { + this.repoAnalysisSettings = repoAnalysisSettings || []; + this.selectedRepoAnalysisSettingIndex = repoAnalysisSettings.length > 1 ? -1 : 0; + } + + public inputFromRepoAnalysisSetting(inputControl: InputControl): boolean { + const repoAnalysisSettingKey = inputControl.getPropertyValue(this.repoAnalysisSettingKey); + return !!repoAnalysisSettingKey && this.repoAnalysisSettings.length > 0; + } + + public async setInputControlValueFromRepoAnalysisResult(inputControl: InputControl): Promise { + const repoAnalysisSettingKey = inputControl.getPropertyValue(this.repoAnalysisSettingKey); + if (this.selectedRepoAnalysisSettingIndex !== -1) { + let value = this.repoAnalysisSettings[this.selectedRepoAnalysisSettingIndex].settings[repoAnalysisSettingKey]; + if (!value || (Array.isArray(value) && value.length === 0)) { + await this.setValueIfNotPresentInRepoAnalysis(inputControl, repoAnalysisSettingKey); + } else { + if (Array.isArray(value)) { + let selectedValue = value[0]; + if (value.length > 1) { + const possibleValues = value.map((element) => ({ label: element, data: element })); + + selectedValue = (await new ControlProvider().showQuickPick(repoAnalysisSettingKey, possibleValues, { + placeHolder: inputControl.getInputDescriptor().name, + })).data; + } + value = selectedValue; + } + inputControl.setValue(value); + } + } else { + const settingIndexMap: Map = new Map(); + this.repoAnalysisSettings.forEach((analysisSetting, index: number) => { + if (!analysisSetting.settings[repoAnalysisSettingKey]) { + return; + } + let keyValue = analysisSetting.settings[repoAnalysisSettingKey]; + if (Array.isArray(keyValue)) { + if (keyValue.length === 0) { + return; + } + keyValue = keyValue.toString(); + } + + if (settingIndexMap.has(keyValue)) { + settingIndexMap.get(keyValue).push(index); + } else { + settingIndexMap.set(keyValue, [index]); + } + }); + if (settingIndexMap.size === 0) { + await this.setValueIfNotPresentInRepoAnalysis(inputControl, repoAnalysisSettingKey); + } else { + let possibleValues = Array.from(settingIndexMap.keys()).map((value) => ({ label: value, data: value })); + let selectedValue: { label: string, data: any }; + + if (possibleValues.length === 1) { + selectedValue = possibleValues[0]; + } + else { + // HACK: Temporarily default to first value without asking user. Remove this HACK later and uncomment the next line + selectedValue = possibleValues[0]; + // selectedValue = await new ControlProvider().showQuickPick(repoAnalysisSettingKey, possibleValues, { placeHolder: inputControl.getInputDescriptor().name }); + } + if (settingIndexMap.get(selectedValue.data).length === 1) { + this.selectedRepoAnalysisSettingIndex = settingIndexMap.get(selectedValue.data)[0]; + } + + const repoAnalysisValue = this.repoAnalysisSettings[settingIndexMap.get(selectedValue.data)[0]].settings[repoAnalysisSettingKey]; + if (inputControl.getInputDataType() === InputDataType.String && Array.isArray(repoAnalysisValue)) { + possibleValues = repoAnalysisValue.map((value) => ({ label: value, data: value })); + if (possibleValues.length === 1) { + selectedValue = possibleValues[0]; + } else { + selectedValue = await new ControlProvider().showQuickPick(repoAnalysisSettingKey, possibleValues, { placeHolder: inputControl.getInputDescriptor().name }); + } + } + inputControl.setValue(selectedValue.data); + } + } + } + + private async setValueIfNotPresentInRepoAnalysis(inputControl: InputControl, repoAnalysisSettingKey: string): Promise { + const error = new Error(`RepostioryAnalysisSetting doesn't contain ${repoAnalysisSettingKey} for input ${inputControl.getInputControlId()}`); + telemetryHelper.logError(Layer, TracePoints.SetInputControlValueFromRepoAnalysisResult, error); + let value = inputControl.getInputDescriptor().defaultValue; + if (!value) { + value = await new ControlProvider().showInputBox(repoAnalysisSettingKey, { + placeHolder: inputControl.getInputDescriptor().name, + validateInput: (v) => inputControl.triggerControlValueValidations(v) + }); + } + return inputControl.setValue(value); + } +} diff --git a/Source/configure/templateInputHelper/utilities/DataSourceExpression.ts b/Source/configure/templateInputHelper/utilities/DataSourceExpression.ts new file mode 100644 index 00000000..cba7a370 --- /dev/null +++ b/Source/configure/templateInputHelper/utilities/DataSourceExpression.ts @@ -0,0 +1,174 @@ +import { DataSource } from "../../model/Contracts"; +import { AzureSession } from "../../model/models"; +import { DataSourceUtility } from "./DataSourceUtility"; + +export class DataSourceExpression { + public operator: Operator; + public leftChild?: DataSourceExpression; + public rightChild?: DataSourceExpression; + public value?: string; + + private _dataSources: DataSource[] = []; + + // Parses a dataSourceId string into an expression based on operators present in the id. + // NOTE: Currently only a simple expression of type "expr1 OPERATOR expr2" is supported. + public static parse(dataSourceId: string, dataSources: DataSource[]): DataSourceExpression { + this._validateDataSourceId(dataSourceId); + var id: string = dataSourceId.trim(); + var parts: any[] = DataSourceExpression._splitByOperator(id); + if (parts.length > 1) { + var leftChild: DataSourceExpression = DataSourceExpression.parse(parts[0].trim(), dataSources); + var rightChild: DataSourceExpression = DataSourceExpression.parse(parts[1].trim(), dataSources); + if (!leftChild && !rightChild) { + return null; + } + + return new DataSourceExpression( + parts[2], + leftChild, + rightChild, + null, + dataSources); + } + + if (!DataSourceUtility.getDataSourceById(dataSources, parts[0])) { + return null; + } + + return new DataSourceExpression( + Operator.UNDEFINED, + null, + null, + parts[0], + dataSources); + } + + public getInputDependencyArray(): string[] { + if (this.operator === Operator.UNDEFINED) { + var dataSource = DataSourceUtility.getDataSourceById(this._dataSources, this.value); + return [dataSource.endpointUrlStem, dataSource.requestBody, dataSource.resultSelector, dataSource.resultTemplate]; + } + + var leftDependencyArray: string[] = []; + var rightDependencyArray: string[] = []; + if (this.leftChild) { + leftDependencyArray = this.leftChild.getInputDependencyArray(); + } + + if (this.rightChild) { + rightDependencyArray = this.rightChild.getInputDependencyArray(); + } + + return leftDependencyArray.concat(rightDependencyArray); + } + + public async evaluateDataSources(inputs: { [key: string]: any }, azureSession: AzureSession): Promise { + var dataPromises: Promise[] = []; + + if (this.operator === Operator.UNDEFINED) { + var dataSource = DataSourceUtility.getDataSourceById(this._dataSources, this.value); + return DataSourceUtility.evaluateDataSource(dataSource, inputs, azureSession); + } + + if (this.leftChild) { + dataPromises.push(this.leftChild.evaluateDataSources(inputs, azureSession)); + } + + if (this.rightChild) { + dataPromises.push(this.rightChild.evaluateDataSources(inputs, azureSession)); + } + + return Promise.all(dataPromises).then((result: any[]) => { + // If one of the data source evaluates to an array while other evaluates to a string, then take the array + if (Array.isArray(result[0]) && !Array.isArray(result[1])) { + return result[0]; + } + + if (Array.isArray(result[1]) && !Array.isArray(result[0])) { + return result[1]; + } + + // If both data sources evaluate to string, then take that is not empty + if (!Array.isArray(result[0]) && !Array.isArray(result[1])) { + return result[0] || result[1]; + } + + if (Array.isArray(result[0]) && Array.isArray(result[1])) { + return DataSourceExpression._applyOperator(this.operator, result[0], result[1]); + } + }); + } + + private constructor(operator: Operator, leftChild: DataSourceExpression, rightChild: DataSourceExpression, value: string, dataSources: DataSource[]) { + this.operator = operator; + this.leftChild = leftChild; + this.rightChild = rightChild; + this.value = value; + this._dataSources = dataSources; + } + + private static _applyOperator( + operator: Operator, array1: Array<{ text: string, value: any, group?: string }>, + array2: Array<{ text: string, value: any, group?: string }>): Array<{ text: string, value: any, group?: string }> { + + switch (operator) { + case Operator.INTERSECT: + return array1.filter(item1 => { + return array2.some((item2) => { + return item2.value.toLowerCase() === item1.value.toLowerCase() + && ((!item2.group && !item1.group) || item2.group.toLowerCase() === item1.group.toLowerCase()); + }); + }); + default: + var error: string = `Data sources do not support operator ${operator}. Supported operators are: INTERSECT`; + //PipelineTemplateErrorLogger.logError(error); + throw error; + } + } + + private static _splitByOperator(id: string): any[] { + var operators: string[] = Object.keys(Operator).filter(k => typeof Operator[k as any] === "number" && k !== "UNDEFINED"); + var parts: string[] = []; + operators.forEach((operator: string) => { + var delimiter: string = " " + operator + " "; + if (id.indexOf(delimiter) !== -1) { + parts = id.split(delimiter); + parts[2] = Operator[operator as any]; + return; + } + }); + + if (parts.length === 0) { + return [id]; + } else { + return parts; + } + } + + private static _validateDataSourceId(dataSourceId: string): void { + var error: string; + if (!dataSourceId) { + error = "DataSourceId should not be null or empty"; + } + + var parts: string[] = dataSourceId.trim().split(" "); + if (parts.length > 1 && parts.length !== 3) { + error = "Invalid DataSourceId. It does not support multiple operators. It should be of format 'expression' or 'expression1 [OPERATOR] expression2'"; + } + + if (dataSourceId.startsWith('(') || dataSourceId.endsWith(')')) { + error = "Invalid DataSourceId. It should not start or end with braces"; + } + + if (error) { + //PipelineTemplateErrorLogger.logError(error); + throw new Error(error); + } + } +} + +export enum Operator { + UNDEFINED = 0, + INTERSECT = 1, + OR = 2 +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/utilities/DataSourceUtility.ts b/Source/configure/templateInputHelper/utilities/DataSourceUtility.ts new file mode 100644 index 00000000..efaeea2f --- /dev/null +++ b/Source/configure/templateInputHelper/utilities/DataSourceUtility.ts @@ -0,0 +1,87 @@ +import { JSONPath } from 'jsonpath-plus'; +import { isNullOrUndefined } from 'util'; +import { ArmRestClient } from "../../clients/azure/armRestClient"; +import { MustacheHelper } from "../../helper/mustacheHelper"; +import { DataSource } from "../../model/Contracts"; +import { AzureSession, QuickPickItemWithData, StringMap } from "../../model/models"; + +export class DataSourceUtility { + + public static getDependentInputIdList(dataSourceEndpointUrl: string): string[] { + var inputIds: string[] = []; + if (!!dataSourceEndpointUrl) { + var dependentInputs = dataSourceEndpointUrl.match(/\{\{\{inputs.\w+\}\}\}/g); + if (!!dependentInputs) { + dependentInputs.forEach((value) => { + var startIndex = value.indexOf("{{{inputs.") + "{{{inputs.".length; + var endIndex = value.indexOf("}}}"); + var inputId = value.substring(startIndex, endIndex); + inputIds.push(inputId); + }); + } + } + + return inputIds; + } + + public static getDataSourceById(dataSources: Array, dataSourceId: string) { + for (var dataSource of dataSources) { + if (dataSource.id === dataSourceId) { + return dataSource; + } + } + return null; + } + + public static async evaluateDataSource(dataSource: DataSource, inputs: StringMap, azureSession: AzureSession): Promise { + + var view = { inputs: inputs }; + var armUri = MustacheHelper.render(dataSource.endpointUrlStem, view); + var httpMethod = dataSource.httpMethod || "GET"; + var requestBody = !!dataSource.requestBody ? MustacheHelper.render(dataSource.requestBody, view) : null; + let amrClient = new ArmRestClient(azureSession); + return amrClient.fetchArmData(armUri, httpMethod, JSON.parse(requestBody)) + .then((response: any) => { + return this.evaluateDataSourceResponse(dataSource, response, view); + }); + } + + private static evaluateDataSourceResponse(dataSource: DataSource, response: any, view: { inputs: StringMap }): any { + if (!!dataSource.resultSelector) { + var resultSelector = MustacheHelper.render(dataSource.resultSelector, view); + response = JSONPath({ json: response, path: resultSelector, wrap: false, flatten: true }); + if (response === "" || response === isNullOrUndefined) { + return null; + } + } + + if (Array.isArray(response)) { + var quickPickItems: Array = []; + + if (!!dataSource.resultTemplate) { + // Apply resultTemplate to each element in the Array + for (let item of response) { + if (typeof item === 'string' || item instanceof String) { + item = { "result": item }; + } + var resultObj = JSON.parse(MustacheHelper.render(dataSource.resultTemplate, { ...view, ...item })); + quickPickItems.push({ label: resultObj.DisplayValue, data: resultObj.Value }); + } + } + else { + for (let item of response) { + quickPickItems.push({ label: item, data: item }); + } + } + + return quickPickItems; + } + else { + if (!!dataSource.resultTemplate) { + response = MustacheHelper.render(dataSource.resultTemplate, { ...view, ...response }); + } + + return response; + } + } +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/utilities/InputControlUtility.ts b/Source/configure/templateInputHelper/utilities/InputControlUtility.ts new file mode 100644 index 00000000..f02c1e31 --- /dev/null +++ b/Source/configure/templateInputHelper/utilities/InputControlUtility.ts @@ -0,0 +1,81 @@ +import { InputMode } from "../../model/Contracts"; +import { ControlType } from "../../model/models"; + +export class InputControlUtility { + + public static doesExpressionContainsDependency(expression: string): boolean { + if (!expression) { + return false; + } + + return /{{{inputs\.\w+}}}|{{{system\.\w+}}}|{{{client\.\w+}}}/g.test(expression); + } + + public static getDependentInputIdList(expression: string): string[] { + return this._fetchIdsInExpressionByType(expression, DependencyType.Input); + } + + public static getDependentClientIdList(expression: string): string[] { + return this._fetchIdsInExpressionByType(expression, DependencyType.Client); + } + + public static getDependentSystemIdList(expression: string): string[] { + return this._fetchIdsInExpressionByType(expression, DependencyType.System); + } + + public static getInputControlType(inputMode: InputMode): ControlType { + switch (inputMode) { + case InputMode.None: + case InputMode.AzureSubscription: + return ControlType.None; + case InputMode.TextBox: + case InputMode.PasswordBox: + return ControlType.InputBox; + case InputMode.Combo: + case InputMode.CheckBox: + case InputMode.RadioButtons: + return ControlType.QuickPick; + default: + return null; + } + } + + private static _fetchIdsInExpressionByType(expression: string, dependencyType: DependencyType): string[] { + if (!expression) { + return []; + } + + var regex; + switch (dependencyType) { + case DependencyType.Input: + regex = /{{{inputs\.(\w+)}}}/g; + break; + + case DependencyType.System: + regex = /{{{system\.(\w+)}}}/g; + break; + + case DependencyType.Client: + regex = /{{{client\.(\w+)}}}/g; + break; + } + + var dependentIds: string[] = []; + var uniqueDependentIdMap = new Map(); + var resultArray; + while ((resultArray = regex.exec(expression)) !== null) { + uniqueDependentIdMap.set(resultArray[1], null); + } + + uniqueDependentIdMap.forEach((value, key) => { + dependentIds.push(key); + }); + return dependentIds; + } +} + +enum DependencyType { + Input, + System, + Client +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/utilities/VisibilityHelper.ts b/Source/configure/templateInputHelper/utilities/VisibilityHelper.ts new file mode 100644 index 00000000..d403a8bd --- /dev/null +++ b/Source/configure/templateInputHelper/utilities/VisibilityHelper.ts @@ -0,0 +1,153 @@ +import { IPredicate, IVisibilityRule } from "../../model/models"; +import { InputControl } from "../InputControl"; + +const Operator_AND: string = "&&"; +const Operator_OR: string = "||"; + +export class VisibilityHelper { + public static parseVisibleRule(visibleRule: string): IVisibilityRule { + let rule: IVisibilityRule = null; + if (visibleRule) { + if (visibleRule.indexOf(Operator_AND) !== -1) { + let rules = visibleRule.split(Operator_AND); + let predicateRules = rules.map(this.getPredicateRule); + rule = { + operator: Operator_AND, + predicateRules: predicateRules + }; + } else if (visibleRule.indexOf(Operator_OR) !== -1) { + let rules = visibleRule.split(Operator_OR); + let predicateRules = rules.map(this.getPredicateRule); + rule = { + operator: Operator_OR, + predicateRules: predicateRules + }; + } else { + let predicateRule = this.getPredicateRule(visibleRule); + rule = { + operator: null, + predicateRules: [predicateRule] + }; + } + } + + return rule; + } + + public static evaluateVisibility(visibilityRule: IVisibilityRule, dependentInputs: InputControl[]): boolean { + let result: boolean = visibilityRule.operator === Operator_AND; + + for (let i = 0, len = visibilityRule.predicateRules.length; i < len; i++) { + let predicateRule = visibilityRule.predicateRules[i]; + let dependentInput: InputControl = dependentInputs.find((dependentInput: InputControl) => { + return dependentInput.getInputControlId() === predicateRule.inputName; + }); + + if (dependentInput) { + let isInputVisible = dependentInput.isVisible(); + if (!isInputVisible) { + result = this._evaluate(result, isInputVisible, visibilityRule.operator); + } else { + let predicateResult = this._getPredicateResult(predicateRule, dependentInput.getValue()); + result = this._evaluate(result, predicateResult, visibilityRule.operator); + } + } else { + result = false; + break; + } + } + return result; + } + + private static getPredicateRule(visibleRule: string): IPredicate { + let reg = /([a-zA-Z0-9 ]+)([!=<>]+)([a-zA-Z0-9. ]+)|([a-zA-Z0-9 ]+(?=NotContains|NotEndsWith|NotStartsWith))(NotContains|NotEndsWith|NotStartsWith)([a-zA-Z0-9. ]+)|([a-zA-Z0-9 ]+(?=Contains|EndsWith|StartsWith))(Contains|EndsWith|StartsWith)([a-zA-Z0-9. ]+)/g; + let rule: IPredicate = null; + let matches = reg.exec(visibleRule); + if (matches && matches.length === 10) { + if (!!matches[1]) { + rule = { + inputName: matches[1].trim(), + condition: matches[2].trim(), + inputValue: matches[3].trim() + }; + } else if (!!matches[4]) { + rule = { + inputName: matches[4].trim(), + condition: matches[5].trim(), + inputValue: matches[6].trim() + }; + } else { + rule = { + inputName: matches[7].trim(), + condition: matches[8].trim(), + inputValue: matches[9].trim() + }; + } + } + return rule; + } + + private static _getPredicateResult(rule: IPredicate, valueToCheck: string): boolean { + let returnValue: boolean = false; + + let valueToCheckLowerCase = valueToCheck ? valueToCheck.toString().toLowerCase() : valueToCheck; + + if (rule) { + let expectedValue = rule.inputValue ? rule.inputValue.toString().toLowerCase() : rule.inputValue; + + switch (rule.condition) { + case "=": + case "==": + returnValue = (valueToCheckLowerCase === expectedValue); + break; + case "!=": + returnValue = (valueToCheckLowerCase !== expectedValue); + break; + case "<": + returnValue = (valueToCheckLowerCase < expectedValue); + break; + case ">": + returnValue = (valueToCheckLowerCase > expectedValue); + break; + case "<=": + returnValue = (valueToCheckLowerCase <= expectedValue); + break; + case ">=": + returnValue = (valueToCheckLowerCase >= expectedValue); + break; + case "Contains": + returnValue = (valueToCheck && valueToCheck.indexOf(expectedValue) >= 0); + break; + case "StartsWith": + returnValue = (valueToCheck && valueToCheck.toLowerCase().startsWith(expectedValue.toLowerCase())); + break; + case "EndsWith": + returnValue = (valueToCheck && valueToCheck.toLowerCase().endsWith(expectedValue.toLowerCase())); + break; + case "NotContains": + returnValue = !(valueToCheck && valueToCheck.indexOf(expectedValue) >= 0); + break; + case "NotStartsWith": + returnValue = !(valueToCheck && valueToCheck.toLowerCase().startsWith(expectedValue.toLowerCase())); + break; + case "NotEndsWith": + returnValue = !(valueToCheck && valueToCheck.toLowerCase().endsWith(expectedValue.toLowerCase())); + break; + } + } + + return returnValue; + } + + private static _evaluate(expr1: boolean, expr2: boolean, operator: string): boolean { + if (operator === Operator_AND) { + return expr1 && expr2; + } else if (operator === Operator_OR) { + return expr1 || expr2; + } else if (operator === null) { + // Single condition, no operator + return expr2; + } + return true; + } +} \ No newline at end of file diff --git a/Source/configure/templateInputHelper/utilities/validation/StaticValidator.ts b/Source/configure/templateInputHelper/utilities/validation/StaticValidator.ts new file mode 100644 index 00000000..d30f9dd0 --- /dev/null +++ b/Source/configure/templateInputHelper/utilities/validation/StaticValidator.ts @@ -0,0 +1,45 @@ +import * as utils from 'util'; +import { Messages } from "../../../resources/messages"; + +export class StaticValidator { + public static validateRequired(value: string): any { + if (!value) { + return Messages.valueRequired; + } + return ""; + } + + public static validateLength(value: string, minLength: number, maxLength: number): any { + if (minLength && minLength > 0 && value.length < minLength) { + return utils.format(Messages.minLengthMessage, minLength); + } + else if (maxLength && maxLength > 0 && value.length > maxLength) { + return utils.format(Messages.maxLengthMessage, maxLength); + } + return ""; + } + + public static validateNumberValue(value: any, minValue: number, maxValue: number): any { + if (!isNaN(Number(value.toString()))) { + var intValue = parseInt(value); + if (minValue && intValue < minValue) { + return utils.format(Messages.minValueMessage, minValue); + } + else if (maxValue && intValue > maxValue) { + return utils.format(Messages.maxValueMessage, maxValue); + } + } + else { + return utils.format(Messages.valueShouldBeNumber, value.toString()); + } + return ""; + } + + public static validateRegex(value: string, pattern: string, regexFlags: string = ""): any { + var regex = new RegExp(pattern, regexFlags); + if (!regex.test(value)) { + return utils.format(Messages.regexPatternNotMatchingMessage, pattern); + } + return ""; + } +} \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxFunctionApp.yml b/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxFunctionApp.yml new file mode 100644 index 00000000..25ba5644 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxFunctionApp.yml @@ -0,0 +1,78 @@ +# .NET Core Function App to Linux on Azure +# Build a .NET Core function app and deploy it to Azure as a Linux function App. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection created during pipeline creation + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Function app name + functionAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: "$(workingDirectory)/*.csproj" + arguments: --output $(System.DefaultWorkingDirectory)/publish_output --configuration Release + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(System.DefaultWorkingDirectory)/publish_output' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(functionAppName) + pool: + vmImage: $(vmImageName) + + strategy: + runOnce: + deploy: + + steps: + - task: AzureFunctionApp@1 + displayName: 'Deploy Azure Functions App: $(functionAppName)' + inputs: + azureSubscription: '$(azureSubscription)' + appType: functionAppLinux + appName: $(functionAppName) + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' + runtimeStack: 'DOCKER|microsoft/azure-functions/dotnet:2.0' + appSettings: '-FUNCTIONS_WORKER_RUNTIME dotnet' \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxWebApp.yml new file mode 100644 index 00000000..c17c6f37 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/dotnetcoreLinuxWebApp.yml @@ -0,0 +1,108 @@ +# .NET Core Web App to Linux on Azure +# Build a .NET Core Web App and deploy it to Azure as a Linux Web App. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection created during pipeline creation + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web App name + webAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + + # Build Configuration + buildConfiguration: 'Release' + + # Build Projects + buildProjects: "{{#equals}}{{{ workingDirectory }}} '.' true '**/*.csproj' '$(workingDirectory)/**/*.csproj'{{/equals}}" + + # Test Projects + testProjects: "{{#equals}}{{{ workingDirectory }}} '.' true '**/*[Tt]est*/*.csproj' '$(workingDirectory)/**/*[Tt]est*/*.csproj'{{/equals}}" + + # Dotnet Core Version + dotnetCoreVersion: 2.x + +stages: +- stage: Build + displayName: Build stage + + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + packageType: sdk + version: $(dotnetCoreVersion) + + - task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: 'restore' + publishWebProjects: true + projects: $(buildProjects) + + - task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: $(buildProjects) + arguments: --configuration $(buildConfiguration) + + - task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: 'test' + projects: $(testProjects) + publishWebProjects: true + arguments: --configuration $(buildConfiguration) + + - task: DotNetCoreCLI@2 + displayName: Publish + inputs: + command: 'publish' + publishWebProjects: true + arguments: --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory) + zipAfterPublish: true + + - publish: $(Build.ArtifactStagingDirectory) + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(webAppName) + pool: + vmImage: $(vmImageName) + strategy: + runOnce: + deploy: + steps: + - task: AzureRMWebAppDeployment@4 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + ConnectionType: "AzureRM" + ConnectedServiceName: $(azureSubscription) + WebAppName: $(webAppName) + WebAppKind: webAppLinux + Package: $(Pipeline.Workspace)/**/*.zip + DeploymentType: "webDeploy" + RuntimeStack: "DOTNETCORE|2.2" diff --git a/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsFunctionApp.yml b/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsFunctionApp.yml new file mode 100644 index 00000000..7a244135 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsFunctionApp.yml @@ -0,0 +1,76 @@ +# .NET Core Function App to Windows on Azure +# Build a .NET Core function app and deploy it to Azure as a Windows function App. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection created during pipeline creation + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Function app name + functionAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'vs2017-win2016' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: "$(workingDirectory)/*.csproj" + arguments: --output $(System.DefaultWorkingDirectory)/publish_output --configuration Release + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(System.DefaultWorkingDirectory)/publish_output' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(functionAppName) + pool: + vmImage: $(vmImageName) + + strategy: + runOnce: + deploy: + + steps: + - task: AzureFunctionApp@1 + displayName: 'Azure Functions App Deploy: {{ functionAppName }}' + inputs: + azureSubscription: '$(azureSubscription)' + appType: functionApp + appName: $(functionAppName) + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsWebApp.yml b/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsWebApp.yml new file mode 100644 index 00000000..ac28fd94 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/dotnetcoreWindowsWebApp.yml @@ -0,0 +1,105 @@ +# .NET Core Web App to Windows on Azure +# Build a .NET Core Web App and deploy it to Azure as a Windows Web App. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/en-us/azure/devops/pipelines/languages/dotnet-core + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection created during pipeline creation + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web App name + webAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'vs2017-win2016' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + + # Build Configuration + buildConfiguration: 'Release' + + # Build Projects + buildProjects: "{{#equals}}{{{ workingDirectory }}} '.' true '**/*.csproj' '$(workingDirectory)/**/*.csproj'{{/equals}}" + + # Test Projects + testProjects: "{{#equals}}{{{ workingDirectory }}} '.' true '**/*[Tt]est*/*.csproj' '$(workingDirectory)/**/*[Tt]est*/*.csproj'{{/equals}}" + + # Dotnet Core Version + dotnetCoreVersion: 2.x + +stages: +- stage: Build + displayName: Build stage + + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + packageType: sdk + version: $(dotnetCoreVersion) + + - task: DotNetCoreCLI@2 + displayName: Restore + inputs: + command: 'restore' + publishWebProjects: true + projects: $(buildProjects) + + - task: DotNetCoreCLI@2 + displayName: Build + inputs: + command: 'build' + projects: $(buildProjects) + arguments: --configuration $(buildConfiguration) + + - task: DotNetCoreCLI@2 + displayName: Test + inputs: + command: 'test' + projects: $(testProjects) + publishWebProjects: true + arguments: --configuration $(buildConfiguration) + + - task: DotNetCoreCLI@2 + displayName: Publish + inputs: + command: 'publish' + publishWebProjects: true + arguments: --configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory) + zipAfterPublish: true + + - publish: $(Build.ArtifactStagingDirectory) + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(webAppName) + pool: + vmImage: $(vmImageName) + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/**/*.zip \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/nodejs.yml b/Source/configure/templates/azurePipelineTemplates/nodejs.yml new file mode 100644 index 00000000..e85719f2 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejs.yml @@ -0,0 +1,72 @@ +# Node.js App on Windows Web App +# Build a Node.js app and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + npm build + displayName: 'npm install and build' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsLinuxFunctionApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsLinuxFunctionApp.yml new file mode 100644 index 00000000..c9eb0d40 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsLinuxFunctionApp.yml @@ -0,0 +1,86 @@ +# Node.js Function App to Linux on Azure +# Build a Node.js function app and deploy it to Azure as a Linux function app. +# Add steps that build code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + functionAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + if [ -f extensions.csproj ] + then + dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin + fi + displayName: 'Build extensions' + workingDirectory: $(workingDirectory) + + - script: | + npm install + npm run build --if-present + npm run test --if-present + displayName: 'Prepare binaries' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(workingDirectory)' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(functionAppName) + pool: + vmImage: $(vmImageName) + strategy: + runOnce: + deploy: + steps: + - task: AzureFunctionApp@1 + displayName: 'Deploy Azure Functions App: $(functionAppName)' + inputs: + azureSubscription: '$(azureSubscription)' + appType: functionAppLinux + appName: $(functionAppName) + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' + runtimeStack: 'DOCKER|microsoft/azure-functions/node:2.0' + appSettings: '-FUNCTIONS_WORKER_RUNTIME node' diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsLinuxWebApp.yml new file mode 100644 index 00000000..c88c9899 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsLinuxWebApp.yml @@ -0,0 +1,73 @@ +# Node.js App on Linux Web App +# Build a Node.js app and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + npm build + displayName: 'npm install and build' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + runtimeStack: 'NODE|10.10' + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWindowsFunctionApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWindowsFunctionApp.yml new file mode 100644 index 00000000..34911c21 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWindowsFunctionApp.yml @@ -0,0 +1,83 @@ +# Node.js Function App to Windows on Azure +# Build a Node.js function app and deploy it to Azure as a Windows function app. +# Add steps that build code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + functionAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'vs2017-win2016' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - powershell: | + if (Test-Path "extensions.csproj") { + dotnet build extensions.csproj --output ./bin + } + displayName: 'Build extensions' + workingDirectory: $(workingDirectory) + + - script: | + npm install + npm run build --if-present + npm run test --if-present + displayName: 'Prepare binaries' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(workingDirectory)' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(functionAppName) + pool: + vmImage: $(vmImageName) + strategy: + runOnce: + deploy: + steps: + - task: AzureFunctionApp@1 + displayName: 'Deploy Azure Functions App: $(functionAppName)' + inputs: + azureSubscription: '$(azureSubscription)' + appType: functionApp + appName: $(functionAppName) + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithAngular.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithAngular.yml new file mode 100644 index 00000000..fb084fbc --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithAngular.yml @@ -0,0 +1,73 @@ +# Node.js with Angular on Windows Web App +# Build a Node.js project that uses Angular and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install -g @angular/cli + npm install + ng build --prod + displayName: 'npm install and build' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithAngularLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithAngularLinuxWebApp.yml new file mode 100644 index 00000000..10238ccb --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithAngularLinuxWebApp.yml @@ -0,0 +1,74 @@ +# Node.js with Angular on Linux Web App +# Build a Node.js project that uses Angular and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install -g @angular/cli + npm install + ng build --prod + displayName: 'npm install and build' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + runtimeStack: 'NODE|10.10' + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithGrunt.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithGrunt.yml new file mode 100644 index 00000000..d3b144eb --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithGrunt.yml @@ -0,0 +1,72 @@ +# Node.js with Grunt on Windows Web App +# Build a Node.js project using the Grunt task runner and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + grunt --gruntfile Gruntfile.js + displayName: 'npm install and run grunt' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithGruntLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithGruntLinuxWebApp.yml new file mode 100644 index 00000000..b6c61d9e --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithGruntLinuxWebApp.yml @@ -0,0 +1,73 @@ +# Node.js with Grunt on Linux Web App +# Build a Node.js project using the Grunt task runner and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + grunt --gruntfile Gruntfile.js + displayName: 'npm install and run grunt' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + runtimeStack: 'NODE|10.10' + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithGulp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithGulp.yml new file mode 100644 index 00000000..19e22a8f --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithGulp.yml @@ -0,0 +1,72 @@ +# Node.js with gulp on Windows Web App +# Build a Node.js project using the gulp task runner and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + gulp default --gulpfile gulpfile.js + displayName: 'npm install and run gulp' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithGulpLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithGulpLinuxWebApp.yml new file mode 100644 index 00000000..bd3208eb --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithGulpLinuxWebApp.yml @@ -0,0 +1,73 @@ +# Node.js with gulp on Linux Web App +# Build a Node.js project using the gulp task runner and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install + gulp default --gulpfile gulpfile.js + displayName: 'npm install and run gulp' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + runtimeStack: 'NODE|10.10' + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpack.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpack.yml new file mode 100644 index 00000000..f7d6f4a3 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpack.yml @@ -0,0 +1,73 @@ +# Node.js with webpack on Windows Web App +# Build a Node.js project using the webpack CLI and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install -g webpack webpack-cli --save-dev + npm install + npx webpack --config webpack.config.js + displayName: 'npm install, run webpack' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpackLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpackLinuxWebApp.yml new file mode 100644 index 00000000..9b549b2b --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/nodejsWithWebpackLinuxWebApp.yml @@ -0,0 +1,74 @@ +# Node.js with webpack on Linux Web App +# Build a Node.js project using the webpack CLI and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/javascript + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + - task: NodeTool@0 + inputs: + versionSpec: '10.x' + displayName: 'Install Node.js' + + - script: | + npm install -g webpack webpack-cli --save-dev + npm install + npx webpack --config webpack.config.js + displayName: 'npm install, run webpack' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + runtimeStack: 'NODE|10.10' + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/azurePipelineTemplates/pythonDjango.yml b/Source/configure/templates/azurePipelineTemplates/pythonDjango.yml new file mode 100644 index 00000000..061f3174 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/pythonDjango.yml @@ -0,0 +1,58 @@ +# Python Django +# Test a Django project on multiple versions of Python. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- {{{ sourceRepository.branch }}} + +pool: + vmImage: 'ubuntu-latest' +strategy: + matrix: + Python35: + PYTHON_VERSION: '3.5' + Python36: + PYTHON_VERSION: '3.6' + Python37: + PYTHON_VERSION: '3.7' + maxParallel: 3 + +steps: +- task: UsePythonVersion@0 + inputs: + versionSpec: '$(PYTHON_VERSION)' + architecture: 'x64' + +- task: PythonScript@0 + displayName: 'Export project path' + inputs: + scriptSource: 'inline' + script: | + """Search all subdirectories for `manage.py`.""" + from glob import iglob + from os import path + # Python >= 3.5 + manage_py = next(iglob(path.join('**', 'manage.py'), recursive=True), None) + if not manage_py: + raise SystemExit('Could not find a Django project') + project_location = path.dirname(path.abspath(manage_py)) + print('Found Django project in', project_location) + print('##vso[task.setvariable variable=projectRoot]{}'.format(project_location)) + +- script: | + python -m pip install --upgrade pip setuptools wheel + pip install -r requirements.txt + pip install unittest-xml-reporting + displayName: 'Install prerequisites' + +- script: | + pushd '$(projectRoot)' + python manage.py test --testrunner xmlrunner.extra.djangotestrunner.XMLTestRunner --no-input + displayName: 'Run tests' + +- task: PublishTestResults@2 + inputs: + testResultsFiles: "**/TEST-*.xml" + testRunTitle: 'Python $(PYTHON_VERSION)' + condition: succeededOrFailed() diff --git a/Source/configure/templates/azurePipelineTemplates/pythonLinuxFunctionApp.yml b/Source/configure/templates/azurePipelineTemplates/pythonLinuxFunctionApp.yml new file mode 100644 index 00000000..de511c74 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/pythonLinuxFunctionApp.yml @@ -0,0 +1,93 @@ +# Python Function App to Linux on Azure +# Build a Python function app and deploy it to Azure as a Linux function app. +# Add steps that analyze code, save build artifacts, deploy, and more: +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + functionAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + + # Python Version + pythonVersion: 3.6 # Functions V2 supports Python 3.6 as of today + +stages: +- stage: Build + displayName: Build stage + + jobs: + - job: Build + displayName: Build + pool: + vmImage: $(vmImageName) + + steps: + - bash: | + if [ -f extensions.csproj ] + then + dotnet build extensions.csproj --runtime ubuntu.16.04-x64 --output ./bin + fi + displayName: 'Build extensions' + workingDirectory: $(workingDirectory) + + - task: UsePythonVersion@0 + displayName: 'Use Python 3.6' + inputs: + versionSpec: $(pythonVersion) + + - bash: | + python -m venv worker_venv + source worker_venv/bin/activate + pip install -r requirements.txt + displayName: 'Install application dependencies' + workingDirectory: $(workingDirectory) + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(workingDirectory)' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - publish: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + + jobs: + - deployment: Deploy + displayName: Deploy + environment: $(functionAppName) + pool: + vmImage: $(vmImageName) + + strategy: + runOnce: + deploy: + + steps: + - task: AzureFunctionApp@1 + displayName: 'Deploy Azure Functions App: $(functionAppName)' + inputs: + azureSubscription: '$(azureSubscription)' + appType: functionAppLinux + appName: $(functionAppName) + package: '$(Pipeline.Workspace)/drop/$(Build.BuildId).zip' + runtimeStack: 'DOCKER|microsoft/azure-functions/python:2.0' + appSettings: '-FUNCTIONS_WORKER_RUNTIME python' \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/pythonLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/pythonLinuxWebApp.yml new file mode 100644 index 00000000..b6466457 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/pythonLinuxWebApp.yml @@ -0,0 +1,87 @@ +# Python to Linux Web App on Azure +# Build your Python project and deploy it to Azure as a Linux Web App. +# Change python version to one thats appropriate for your application. +# https://docs.microsoft.com/azure/devops/pipelines/languages/python + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection created during pipeline creation + azureServiceConnectionId: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Agent VM image name + vmImageName: 'ubuntu-latest' + + # Environment name + environmentName: '{{{ targetResource.resource.name }}}' + + # Project root folder. Point to the folder containing manage.py file. + projectRoot: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + pool: + vmImage: $(vmImageName) + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.7' + displayName: 'Use Python 3.7' + + - script: | + python -m venv --copies antenv + source antenv/bin/activate + python -m pip install --upgrade pip + pip install setup + pip install -r requirements.txt + workingDirectory: $(projectRoot) + displayName: "Install requirements" + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: '$(projectRoot)' + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + displayName: 'Upload package' + artifact: drop + +- stage: Deploy + displayName: 'Deploy Web App' + dependsOn: Build + condition: succeeded() + jobs: + - deployment: DeploymentJob + pool: + vmImage: $(vmImageName) + environment: $(environmentName) + strategy: + runOnce: + deploy: + steps: + + - task: UsePythonVersion@0 + inputs: + versionSpec: '3.7' + displayName: 'Use Python 3.7' + + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureServiceConnectionId) + appName: $(webAppName) + appType: webAppLinux + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip + runtimeStack: 'PYTHON|3.7' + startupCommand: '' # Provide an optional startup command that will be run as part of container startup. For Node, it is the PM2 configuration file or your script file; For .Net Core it is the compiled DLL name as dotnet .dll. Learn more at https://docs.microsoft.com/en-us/azure/app-service/containers/app-service-linux-faq#built-in-images \ No newline at end of file diff --git a/Source/configure/templates/azurePipelineTemplates/simpleLinuxWebApp.yml b/Source/configure/templates/azurePipelineTemplates/simpleLinuxWebApp.yml new file mode 100644 index 00000000..514e52b0 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/simpleLinuxWebApp.yml @@ -0,0 +1,61 @@ +# Simple App on Linux Web App +# Package and deploy a simple web application and deploy it to Azure as Linux web app. + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'ubuntu-latest' + + steps: + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'ubuntu-latest' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webAppLinux + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip + startupCommand: '' # Provide an optional startup command that will be run as part of container startup. For Node, it is the PM2 configuration file or your script file; For .Net Core it is the compiled DLL name as dotnet .dll. Learn more at https://docs.microsoft.com/en-us/azure/app-service/containers/app-service-linux-faq#built-in-images diff --git a/Source/configure/templates/azurePipelineTemplates/simpleWebApp.yml b/Source/configure/templates/azurePipelineTemplates/simpleWebApp.yml new file mode 100644 index 00000000..1ea8f197 --- /dev/null +++ b/Source/configure/templates/azurePipelineTemplates/simpleWebApp.yml @@ -0,0 +1,60 @@ +# Simple App on Windows Web App +# Package and deploy a simple web application and deploy it to Azure as Windows web app. + +trigger: +- {{{ sourceRepository.branch }}} + +variables: + # Azure Resource Manager connection + azureSubscription: '{{{ targetResource.serviceConnectionId }}}' + + # Web app name + webAppName: '{{{ targetResource.resource.name }}}' + + # Working Directory + workingDirectory: '{{{ workingDirectory }}}' + +stages: +- stage: Build + displayName: Build stage + jobs: + - job: BuildJob + displayName: Build + pool: + vmImage: 'vs2017-win2016' + + steps: + + - task: ArchiveFiles@2 + displayName: 'Archive files' + inputs: + rootFolderOrFile: $(workingDirectory) + includeRootFolder: false + archiveType: zip + archiveFile: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + replaceExistingArchive: true + + - upload: $(Build.ArtifactStagingDirectory)/$(Build.BuildId).zip + artifact: drop + +- stage: Deploy + displayName: Deploy stage + dependsOn: Build + condition: succeeded() + jobs: + - deployment: Deploy + environment: 'development' + pool: + vmImage: 'vs2017-win2016' + + strategy: + runOnce: + deploy: + steps: + - task: AzureWebApp@1 + displayName: 'Deploy Azure Web App: $(webAppName)' + inputs: + azureSubscription: $(azureSubscription) + appType: webApp + appName: $(webAppName) + package: $(Pipeline.Workspace)/drop/$(Build.BuildId).zip diff --git a/Source/configure/templates/dependencies/deployment.yml b/Source/configure/templates/dependencies/deployment.yml new file mode 100644 index 00000000..315a775c --- /dev/null +++ b/Source/configure/templates/dependencies/deployment.yml @@ -0,0 +1,19 @@ +apiVersion : apps/v1 +kind: Deployment +metadata: + name: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} +spec: + replicas: 2 + selector: + matchLabels: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + template: + metadata: + labels: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + spec: + containers: + - name: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + image: {{#toLower}}{{{inputs.containerRegistry.name}}}{{/toLower}}.azurecr.io/{{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + ports: + - containerPort: {{{ inputs.containerPort }}} \ No newline at end of file diff --git a/Source/configure/templates/dependencies/ingress.yml b/Source/configure/templates/dependencies/ingress.yml new file mode 100644 index 00000000..da5ceaf0 --- /dev/null +++ b/Source/configure/templates/dependencies/ingress.yml @@ -0,0 +1,17 @@ +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + labels: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + annotations: + kubernetes.io/ingress.class: addon-http-application-routing +spec: + rules: + - host: {{{ inputs.namespace }}}-{{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}}.{{{inputs.aksCluster.properties.addonProfiles.httpApplicationRouting.config.httpapplicationroutingzonename }}} + http: + paths: + - path: / + backend: + serviceName: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + servicePort: {{{ inputs.containerPort }}} \ No newline at end of file diff --git a/Source/configure/templates/dependencies/service-ingress.yml b/Source/configure/templates/dependencies/service-ingress.yml new file mode 100644 index 00000000..5649f801 --- /dev/null +++ b/Source/configure/templates/dependencies/service-ingress.yml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + labels: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} +spec: + type: ClusterIP + ports: + - port: {{{ inputs.containerPort }}} + targetPort: {{{ inputs.containerPort }}} + protocol: TCP + name: http + selector: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} \ No newline at end of file diff --git a/Source/configure/templates/dependencies/service.yml b/Source/configure/templates/dependencies/service.yml new file mode 100644 index 00000000..8627a516 --- /dev/null +++ b/Source/configure/templates/dependencies/service.yml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + labels: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} +spec: + type: LoadBalancer + ports: + - port: {{{ inputs.containerPort }}} + targetPort: {{{ inputs.containerPort }}} + protocol: TCP + name: http + selector: + app: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/AksWithReuseACR.yml b/Source/configure/templates/githubWorkflowTemplates/AksWithReuseACR.yml new file mode 100644 index 00000000..b63cb8bd --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/AksWithReuseACR.yml @@ -0,0 +1,62 @@ +on: [push] +jobs: + build-and-deploy: + env: + REGISTRY_URL: {{#toLower}}{{{inputs.containerRegistry.name}}}{{/toLower}}.azurecr.io + NAMESPACE: {{{ inputs.namespace }}} + IMAGE_NAME: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 50{{/substring}} + SECRET_NAME: {{#substring}}'{{#toLower}}{{#sanitizeString}}{{{ inputs.aksCluster.name }}}{{/sanitizeString}}{{/toLower}}' 0 63{{/substring}} + + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@master + + - uses: azure/docker-login@v1 + with: + {{=<% %>=}} + login-server: ${{ env.REGISTRY_URL }} + username: ${{ secrets.<% assets.containerRegistryUsername %> }} + password: ${{ secrets.<% assets.containerRegistryPassword %> }} + <%={{ }}=%> + + - name: Build and push image to ACR + id: build-image + run: | + docker build "$GITHUB_WORKSPACE/{{{ workingDirectory }}}" -f "{{{ workingDirectory }}}/Dockerfile" -t $REGISTRY_URL/$IMAGE_NAME:{{=<% %>=}}${{ github.sha }}<%={{ }}=%> + docker push $REGISTRY_URL/$IMAGE_NAME:{{=<% %>=}}${{ github.sha }}<%={{ }}=%> + + - uses: azure/k8s-set-context@v1 + with: + kubeconfig: {{=<% %>=}}${{ secrets.<% assets.kubeConfig %> }}<%={{ }}=%> + id: login + + - name: Create namespace + run: | + namespacePresent=`kubectl get namespace | grep $NAMESPACE | wc -l` + if [ $namespacePresent -eq 0 ] + then + echo `kubectl create namespace $NAMESPACE` + fi + + - uses: azure/k8s-create-secret@v1 + with: + {{=<% %>=}} + namespace: ${{ env.NAMESPACE }} + container-registry-url: ${{ env.REGISTRY_URL }} + container-registry-username: ${{ secrets.<% assets.containerRegistryUsername %> }} + container-registry-password: ${{ secrets.<% assets.containerRegistryPassword %> }} + secret-name: ${{ env.SECRET_NAME }} + <%={{ }}=%> + + - uses: azure/k8s-deploy@v1 + with: + namespace: {{=<% %>=}}${{ env.NAMESPACE }}<%={{ }}=%> + manifests: | + {{{assets.deployment}}} + {{#if}}{{{inputs.aksCluster.properties.addonProfiles.httpApplicationRouting.enabled}}} {{{assets.service-ingress}}} {{{assets.service}}} {{/if}} + {{#if}}{{{inputs.aksCluster.properties.addonProfiles.httpApplicationRouting.enabled}}} {{{assets.ingress}}} {{/if}} + + images: | + {{=<% %>=}}${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ github.sha }}<%={{ }}=%> + imagepullsecrets: | + {{=<% %>=}}${{ env.SECRET_NAME }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsLinuxFunctionApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsLinuxFunctionApp.yml new file mode 100644 index 00000000..bd4b40be --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsLinuxFunctionApp.yml @@ -0,0 +1,45 @@ +# Node.js Function App to Linux on Azure +# Build a Node.js function app and deploy it to Azure as a Linux function app. +# Add steps that build code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node Function app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - name: 'Run npm' + shell: bash + working-directory: {{{ workingDirectory }}} + run: | + npm install + npm run build --if-present + npm run test --if-present + + # deploy web app using publish profile credentials + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsOnLinux.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsOnLinux.yml new file mode 100644 index 00000000..c096f37c --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsOnLinux.yml @@ -0,0 +1,42 @@ +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + # install dependencies, build, and test + - name: npm install, build, and test + working-directory: {{{ workingDirectory }}} + run: | + npm install + npm run build --if-present + npm run test --if-present + + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "NODE|lts"}' #'General configuration settings as Key Value pairs' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsOnWindows.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsOnWindows.yml new file mode 100644 index 00000000..1389a176 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsOnWindows.yml @@ -0,0 +1,32 @@ +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build, and test + working-directory: {{{ workingDirectory }}} + run: | + npm install + npm run build --if-present + npm run test --if-present + + # deploy web app using publish profile credentials + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWindowsFunctionApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWindowsFunctionApp.yml new file mode 100644 index 00000000..72bc146f --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWindowsFunctionApp.yml @@ -0,0 +1,45 @@ +# Node.js Function App to Windows on Azure +# Build a Node.js function app and deploy it to Azure as a Windows function app. +# Add steps that build code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node Function app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - name: 'Run npm' + shell: bash + working-directory: {{{ workingDirectory }}} + run: | + # If your function app project is not located in your repository's root + # Please change your directory for npm in pushd + pushd . + npm install + npm run build --if-present + npm run test --if-present + popd + + # deploy web app using publish profile credentials + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnLinuxWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnLinuxWebApp.yml new file mode 100644 index 00000000..f872c4ab --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnLinuxWebApp.yml @@ -0,0 +1,50 @@ +# Node.js with Angular on Linux Web App +# Build a Node.js project that uses Angular and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + # login using publishing credentials + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install -g @angular/cli + npm install + ng build --prod + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "NODE|10.10"}' #'General configuration settings as Key Value pairs' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnWindowsWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnWindowsWebApp.yml new file mode 100644 index 00000000..432bf6c7 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithAngularOnWindowsWebApp.yml @@ -0,0 +1,36 @@ +# Node.js with Angular on Windows Web App +# Build a Node.js project that uses Angular and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install -g @angular/cli + npm install + ng build --prod + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnLinuxWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnLinuxWebApp.yml new file mode 100644 index 00000000..11242a00 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnLinuxWebApp.yml @@ -0,0 +1,49 @@ +# Node.js with Grunt on Linux Web App +# Build a Node.js project using the Grunt task runner and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + # login using publishing credentials + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install + grunt --gruntfile {{{repositoryAnalysisApplicationSettings.settings.gruntFilePath}}} + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "NODE|10.10"}' #'General configuration settings as Key Value pairs' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnWindowsWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnWindowsWebApp.yml new file mode 100644 index 00000000..e9b77f9f --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGruntOnWindowsWebApp.yml @@ -0,0 +1,35 @@ +# Node.js with Grunt on Windows Web App +# Build a Node.js project using the Grunt task runner and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install + grunt --gruntfile {{{repositoryAnalysisApplicationSettings.settings.gruntFilePath}}} + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnLinuxWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnLinuxWebApp.yml new file mode 100644 index 00000000..15e7b975 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnLinuxWebApp.yml @@ -0,0 +1,49 @@ +# Node.js with gulp on Linux Web App +# Build a Node.js project using the gulp task runner and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + # login using publishing credentials + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install + gulp default --gulpfile {{{repositoryAnalysisApplicationSettings.settings.gulpFilePath}}} + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "NODE|10.10"}' #'General configuration settings as Key Value pairs' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnWindowsWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnWindowsWebApp.yml new file mode 100644 index 00000000..55417b2d --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithGulpOnWindowsWebApp.yml @@ -0,0 +1,35 @@ +# Node.js with gulp on Windows Web App +# Build a Node.js project using the gulp task runner and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install + gulp default --gulpfile {{{repositoryAnalysisApplicationSettings.settings.gulpFilePath}}} + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnLinuxWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnLinuxWebApp.yml new file mode 100644 index 00000000..db5f637f --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnLinuxWebApp.yml @@ -0,0 +1,50 @@ +# Node.js with webpack on Linux Web App +# Build a Node.js project using the webpack CLI and deploy it to Azure as a Linux web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + # login using publishing credentials + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install -g webpack webpack-cli --save-dev + npm install + npx webpack --config webpack.config.js + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "NODE|10.10"}' #'General configuration settings as Key Value pairs' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnWindowsWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnWindowsWebApp.yml new file mode 100644 index 00000000..006c3cfb --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/nodejsWithWebpackOnWindowsWebApp.yml @@ -0,0 +1,36 @@ +# Node.js with webpack on Windows Web App +# Build a Node.js project using the webpack CLI and deploy it to Azure as a Windows web app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Node app + +jobs: + build-and-deploy: + runs-on: windows-2019 + steps: + # checkout the repo + - uses: actions/checkout@master + + - uses: actions/setup-node@v1 + with: + node-version: '10.x' + + # install dependencies, build, and test + - name: npm install, build + working-directory: {{{ workingDirectory }}} + run: | + npm install -g webpack webpack-cli --save-dev + npm install + npx webpack --config webpack.config.js + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/pythonLinuxFunctionApp.yml b/Source/configure/templates/githubWorkflowTemplates/pythonLinuxFunctionApp.yml new file mode 100644 index 00000000..f9189261 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/pythonLinuxFunctionApp.yml @@ -0,0 +1,74 @@ +# Python Function App to Linux on Azure +# Build a Python function app and deploy it to Azure as a Linux function app. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Python Function app + +jobs: + build: + runs-on: ubuntu-latest + steps: + + # checkout the repo + - uses: actions/checkout@master + + # Setup python + - name: Setup python + uses: actions/setup-python@v1 + with: + python-version: 3.6 + + # Install dependencies + - name: 'Run pip' + shell: bash + working-directory: {{{ workingDirectory }}} + run: | + python -m pip install --upgrade pip + python -m venv --copies worker_venv + source worker_venv/bin/activate + pip install -r {{{repositoryAnalysisApplicationSettings.settings.pythonRequirementsFilePath}}} + + # upload artificats + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v2 + with: + name: webapp + path: | + . + !venv/ + + deploy: + runs-on: ubuntu-latest + needs: build + steps: + + # Download artifacts + - name: Download artifact from build job + uses: actions/download-artifact@v2 + with: + name: webapp + path: . + + # Azure login + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + # deploy function + - name: 'Run Azure Functions Action' + uses: Azure/functions-action@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + scm-do-build-during-deployment: true + enable-oryx-build: true + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/pythonLinuxWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/pythonLinuxWebApp.yml new file mode 100644 index 00000000..9a7f4803 --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/pythonLinuxWebApp.yml @@ -0,0 +1,77 @@ +# Python Web App to Linux on Azure +# Build a Python WebApp and deploy it to Azure as a Linux WebApp. +# Add steps that analyze code, save build artifacts, deploy, and more: + +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Build and deploy Python app + +jobs: + build: + runs-on: ubuntu-latest + steps: + + # checkout the repo + - uses: actions/checkout@master + + # setup python + - name: Setup Python 3.7 + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + # install dependencies + - name: python install + working-directory: {{{ workingDirectory }}} + run: | + sudo apt install python3.7-venv + python -m venv --copies antenv + source antenv/bin/activate + pip install setuptools + pip install -r {{{repositoryAnalysisApplicationSettings.settings.pythonRequirementsFilePath}}} + + # Upload artificats + - name: Upload artifact for deployment job + uses: actions/upload-artifact@v2 + with: + name: webapp + path: | + . + !venv/ + + deploy: + runs-on: ubuntu-latest + needs: build + steps: + + # Download artifacts + - name: Download artifact from build job + uses: actions/download-artifact@v2 + with: + name: webapp + path: . + + # Azure login + - uses: azure/login@v1 + with: + creds: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> + + - uses: azure/appservice-settings@v1 + with: + app-name: {{{ targetResource.resource.name }}} + general-settings-json: '{"linuxFxVersion": "PYTHON|3.7"}' #'General configuration settings as Key Value pairs' + app-settings-json: '[{ "name": "SCM_DO_BUILD_DURING_DEPLOYMENT", "value": "true" }]' + + # deploy web app + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + + # Azure logout + - name: logout + run: | + az logout \ No newline at end of file diff --git a/Source/configure/templates/githubWorkflowTemplates/simpleWebApp.yml b/Source/configure/templates/githubWorkflowTemplates/simpleWebApp.yml new file mode 100644 index 00000000..0254cfac --- /dev/null +++ b/Source/configure/templates/githubWorkflowTemplates/simpleWebApp.yml @@ -0,0 +1,20 @@ +on: + push: + branches: + - {{{ sourceRepository.branch }}} + +name: Package and deploy simple web app + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + steps: + # checkout the repo + - uses: actions/checkout@master + + # deploy web app using publish profile credentials + - uses: azure/webapps-deploy@v1 + with: + app-name: {{{ targetResource.resource.name }}} + package: {{{ workingDirectory }}} + publish-profile: {{=<% %>=}}${{ secrets.<% targetResource.serviceConnectionId %> }}<%={{ }}=%> \ No newline at end of file diff --git a/Source/configure/utilities/templateConverter.ts b/Source/configure/utilities/templateConverter.ts new file mode 100644 index 00000000..9aee4281 --- /dev/null +++ b/Source/configure/utilities/templateConverter.ts @@ -0,0 +1,108 @@ +const parametersRegex = /(inputs|variables|assets|secrets|system)\.\w+/g; +const enclosedParametersRegex = /{{{?\s*(inputs|variables|assets|secrets|system)\.\w+\s*}?}}/g; +const mustacheHelperRegex = /{{{?\s*#(\w+)(.*?)({?{{\s*\/\1)}?}}/; + +export function convertExpression(expression: string): string { + let match = expression.match(mustacheHelperRegex); + if (!match) { + return expression; + } + let localMustacheExpression = ""; + let openingHelperFunc = "{{"; + let closingHelperFunc = "}}"; + let parts = []; + let helperName = ""; + + if (expression.startsWith('{{{')) { + closingHelperFunc = "}}}" + openingHelperFunc = "{{{" + } + + if (expression.indexOf(' ') !== -1 && expression.indexOf(' ') < expression.indexOf(closingHelperFunc)) { + helperName = expression.substring(openingHelperFunc.length + 1, expression.indexOf(' ')); + parts.push(expression.substring(0, expression.indexOf(' ')) + closingHelperFunc); + let part = expression.substring(expression.indexOf(' '), expression.indexOf(closingHelperFunc)).trim(); + let input = parametersRegex.exec(part); + while (input) { + part = replaceAtIndex(part, input[0], "{{{" + input[0] + "}}}", input.index); + input = parametersRegex.exec(part); + } + parts.push(part); + } + else { + helperName = expression.substring(openingHelperFunc.length + 1, expression.indexOf(closingHelperFunc)); + parts.push(expression.substring(0, expression.indexOf(closingHelperFunc) + closingHelperFunc.length)); + } + + if (expression.substr(expression.indexOf(closingHelperFunc) + closingHelperFunc.length, openingHelperFunc.length + 1 + helperName.length) === openingHelperFunc + '/' + helperName) { + parts.push(expression.substring(expression.indexOf(closingHelperFunc) + closingHelperFunc.length)); + } + else { + let part = expression.substring(expression.indexOf(closingHelperFunc) + closingHelperFunc.length, expression.indexOf(openingHelperFunc + '/' + helperName)); + part = convertStringMustachExpression(part); + parts.push(part); + parts.push(expression.substring(expression.indexOf(openingHelperFunc + '/' + helperName))); + } + + if (parts.length === 4) { + localMustacheExpression = parts[0] + parts[1] + ' ' + parts[2] + parts[3]; + } + else { + localMustacheExpression = parts.join(''); + } + return localMustacheExpression + +} + +export function convertStringMustachExpression(text: string): string { + let helperRegExp = /{?{{\s*#(\w+)(.*?)({?{{\s*\/\1)}}}?/g; + let result = helperRegExp.exec(text); + while (result) { + let exp = convertExpression(result[0]); + text = replaceAtIndex(text, result[0], exp, result.index); + result = helperRegExp.exec(text); + } + return text; +} + +export function sanitizeExpression(text: string): string { + let result = enclosedParametersRegex.exec(text); + while (result) { + if (!result[0].startsWith('{{{')) { + text = replaceAtIndex(text, result[0], '{' + result[0] + '}', result.index); + } + result = enclosedParametersRegex.exec(text); + } + return text; +} + +export function convertToLocalMustacheExpression(object: any): any { + if (typeof object === "string") { + object = sanitizeExpression(object); + return convertStringMustachExpression(object); + } + + if (Array.isArray(object)) { + for (let i = 0; i < object.length; i++) { + object[i] = convertToLocalMustacheExpression(object[i]); + } + return object; + } + + Object.keys(object).forEach(key => { + if (!!key && !!object[key]) { + object[key] = convertToLocalMustacheExpression(object[key]); + } + }); + + return object; +} + +function replaceAtIndex(text: string, searchValue: string, replaceValue: string, index: number) { + if (text.indexOf(searchValue, index) !== -1) { + let preStr = text.substr(0, index); + let postStr = text.substr(index + searchValue.length); + text = preStr + replaceValue + postStr; + } + return text; +} diff --git a/Source/configure/utilities/utilities.ts b/Source/configure/utilities/utilities.ts new file mode 100644 index 00000000..686c32f7 --- /dev/null +++ b/Source/configure/utilities/utilities.ts @@ -0,0 +1,21 @@ +import * as crypto from 'crypto'; + +export class Utilities { + public static createSha256Hash(input: string): string { + return crypto.createHash('sha256').update(input).digest('hex'); + } +} + +export class WhiteListedError extends Error { + constructor(message?: string, error?: any) { + super(message || error.message); + if (error) { + const errorClone = JSON.parse(JSON.stringify(error)); + for (var attribute in errorClone) { + this[attribute] = errorClone[attribute]; + } + this.message = message || this.message; + this.stack = error.stack || ""; + } + } +} \ No newline at end of file diff --git a/Source/configure/utilities/webAppNodeVersionConverter.ts b/Source/configure/utilities/webAppNodeVersionConverter.ts new file mode 100644 index 00000000..f475e577 --- /dev/null +++ b/Source/configure/utilities/webAppNodeVersionConverter.ts @@ -0,0 +1,38 @@ +import { JSONPath } from 'jsonpath-plus'; +import * as vscode from 'vscode'; +import { ArmRestClient } from '../clients/azure/armRestClient'; +import { AzureSession } from "../model/models"; +import { Messages } from '../resources/messages'; + +export async function webAppRuntimeNodeVersionConverter(nodeVersion: string, armUri: string, azureSession: AzureSession): Promise { + if (nodeVersion.indexOf('|') >= 0) { + nodeVersion = nodeVersion.split('|')[1]; + } else { + nodeVersion = 'lts'; + } + if (nodeVersion.toLowerCase() === 'lts') { + let versions: string[]; + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: Messages.GettingNodeVersion + }, + async () => { + const resultSelector = "$.value[?(@.name === 'node')].properties.majorVersions[*].runtimeVersion"; + const response = await new ArmRestClient(azureSession).fetchArmData(armUri, 'GET'); + versions = JSONPath({ path: resultSelector, json: response, wrap: false, flatten: true }); + } + ); + let maxVersion = 0; + versions.forEach((version: string) => { + const match = version.match(/(\d+)(-lts|\.\d+)/i); + if (match && match.length > 1) { + maxVersion = Math.max(maxVersion, +match[1]); + } + }); + nodeVersion = maxVersion + '.x'; + } else if (nodeVersion.match(/(\d+)-lts/i)) { + nodeVersion = nodeVersion.replace(/-lts/i, '.x'); + } + return nodeVersion; +} \ No newline at end of file diff --git a/Source/extension.ts b/Source/extension.ts new file mode 100644 index 00000000..56ed3cb5 --- /dev/null +++ b/Source/extension.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. +*--------------------------------------------------------------------------------------------*/ + +import * as path from 'path'; +import * as vscode from 'vscode'; +import { AzureUserInput, callWithTelemetryAndErrorHandling, createTelemetryReporter, IActionContext, registerUIExtensionVariables } from 'vscode-azureextensionui'; +import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient'; +import { activateConfigurePipeline } from './configure/activate'; +import { telemetryHelper } from './configure/helper/telemetryHelper'; +import { extensionVariables } from './configure/model/models'; +import { TelemetryKeys } from './configure/resources/telemetryKeys'; +import { TracePoints } from './configure/resources/tracePoints'; +import * as logger from './logger'; + +const Layer = 'Extension'; + +export async function activate(context: vscode.ExtensionContext) { + extensionVariables.reporter = createTelemetryReporter(context); + registerUiVariables(context); + + await callWithTelemetryAndErrorHandling('azuredeploy.activate', async (activateContext: IActionContext) => { + activateContext.telemetry.properties.isActivationEvent = 'true'; + telemetryHelper.initialize(activateContext, 'activate'); + await telemetryHelper.executeFunctionWithTimeTelemetry( + async () => { + await activateConfigurePipeline(); + }, + TelemetryKeys.ExtensionActivationDuration); + }); + + startLanguageClient(context); + + logger.log('Extension has been activated!', 'ExtensionActivated'); +} + +function startLanguageClient(context: vscode.ExtensionContext) { + try { + // The YAML language server is implemented in node + let serverModule = context.asAbsolutePath(path.join('node_modules', 'yaml-language-server', 'out', 'server', 'src', 'server.js')); + + // The debug options for the server + let debugOptions = { execArgv: ['--nolazy', '--inspect=6009'] }; + + // If the extension is launched in debug mode then the debug server options are used + // Otherwise the run options are used + let serverOptions: ServerOptions = { + run: { module: serverModule, transport: TransportKind.ipc }, + debug: { module: serverModule, transport: TransportKind.ipc, options: debugOptions } + }; + + // Options to control the scope of language client + let clientOptions: LanguageClientOptions = { + documentSelector: [ + { language: "yaml", pattern: "**/.github/workflows/**", scheme: "file" } + ], + synchronize: { + // Synchronize these setting sections with the server + configurationSection: ['yaml', 'http.proxy', 'http.proxyStrictSSL'], + // Notify the server about file changes to YAML files contained in the workspace + fileEvents: [ + vscode.workspace.createFileSystemWatcher('**/*.?(e)y?(a)ml') + ] + } + }; + + // Create the language client and start it + let client = new LanguageClient('yaml', 'YAML Support', serverOptions, clientOptions); + let disposable = client.start(); + + // Push the disposable to the context's subscriptions so that the + // client can be deactivated on extension deactivation + context.subscriptions.push(disposable); + } + catch (error) { + telemetryHelper.logError(Layer, TracePoints.LanguageClientActivationFailed, error); + } +} + +function registerUiVariables(context: vscode.ExtensionContext) { + // Register ui extension variables is required to be done for telemetry to start flowing for extension activation and other events. + // It also facilitates registering command and called events telemetry. + extensionVariables.outputChannel = vscode.window.createOutputChannel('Deploy to Azure'); + context.subscriptions.push(extensionVariables.outputChannel); + extensionVariables.context = context; + extensionVariables.ui = new AzureUserInput(context.globalState); + registerUIExtensionVariables(extensionVariables); +} + +// this method is called when your extension is deactivated +export function deactivate() { +} \ No newline at end of file diff --git a/Source/logger.ts b/Source/logger.ts new file mode 100644 index 00000000..28a70627 --- /dev/null +++ b/Source/logger.ts @@ -0,0 +1,20 @@ +import { extensionVariables } from "./configure/model/models"; + +/*--------------------------------------------------------------------------------------------- +* Copyright (c) Microsoft Corporation. All rights reserved. +* Licensed under the MIT License. +*--------------------------------------------------------------------------------------------*/ + +// TODO: How can we write this to disk too so that we can remotely debug issues? +// TODO: Set env var or something to turn logging on/off? + +export function log(message: string, event?: string){ + let logMessage = `(${new Date().toLocaleString()}) `; + + if (event) { + logMessage += `[${event}] `; + } + + logMessage += `${message}`; + extensionVariables.outputChannel.appendLine(logMessage); +} diff --git a/package-lock.json b/package-lock.json index 2aa9297a..03a07cee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,7999 +1,2088 @@ { "name": "azure-deploy", - "version": "1.2.3", + "version": "0.0.1", "lockfileVersion": 1, "requires": true, "dependencies": { - "@types/anymatch": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@types/anymatch/-/anymatch-1.3.1.tgz", - "integrity": "sha512-/+CRPXpBDpo2RK9C68N3b2cOvO0Cf5B9aPijHsoDQTHivnGSObdOF2BRQOYjojWTDy6nQvMjmqRXIxH55VjxxA==", - "dev": true - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true + "@esbuild/aix-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz", + "integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==", + "dev": true, + "optional": true }, - "@types/fs-extra": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-4.0.5.tgz", - "integrity": "sha512-tIG0GpHum5IFb8Qze/cSv0w/0gNzHB+MUDftTQaxenx46z50g51/MPkNLssLz9+uZLzCDd35bT9qtWOTXZ21Gw==", + "@esbuild/android-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz", + "integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==", "dev": true, - "requires": { - "@types/node": "*" - } + "optional": true }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "@esbuild/android-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz", + "integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==", "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } + "optional": true }, - "@types/js-yaml": { - "version": "3.12.1", - "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.1.tgz", - "integrity": "sha512-SGGAhXLHDx+PK4YLNcNGa6goPf9XRWQNAUUbffkwVGGXIxmDKWyGGL4inzq2sPmExu431Ekb9aEMn9BkPqEYFA==", - "dev": true + "@esbuild/android-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz", + "integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==", + "dev": true, + "optional": true }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "@esbuild/darwin-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz", + "integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==", + "dev": true, + "optional": true }, - "@types/mocha": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-7.0.2.tgz", - "integrity": "sha512-ZvO2tAcjmMi8V/5Z3JsyofMe3hasRcaw88cto5etSVMwVQfeivGAlEYmaQgceUSVYFofVjT+ioHsATjdWcFt1w==", - "dev": true + "@esbuild/darwin-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz", + "integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==", + "dev": true, + "optional": true }, - "@types/mustache": { - "version": "0.8.32", - "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-0.8.32.tgz", - "integrity": "sha512-RTVWV485OOf4+nO2+feurk0chzHkSjkjALiejpHltyuMf/13fGymbbNNFrSKdSSUg1TIwzszXdWsVirxgqYiFA==", - "dev": true + "@esbuild/freebsd-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz", + "integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==", + "dev": true, + "optional": true }, - "@types/node": { - "version": "7.0.43", - "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.43.tgz", - "integrity": "sha512-7scYwwfHNppXvH/9JzakbVxk0o0QUILVk1Lv64GRaxwPuGpnF1QBiwdvhDpLcymb8BpomQL3KYoWKq3wUdDMhQ==", - "dev": true + "@esbuild/freebsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz", + "integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==", + "dev": true, + "optional": true }, - "@types/q": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.0.tgz", - "integrity": "sha512-sWj7AMiG0fYmta6ug1ublLjtj/tqn+CnCZeo7yswR1ykxel0FOWFGdWviTcGSNAMmtLbycDqbg6w98VPFKJmbw==", - "dev": true + "@esbuild/linux-arm": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz", + "integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==", + "dev": true, + "optional": true }, - "@types/source-list-map": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", - "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", - "dev": true + "@esbuild/linux-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz", + "integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==", + "dev": true, + "optional": true }, - "@types/tapable": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/tapable/-/tapable-1.0.5.tgz", - "integrity": "sha512-/gG2M/Imw7cQFp8PGvz/SwocNrmKFjFsm5Pb8HdbHkZ1K8pmuPzOX4VeVoiEecFCVf4CsN1r3/BRvx+6sNqwtQ==", - "dev": true + "@esbuild/linux-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz", + "integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==", + "dev": true, + "optional": true }, - "@types/uglify-js": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@types/uglify-js/-/uglify-js-3.9.1.tgz", - "integrity": "sha512-rdBIeMQyRBOXogop/EYBvSkYFn9D9yGxUa5hagBVG55KIdSUbp22EACJSHCs6kmmfunojAhf7zJH+Ds06/qLaQ==", + "@esbuild/linux-loong64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz", + "integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==", "dev": true, - "requires": { - "source-map": "^0.6.1" - } + "optional": true }, - "@types/underscore": { - "version": "1.8.9", - "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.8.9.tgz", - "integrity": "sha512-vfzZGgZKRFy7KEWcBGfIFk+h6B+thDCLfkD1exMBMRlUsx2icA+J6y4kAbZs/TjSTeY1duw89QUU133TSzr60Q==", - "dev": true + "@esbuild/linux-mips64el": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz", + "integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==", + "dev": true, + "optional": true }, - "@types/webpack": { - "version": "4.41.13", - "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.13.tgz", - "integrity": "sha512-RYmIHOWSxnTTa765N6jJBVE45pd2SYNblEYshVDduLw6RhocazNmRzE5/ytvBD8IkDMH6DI+bcrqxh8NILimBA==", + "@esbuild/linux-ppc64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz", + "integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==", "dev": true, - "requires": { - "@types/anymatch": "*", - "@types/node": "*", - "@types/tapable": "*", - "@types/uglify-js": "*", - "@types/webpack-sources": "*", - "source-map": "^0.6.0" - } + "optional": true }, - "@types/webpack-sources": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", - "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", + "@esbuild/linux-riscv64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz", + "integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==", "dev": true, - "requires": { - "@types/node": "*", - "@types/source-list-map": "*", - "source-map": "^0.6.1" - } + "optional": true }, - "@webassemblyjs/ast": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.7.11.tgz", - "integrity": "sha512-ZEzy4vjvTzScC+SH8RBssQUawpaInUdMTYwYYLh54/s8TuT0gBLuyUnppKsVyZEi876VmmStKsUs28UxPgdvrA==", + "@esbuild/linux-s390x": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz", + "integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==", "dev": true, - "requires": { - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11" - } + "optional": true }, - "@webassemblyjs/floating-point-hex-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.7.11.tgz", - "integrity": "sha512-zY8dSNyYcgzNRNT666/zOoAyImshm3ycKdoLsyDw/Bwo6+/uktb7p4xyApuef1dwEBo/U/SYQzbGBvV+nru2Xg==", - "dev": true + "@esbuild/linux-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz", + "integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-api-error": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.7.11.tgz", - "integrity": "sha512-7r1qXLmiglC+wPNkGuXCvkmalyEstKVwcueZRP2GNC2PAvxbLYwLLPr14rcdJaE4UtHxQKfFkuDFuv91ipqvXg==", - "dev": true + "@esbuild/netbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz", + "integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-buffer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.7.11.tgz", - "integrity": "sha512-MynuervdylPPh3ix+mKZloTcL06P8tenNH3sx6s0qE8SLR6DdwnfgA7Hc9NSYeob2jrW5Vql6GVlsQzKQCa13w==", - "dev": true + "@esbuild/openbsd-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz", + "integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-code-frame": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.7.11.tgz", - "integrity": "sha512-T8ESC9KMXFTXA5urJcyor5cn6qWeZ4/zLPyWeEXZ03hj/x9weSokGNkVCdnhSabKGYWxElSdgJ+sFa9G/RdHNw==", + "@esbuild/sunos-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz", + "integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==", "dev": true, - "requires": { - "@webassemblyjs/wast-printer": "1.7.11" - } + "optional": true }, - "@webassemblyjs/helper-fsm": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-fsm/-/helper-fsm-1.7.11.tgz", - "integrity": "sha512-nsAQWNP1+8Z6tkzdYlXT0kxfa2Z1tRTARd8wYnc/e3Zv3VydVVnaeePgqUzFrpkGUyhUUxOl5ML7f1NuT+gC0A==", - "dev": true + "@esbuild/win32-arm64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz", + "integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-module-context": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-module-context/-/helper-module-context-1.7.11.tgz", - "integrity": "sha512-JxfD5DX8Ygq4PvXDucq0M+sbUFA7BJAv/GGl9ITovqE+idGX+J3QSzJYz+LwQmL7fC3Rs+utvWoJxDb6pmC0qg==", - "dev": true + "@esbuild/win32-ia32": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz", + "integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-wasm-bytecode": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.7.11.tgz", - "integrity": "sha512-cMXeVS9rhoXsI9LLL4tJxBgVD/KMOKXuFqYb5oCJ/opScWpkCMEz9EJtkonaNcnLv2R3K5jIeS4TRj/drde1JQ==", - "dev": true + "@esbuild/win32-x64": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz", + "integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==", + "dev": true, + "optional": true }, - "@webassemblyjs/helper-wasm-section": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.7.11.tgz", - "integrity": "sha512-8ZRY5iZbZdtNFE5UFunB8mmBEAbSI3guwbrsCl4fWdfRiAcvqQpeqd5KHhSWLL5wuxo53zcaGZDBU64qgn4I4Q==", + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11" + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + } + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + } } }, - "@webassemblyjs/ieee754": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.7.11.tgz", - "integrity": "sha512-Mmqx/cS68K1tSrvRLtaV/Lp3NZWzXtOHUW2IvDvl2sihAwJh4ACE0eL6A8FvMyDG9abes3saB6dMimLOs+HMoQ==", - "dev": true, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "requires": { - "@xtuc/ieee754": "^1.2.0" + "debug": "^4.1.1" } }, - "@webassemblyjs/leb128": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.7.11.tgz", - "integrity": "sha512-vuGmgZjjp3zjcerQg+JA+tGOncOnJLWVkt8Aze5eWQLwTQGNgVLcyOTqgSCxWTR4J42ijHbBxnuRaL1Rv7XMdw==", + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@xtuc/long": "4.2.1" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" } }, - "@webassemblyjs/utf8": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.7.11.tgz", - "integrity": "sha512-C6GFkc7aErQIAH+BMrIdVSmW+6HSe20wg57HEC1uqJP8E/xpMjXqQUxkQw07MhNDSDcGpxI9G5JSNOQCqJk4sA==", + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, - "@webassemblyjs/wasm-edit": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.7.11.tgz", - "integrity": "sha512-FUd97guNGsCZQgeTPKdgxJhBXkUbMTY6hFPf2Y4OedXd48H97J+sOY2Ltaq6WGVpIH8o/TGOVNiVz/SbpEMJGg==", + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/helper-wasm-section": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-opt": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "@webassemblyjs/wast-printer": "1.7.11" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" } }, - "@webassemblyjs/wasm-gen": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.7.11.tgz", - "integrity": "sha512-U/KDYp7fgAZX5KPfq4NOupK/BmhDc5Kjy2GIqstMhvvdJRcER/kUsMThpWeRP8BMn4LXaKhSTggIJPOeYHwISA==", + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" - } + "optional": true }, - "@webassemblyjs/wasm-opt": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.7.11.tgz", - "integrity": "sha512-XynkOwQyiRidh0GLua7SkeHvAPXQV/RxsUeERILmAInZegApOUAIJfRuPYe2F7RcjOC9tW3Cb9juPvAC/sCqvg==", + "@playform/build": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@playform/build/-/build-0.0.7.tgz", + "integrity": "sha512-NNV3BhhbzfRKva6R+rd7pCQxF+Bjcxg0kvC/zOSLKvKjgYMsyDdwTRWZF+2K2BuhAIwcmSwoa1zjMJPhYzyYPw==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-buffer": "1.7.11", - "@webassemblyjs/wasm-gen": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11" + "@types/node": "20.12.7", + "commander": "12.0.0", + "deepmerge-ts": "5.1.0", + "esbuild": "0.20.2", + "fast-glob": "3.3.2", + "tsc-alias": "1.8.8", + "typescript": "5.4.5" + }, + "dependencies": { + "commander": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", + "dev": true + }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + } } }, - "@webassemblyjs/wasm-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.7.11.tgz", - "integrity": "sha512-6lmXRTrrZjYD8Ng8xRyvyXQJYUQKYSXhJqXOBLw24rdiXsHAOlvw5PhesjdcaMadU/pyPQOJ5dHreMjBxwnQKg==", + "@playform/document": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@playform/document/-/document-0.0.6.tgz", + "integrity": "sha512-birdfwDtVwbSSi/cyHfQwLaqQG9oVsaJESL8ORu2smJBDqHNPYj/sP5jDyRtIbp3+oaWfsaxM4iXoQtYmzpLGQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-wasm-bytecode": "1.7.11", - "@webassemblyjs/ieee754": "1.7.11", - "@webassemblyjs/leb128": "1.7.11", - "@webassemblyjs/utf8": "1.7.11" + "commander": "12.0.0", + "fast-glob": "3.3.2", + "typedoc": "0.25.12", + "typedoc-plugin-keywords": "1.6.0", + "typedoc-plugin-mdn-links": "3.1.19", + "typedoc-plugin-merge-modules": "5.1.0", + "typedoc-plugin-remove-references": "0.0.6", + "typedoc-plugin-rename-defaults": "0.7.0", + "typedoc-plugin-zod": "1.1.2" + }, + "dependencies": { + "commander": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.0.0.tgz", + "integrity": "sha512-MwVNWlYjDTtOjX5PiD7o5pK0UrFU/OYgcJfjjK4RaHZETNtjJqrZa9Y9ds88+A+f+d5lv+561eZ+yCKoS3gbAA==", + "dev": true + } } }, - "@webassemblyjs/wast-parser": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-parser/-/wast-parser-1.7.11.tgz", - "integrity": "sha512-lEyVCg2np15tS+dm7+JJTNhNWq9yTZvi3qEhAIIOaofcYlUp0UR5/tVqOwa/gXYr3gjwSZqw+/lS9dscyLelbQ==", + "@types/fs-extra": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/floating-point-hex-parser": "1.7.11", - "@webassemblyjs/helper-api-error": "1.7.11", - "@webassemblyjs/helper-code-frame": "1.7.11", - "@webassemblyjs/helper-fsm": "1.7.11", - "@xtuc/long": "4.2.1" + "@types/jsonfile": "*", + "@types/node": "*" } }, - "@webassemblyjs/wast-printer": { - "version": "1.7.11", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.7.11.tgz", - "integrity": "sha512-m5vkAsuJ32QpkdkDOUPGSltrg8Cuk3KBx4YrmAGQwCZPRdUHXxG4phIOuuycLemHFr74sWL9Wthqss4fzdzSwg==", + "@types/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-IO+MJPVhoqz+28h1qLAcBEH2+xHMK6MTyHJc7MTnnYb6wsoLR29POVGJ7LycmVXIqyy/4/2ShP5sUwTXuOwb/w==", "dev": true, "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/wast-parser": "1.7.11", - "@xtuc/long": "4.2.1" + "@types/minimatch": "^5.1.2", + "@types/node": "*" } }, - "@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "@types/js-yaml": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-4.0.9.tgz", + "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, - "@xtuc/long": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.1.tgz", - "integrity": "sha512-FZdkNBDqBRHKQ2MEbSC17xnPFOhZxeJ2YGSfr2BKf3sujG49Qe3bB+rGCwQfIaA7WHnGeGkSijX4FuBCdrzW/g==", + "@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true }, - "acorn": { - "version": "5.7.4", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz", - "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==", + "@types/mustache": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.5.tgz", + "integrity": "sha512-PLwiVvTBg59tGFL/8VpcGvqOu3L4OuveNvPi0EYbWchRdEVP++yRUXJPFl+CApKEq13017/4Nf7aQ5lTtHUNsA==", "dev": true }, - "acorn-dynamic-import": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/acorn-dynamic-import/-/acorn-dynamic-import-3.0.0.tgz", - "integrity": "sha512-zVWV8Z8lislJoOKKqdNMOB+s6+XV5WERty8MnKBeFgwA+19XJjJHs2RP5dzM57FftIs+jQnRToLiWazKr6sSWg==", + "@types/node": { + "version": "20.12.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz", + "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==", "dev": true, "requires": { - "acorn": "^5.0.0" + "undici-types": "~5.26.4" } }, - "adal-node": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/adal-node/-/adal-node-0.1.28.tgz", - "integrity": "sha1-RoxLs+u9lrEnBmn0ucuk4AZepIU=", - "requires": { - "@types/node": "^8.0.47", - "async": ">=0.6.0", - "date-utils": "*", - "jws": "3.x.x", - "request": ">= 2.52.0", - "underscore": ">= 1.3.1", - "uuid": "^3.1.0", - "xmldom": ">= 0.1.x", - "xpath.js": "~1.1.0" - }, - "dependencies": { - "@types/node": { - "version": "8.10.51", - "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.51.tgz", - "integrity": "sha512-cArrlJp3Yv6IyFT/DYe+rlO8o3SIHraALbBW/+CcCYW/a9QucpLI+n2p4sRxAvl2O35TiecpX2heSZtJjvEO+Q==" - } - } + "@types/q": { + "version": "1.5.8", + "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.8.tgz", + "integrity": "sha512-hroOstUScF6zhIi+5+x0dzqrHA1EJi+Irri6b1fxolMTqqHIV/Cg77EtnQcZqZCu8hR3mX2BzIxN4/GzI68Kfw==", + "dev": true }, - "agent-base": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-4.3.0.tgz", - "integrity": "sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg==", - "requires": { - "es6-promisify": "^5.0.0" - } + "@types/underscore": { + "version": "1.11.15", + "resolved": "https://registry.npmjs.org/@types/underscore/-/underscore-1.11.15.tgz", + "integrity": "sha512-HP38xE+GuWGlbSRq9WrZkousaQ7dragtZCruBVMi0oX1migFZavZ3OROKHSkNp/9ouq82zrWtZpg18jFnVN96g==", + "dev": true }, "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, "requires": { - "fast-deep-equal": "^2.0.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", "uri-js": "^4.2.2" } }, - "ajv-errors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", - "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true - }, - "ajv-keywords": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true - }, - "ansi-colors": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", - "integrity": "sha512-LEHHyuhlPY3TmuUYMh2oz89lTShfvgbmzaBcxve9t/9Wuy7Dwf4yoAKcND7KFT1HAQfqZ12qtc+DUrBMeKF9nw==", + "ansi-sequence-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ansi-sequence-parser/-/ansi-sequence-parser-1.1.1.tgz", + "integrity": "sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==", "dev": true }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", - "dev": true + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "anymatch": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-1.3.2.tgz", - "integrity": "sha512-0XNayC8lTHQ2OI8aljNCN3sSx6hsr/1+rlcDAotXJR7C1oZZHCNsfpbKwMjRA3Uqb5tF1Rae2oloTr4xpq+WjA==", + "assert": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/assert/-/assert-2.1.0.tgz", + "integrity": "sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==", "dev": true, "requires": { - "micromatch": "^2.1.5", - "normalize-path": "^2.0.0" + "call-bind": "^1.0.2", + "is-nan": "^1.3.2", + "object-is": "^1.1.5", + "object.assign": "^4.1.4", + "util": "^0.12.5" }, "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { - "remove-trailing-separator": "^1.0.1" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" } } } }, - "applicationinsights": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.4.0.tgz", - "integrity": "sha512-TV8MYb0Kw9uE2cdu4V/UvTKdOABkX2+Fga9iDz0zqV7FLrNXfmAugWZmmdTx4JoynYkln3d5CUHY3oVSUEbfFw==", + "assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true + }, + "available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, "requires": { - "cls-hooked": "^4.2.2", - "continuation-local-storage": "^3.2.1", - "diagnostic-channel": "0.2.0", - "diagnostic-channel-publishers": "^0.3.2" + "possible-typed-array-names": "^1.0.0" } }, - "aproba": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", - "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, - "archiver": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", - "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", + "blakejs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", + "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { - "archiver-utils": "^2.1.0", - "async": "^2.6.3", - "buffer-crc32": "^0.2.1", - "glob": "^7.1.4", - "readable-stream": "^3.4.0", - "tar-stream": "^2.1.0", - "zip-stream": "^2.1.2" - }, - "dependencies": { - "async": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", - "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", - "dev": true, - "requires": { - "lodash": "^4.17.14" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "archiver-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", - "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", - "dev": true, + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "requires": { - "glob": "^7.1.4", - "graceful-fs": "^4.2.0", - "lazystream": "^1.0.0", - "lodash.defaults": "^4.2.0", - "lodash.difference": "^4.5.0", - "lodash.flatten": "^4.4.0", - "lodash.isplainobject": "^4.0.6", - "lodash.union": "^4.6.0", - "normalize-path": "^3.0.0", - "readable-stream": "^2.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" } } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "chai": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.0.tgz", + "integrity": "sha512-kDZ7MZyM6Q1DhR9jy7dalKohXQ2yrlXkk59CR52aRKxJrobmlBNqnFQxX9xOX8w+4mz8SYlKJa/7D7ddltFXCw==", + "dev": true, "requires": { - "sprintf-js": "~1.0.2" + "assertion-error": "^2.0.1", + "check-error": "^2.0.0", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" } }, - "arr-diff": { + "check-error": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-2.0.0.tgz", - "integrity": "sha1-jzuCf5Vai9ZpaX5KQlasPOrjVs8=", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.0.0.tgz", + "integrity": "sha512-tjLAOBHKVxtPoHe/SA7kNOMvhCRdCJ3vETdeY0RuAc9popf+hyaSV6ZEg9hr4cpWF7jmo/JSWEnLDrnijS9Tog==", + "dev": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "requires": { - "arr-flatten": "^1.0.1" + "color-name": "~1.1.4" } }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-uniq": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", - "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", - "dev": true - }, - "array-unique": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.2.1.tgz", - "integrity": "sha1-odl8yvy8JiXMcPrc6zalDFiwGlM=", + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "arrify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", - "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "asn1.js": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", - "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, "requires": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } } } }, - "assert": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/assert/-/assert-1.4.1.tgz", - "integrity": "sha1-mZEtWRg2tab1s0XA8H7vwI/GXZE=", - "dev": true, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", "requires": { - "util": "0.10.3" + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + } } }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "deep-eql": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.1.tgz", + "integrity": "sha512-nwQCf6ne2gez3o1MxWifqkciwt0zhl0LO1/UwVu4uMBuPmflWM4oQ70XMqHqnBJA+nhzncaqL9HVL6KkHJ28lw==", "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "deepmerge-ts": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/deepmerge-ts/-/deepmerge-ts-5.1.0.tgz", + "integrity": "sha512-eS8dRJOckyo9maw9Tu5O5RUi/4inFLrnoLkBe3cPfDMx3WZioXtmOew4TXQaxq7Rhl4xjDtR7c6x8nNTxOvbFw==", "dev": true }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "requires": { - "lodash": "^4.14.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" } }, - "async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, - "async-hook-jl": { - "version": "1.7.6", - "resolved": "https://registry.npmjs.org/async-hook-jl/-/async-hook-jl-1.7.6.tgz", - "integrity": "sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg==", + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "dev": true, "requires": { - "stack-chain": "^1.3.7" + "object-keys": "^1.0.12" } }, - "async-listener": { - "version": "0.6.10", - "resolved": "https://registry.npmjs.org/async-listener/-/async-listener-0.6.10.tgz", - "integrity": "sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw==", + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, "requires": { - "semver": "^5.3.0", - "shimmer": "^1.1.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } + "path-type": "^4.0.0" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "at-least-node": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", - "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "azure-arm-resource": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-7.3.0.tgz", - "integrity": "sha512-2K+ps1Iwa4PBQFwdCn1X8kAVIRLH5M7nlNZtfOWaYd7DXJ131qJpwW8ul6gKZgG7DAI3PBodrGsHFvPdgA+AzQ==", - "requires": { - "ms-rest": "^2.3.3", - "ms-rest-azure": "^2.5.5" - } - }, - "azure-arm-storage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/azure-arm-storage/-/azure-arm-storage-3.2.0.tgz", - "integrity": "sha512-jrew8qgqCiJ66QyjAhQis7AXhK6hp5soypvcxio3g/b1mNJr7RsQ6vD5zBRebcvM+QBGzvv7COiXy/xzibyOgA==", + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "requires": { - "ms-rest": "^2.2.2", - "ms-rest-azure": "^2.3.3" + "get-intrinsic": "^1.2.4" } }, - "azure-arm-website": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/azure-arm-website/-/azure-arm-website-5.7.0.tgz", - "integrity": "sha512-GnwqaelTIhv40YI3Ch8+Q272X6XXWTq99Y1aYWZb1cejSP4gjrWWeppwor4HtjlVU9i9YIvYO91TRjQt8FrHVA==", - "requires": { - "ms-rest": "^2.3.3", - "ms-rest-azure": "^2.5.5" + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==" + }, + "esbuild": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz", + "integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==", + "dev": true, + "requires": { + "@esbuild/aix-ppc64": "0.20.2", + "@esbuild/android-arm": "0.20.2", + "@esbuild/android-arm64": "0.20.2", + "@esbuild/android-x64": "0.20.2", + "@esbuild/darwin-arm64": "0.20.2", + "@esbuild/darwin-x64": "0.20.2", + "@esbuild/freebsd-arm64": "0.20.2", + "@esbuild/freebsd-x64": "0.20.2", + "@esbuild/linux-arm": "0.20.2", + "@esbuild/linux-arm64": "0.20.2", + "@esbuild/linux-ia32": "0.20.2", + "@esbuild/linux-loong64": "0.20.2", + "@esbuild/linux-mips64el": "0.20.2", + "@esbuild/linux-ppc64": "0.20.2", + "@esbuild/linux-riscv64": "0.20.2", + "@esbuild/linux-s390x": "0.20.2", + "@esbuild/linux-x64": "0.20.2", + "@esbuild/netbsd-x64": "0.20.2", + "@esbuild/openbsd-x64": "0.20.2", + "@esbuild/sunos-x64": "0.20.2", + "@esbuild/win32-arm64": "0.20.2", + "@esbuild/win32-ia32": "0.20.2", + "@esbuild/win32-x64": "0.20.2" } }, - "azureintegration-repoanalysis-client-internal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/azureintegration-repoanalysis-client-internal/-/azureintegration-repoanalysis-client-internal-1.0.0.tgz", - "integrity": "sha512-k/VvC8DWQB0tQeWlgDgOX5IVKqoTjfsGb75Cv03b5nr099gqm14cd3KdSwI4Ox0YzlOobxGRn6FNUOkO3Jd6jg==" + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, - "babel-code-frame": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", - "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { - "chalk": "^1.1.3", - "esutils": "^2.0.2", - "js-tokens": "^3.0.2" + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" }, "dependencies": { - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" + "fill-range": "^7.0.1" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - } - } - }, - "babel-runtime": { - "version": "6.26.0", - "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", - "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", - "dev": true, - "requires": { - "core-js": "^2.4.0", - "regenerator-runtime": "^0.11.0" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "is-descriptor": "^1.0.0" + "to-regex-range": "^5.0.1" } }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "is-glob": "^4.0.1" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "is-extglob": "^2.1.1" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "braces": "^3.0.2", + "picomatch": "^2.3.1" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } } } }, - "base64-js": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", - "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==", - "dev": true - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, "requires": { - "tweetnacl": "^0.14.3" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - } + "reusify": "^1.0.4" } }, - "big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true - }, - "binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true - }, - "bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", "dev": true, - "optional": true, "requires": { - "file-uri-to-path": "1.0.0" + "is-callable": "^1.1.3" } }, - "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", "dev": true, "requires": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" } }, - "blakejs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", - "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" + "fs-extra": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", + "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "dependencies": { + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + } + } }, - "bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", "dev": true }, - "bn.js": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.1.tgz", - "integrity": "sha512-IUTD/REb78Z2eodka1QZyyEk66pciRcP6Sroka0aI3tG/iwIdYLrBD62RsubR7vqdt3WyX8p4jxeatzmRSphtA==", + "get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "dependencies": { + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + } } }, - "braces": { - "version": "1.8.5", - "resolved": "https://registry.npmjs.org/braces/-/braces-1.8.5.tgz", - "integrity": "sha1-uneWLhLf+WnWt2cR6RS3N4V79qc=", + "glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", "dev": true, "requires": { - "expand-range": "^1.8.1", - "preserve": "^0.2.0", - "repeat-element": "^1.1.2" + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, - "brorand": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", - "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", - "dev": true - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "browserify-aes": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", - "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "requires": { - "buffer-xor": "^1.0.3", - "cipher-base": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.3", - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" } }, - "browserify-cipher": { + "gopd": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", - "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", - "dev": true, + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "requires": { - "browserify-aes": "^1.0.4", - "browserify-des": "^1.0.0", - "evp_bytestokey": "^1.0.0" + "get-intrinsic": "^1.1.3" } }, - "browserify-des": { + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "has-property-descriptors": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", - "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", - "dev": true, + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "requires": { - "cipher-base": "^1.0.1", - "des.js": "^1.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" + "es-define-property": "^1.0.0" } }, - "browserify-rsa": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", - "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==" + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "randombytes": "^2.0.1" + "has-symbols": "^1.0.3" }, "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true } } }, - "browserify-sign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.1.0.tgz", - "integrity": "sha512-VYxo7cDCeYUoBZ0ZCy4UyEUCP3smyBd4DRQM5nrFS1jJjPJjX7rP3oLRpPoWfkhQfyJ0I9ZbHbKafrFD/SGlrg==", - "dev": true, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "requires": { - "bn.js": "^5.1.1", - "browserify-rsa": "^4.0.1", - "create-hash": "^1.2.0", - "create-hmac": "^1.1.7", - "elliptic": "^6.5.2", - "inherits": "^2.0.4", - "parse-asn1": "^5.1.5", - "readable-stream": "^3.6.0" + "function-bind": "^1.1.2" }, "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" } } }, - "browserify-zlib": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/browserify-zlib/-/browserify-zlib-0.2.0.tgz", - "integrity": "sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==", - "dev": true, - "requires": { - "pako": "~1.0.5" - } + "ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "dev": true }, - "buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "buffer-crc32": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", - "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=", + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk=" + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==" }, - "buffer-from": { + "is-arguments": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", - "dev": true - }, - "buffer-xor": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", - "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", - "dev": true + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } }, - "builtin-modules": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", - "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "dev": true }, - "builtin-status-codes": { + "is-fullwidth-code-point": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", - "integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true }, - "cacache": { - "version": "12.0.4", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-12.0.4.tgz", - "integrity": "sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==", + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", "dev": true, "requires": { - "bluebird": "^3.5.5", - "chownr": "^1.1.1", - "figgy-pudding": "^3.5.1", - "glob": "^7.1.4", - "graceful-fs": "^4.1.15", - "infer-owner": "^1.0.3", - "lru-cache": "^5.1.1", - "mississippi": "^3.0.0", - "mkdirp": "^0.5.1", - "move-concurrently": "^1.0.1", - "promise-inflight": "^1.0.1", - "rimraf": "^2.6.3", - "ssri": "^6.0.1", - "unique-filename": "^1.1.1", - "y18n": "^4.0.0" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } + "has-tostringtag": "^1.0.0" } }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "is-nan": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/is-nan/-/is-nan-1.3.2.tgz", + "integrity": "sha512-E+zBKpQ2t6MEo1VsonYmluk9NxGrbzpeeLC2xIViuO2EjU2xsXsBPwTr3Ykv9l08UYEVEdWeRZNouaZqF6RN0w==", "dev": true, "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } + "call-bind": "^1.0.0", + "define-properties": "^1.1.3" } }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true + "is-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.13.tgz", + "integrity": "sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==", + "dev": true, + "requires": { + "which-typed-array": "^1.1.14" + } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true }, - "chai": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.2.0.tgz", - "integrity": "sha512-XQU3bhBukrOsQCuwZndwGcCVQHyZi53fQ6Ys1Fym7E4olpIqqZZhhoFJoaKVvV17lWQoXYwgWN2nF5crA8J2jw==", + "jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", "dev": true, "requires": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.2", - "deep-eql": "^3.0.1", - "get-func-name": "^2.0.0", - "pathval": "^1.1.0", - "type-detect": "^4.0.5" + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" } }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - } + "argparse": "^2.0.1" } }, - "check-error": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", - "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "chokidar": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-1.7.0.tgz", - "integrity": "sha1-eY5ol3gVHIB2tLNg5e3SjNortGg=", + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "jsonc-parser": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.1.tgz", + "integrity": "sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==" + }, + "jsonpath-plus": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-8.1.0.tgz", + "integrity": "sha512-qVTiuKztFGw0dGhYi3WNqvddx3/SHtyDT0xJaeyz4uP0d1tkpG+0y5uYQ4OcIo1TLAz3PE/qDOW9F0uDt3+CTw==" + }, + "loupe": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.0.tgz", + "integrity": "sha512-qKl+FrLXUhFuHUoDJG7f8P8gEMHq9NFS0c6ghXG1J0rldmZFQZoNVv/vyirE9qwCIhWZDsvEFd1sbFu3GvRQFg==", "dev": true, "requires": { - "anymatch": "^1.3.0", - "async-each": "^1.0.0", - "fsevents": "^1.0.0", - "glob-parent": "^2.0.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^2.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0" + "get-func-name": "^2.0.1" } }, - "chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", "dev": true }, - "chrome-trace-event": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz", - "integrity": "sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ==", - "dev": true, - "requires": { - "tslib": "^1.9.0" - } + "marked": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/marked/-/marked-4.3.0.tgz", + "integrity": "sha512-PRsaiG84bK+AMvxziE/lCFss8juXjNaWzVbN5tXAm4XjeaS9NAHhop+PjQxz2A9h8Q4M/xGmzP8vqNwy6JeK0A==", + "dev": true }, - "cipher-base": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", - "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" + "brace-expansion": "^1.1.7" } }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true + }, + "mock-require": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz", + "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==", "dev": true, "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "get-caller-file": "^1.0.2", + "normalize-path": "^2.1.1" }, "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", "dev": true, "requires": { - "is-descriptor": "^0.1.0" + "remove-trailing-separator": "^1.0.1" } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true } } }, - "clean-webpack-plugin": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz", - "integrity": "sha512-MciirUH5r+cYLGCOL5JX/ZLzOZbVr1ot3Fw+KcvbhUb6PM+yycqd9ZhIlcigQ5gl+XhppNmw3bEFuaaMNyLj3A==", + "mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==" + }, + "mylas": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/mylas/-/mylas-2.1.13.tgz", + "integrity": "sha512-+MrqnJRtxdF+xngFfUUkIMQrUUL0KsxbADUkn23Z/4ibGg192Q+z+CQyiYwvWTsYjJygmMR8+w3ZDa98Zh6ESg==", + "dev": true + }, + "nock": { + "version": "13.5.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.4.tgz", + "integrity": "sha512-yAyTfdeNJGGBFxWdzSKCBYxs5FxLbCg5X5Q4ets974hcQzG1+qCxvIyOo4j2Ry6MUlhWVMX4OoYDefAIIwupjw==", "dev": true, "requires": { - "@types/webpack": "^4.4.31", - "del": "^4.1.1" + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" } }, - "cliui": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", - "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", "dev": true, "requires": { - "string-width": "^3.1.0", - "strip-ansi": "^5.2.0", - "wrap-ansi": "^5.1.0" + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", "dev": true, "requires": { - "ansi-regex": "^4.1.0" + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" } } } }, - "cls-hooked": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/cls-hooked/-/cls-hooked-4.2.2.tgz", - "integrity": "sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw==", + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, "requires": { - "async-hook-jl": "^1.7.6", - "emitter-listener": "^1.0.1", - "semver": "^5.4.1" + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true } } }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } + "pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "plimit-lit": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/plimit-lit/-/plimit-lit-1.6.1.tgz", + "integrity": "sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==", + "dev": true, "requires": { - "delayed-stream": "~1.0.0" + "queue-lit": "^1.5.1" } }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", + "possible-typed-array-names": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", + "integrity": "sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q==", "dev": true }, - "commondir": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", - "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", - "dev": true + "prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "optional": true }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, - "compress-commons": { + "punycode": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", - "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", - "dev": true, - "requires": { - "buffer-crc32": "^0.2.13", - "crc32-stream": "^3.0.1", - "normalize-path": "^3.0.0", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" + }, + "queue-lit": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/queue-lit/-/queue-lit-1.5.2.tgz", + "integrity": "sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==", "dev": true }, - "concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", "requires": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } + "resolve": "^1.1.6" } }, - "console-browserify": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.2.0.tgz", - "integrity": "sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==", + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", "dev": true }, - "constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", - "dev": true + "request-light": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", + "integrity": "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg==" }, - "continuation-local-storage": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz", - "integrity": "sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA==", - "requires": { - "async-listener": "^0.6.0", - "emitter-listener": "^1.1.1" - } + "require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==" }, - "copy-concurrently": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/copy-concurrently/-/copy-concurrently-1.0.5.tgz", - "integrity": "sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==", - "dev": true, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", "requires": { - "aproba": "^1.1.1", - "fs-write-stream-atomic": "^1.0.8", - "iferr": "^0.1.5", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.0" + "path-parse": "^1.0.6" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==", + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", "dev": true }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cpx": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/cpx/-/cpx-1.5.0.tgz", - "integrity": "sha1-GFvgGFEdhycN7czCkxceN2VauI8=", + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, "requires": { - "babel-runtime": "^6.9.2", - "chokidar": "^1.6.0", - "duplexer": "^0.1.1", - "glob": "^7.0.5", - "glob2base": "^0.0.12", - "minimatch": "^3.0.2", - "mkdirp": "^0.5.1", - "resolve": "^1.1.7", - "safe-buffer": "^5.0.1", - "shell-quote": "^1.6.1", - "subarg": "^1.0.0" + "queue-microtask": "^1.2.2" } }, - "crc": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", - "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", - "dev": true, - "requires": { - "buffer": "^5.1.0" - } - }, - "crc32-stream": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", - "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", - "dev": true, - "requires": { - "crc": "^3.4.4", - "readable-stream": "^3.4.0" - } - }, - "create-ecdh": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.3.tgz", - "integrity": "sha512-GbEHQPMOswGpKXM9kCWVrremUcBmjteUaQ01T9rkKCPDXfUHX0IoP9LpHYo2NPFampa4e+/pFDc3jQdxrxQLaw==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "elliptic": "^6.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - } - } - }, - "create-hash": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", - "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.1", - "inherits": "^2.0.1", - "md5.js": "^1.3.4", - "ripemd160": "^2.0.1", - "sha.js": "^2.4.0" - } - }, - "create-hmac": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", - "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", - "dev": true, - "requires": { - "cipher-base": "^1.0.3", - "create-hash": "^1.1.0", - "inherits": "^2.0.1", - "ripemd160": "^2.0.0", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "crypto-browserify": { - "version": "3.12.0", - "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", - "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", - "dev": true, - "requires": { - "browserify-cipher": "^1.0.0", - "browserify-sign": "^4.0.0", - "create-ecdh": "^4.0.0", - "create-hash": "^1.1.0", - "create-hmac": "^1.1.0", - "diffie-hellman": "^5.0.0", - "inherits": "^2.0.1", - "pbkdf2": "^3.0.3", - "public-encrypt": "^4.0.0", - "randombytes": "^2.0.0", - "randomfill": "^1.0.3" - } - }, - "cyclist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", - "integrity": "sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk=", - "dev": true - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "date-utils": { - "version": "1.2.21", - "resolved": "https://registry.npmjs.org/date-utils/-/date-utils-1.2.21.tgz", - "integrity": "sha1-YfsWzcEnSzyayq/+n8ad+HIKK2Q=" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", - "dev": true, - "requires": { - "type-detect": "^4.0.0" - } - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "del": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz", - "integrity": "sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==", - "dev": true, + "semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "requires": { - "@types/glob": "^7.1.1", - "globby": "^6.1.0", - "is-path-cwd": "^2.0.0", - "is-path-in-cwd": "^2.0.0", - "p-map": "^2.0.0", - "pify": "^4.0.1", - "rimraf": "^2.6.3" + "lru-cache": "^6.0.0" }, "dependencies": { - "array-union": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz", - "integrity": "sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk=", - "dev": true, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "requires": { - "array-uniq": "^1.0.1" + "yallist": "^4.0.0" } }, - "globby": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-6.1.0.tgz", - "integrity": "sha1-9abXDoOV4hyFj7BInWTfAkJNUGw=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "dependencies": { - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - } - } + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, - "des.js": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", - "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0" - } - }, - "diagnostic-channel": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz", - "integrity": "sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc=", + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "requires": { - "semver": "^5.3.0" + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" }, "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" } } }, - "diagnostic-channel-publishers": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.3.tgz", - "integrity": "sha512-qIocRYU5TrGUkBlDDxaziAK1+squ8Yf2Ls4HldL3xxb/jzmWO2Enux7CvevNKYmF2kDXZ9HiRqwjPsjk8L+i2Q==" - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "diffie-hellman": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", - "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "requires": { - "bn.js": "^4.1.0", - "miller-rabin": "^4.0.0", - "randombytes": "^2.0.0" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - } - } - }, - "dom-serializer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.1.tgz", - "integrity": "sha512-sK3ujri04WyjwQXVoK4PU3y8ula1stq10GJZpqHIUgoGZdsGzAGu65BnU3d08aTVSvO7mGPZUc0wTEDL+qGE0Q==", - "requires": { - "domelementtype": "^2.0.1", - "entities": "^2.0.0" - }, - "dependencies": { - "domelementtype": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.1.tgz", - "integrity": "sha512-5HOHUDsYZWV8FGWN0Njbr/Rn7f/eWSQi1v7+HsUVwXgn8nWWlL64zKDkS0n8ZmQ3mlWOMuXOnR+7Nx/5tMO5AQ==" - }, - "entities": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz", - "integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw==" - } + "shebang-regex": "^3.0.0" } }, - "domain-browser": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz", - "integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==", + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true }, - "domelementtype": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", - "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w==" - }, - "domhandler": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz", - "integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==", - "requires": { - "domelementtype": "1" - } - }, - "domutils": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz", - "integrity": "sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg==", + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", "requires": { - "dom-serializer": "0", - "domelementtype": "1" + "interpret": "^1.0.0", + "rechoir": "^0.6.2" } }, - "duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=" - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", + "shiki": { + "version": "0.14.7", + "resolved": "https://registry.npmjs.org/shiki/-/shiki-0.14.7.tgz", + "integrity": "sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==", "dev": true, "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { - "safe-buffer": "^5.0.1" + "ansi-sequence-parser": "^1.1.0", + "jsonc-parser": "^3.2.0", + "vscode-oniguruma": "^1.7.0", + "vscode-textmate": "^8.0.0" } }, - "elliptic": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", - "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", - "dev": true, + "side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", "requires": { - "bn.js": "^4.4.0", - "brorand": "^1.0.1", - "hash.js": "^1.0.0", - "hmac-drbg": "^1.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.0" + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" }, "dependencies": { - "bn.js": { - "version": "4.11.9", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", - "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==", - "dev": true + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==" } } }, - "emitter-listener": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/emitter-listener/-/emitter-listener-1.1.2.tgz", - "integrity": "sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ==", - "requires": { - "shimmer": "^1.2.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-4.1.1.tgz", - "integrity": "sha512-98p2zE+rL7/g/DzMHMTF4zZlCgeVdJ7yr6xzEpJRYwFYrGi9ANdn5DnJURg6RpBkyk60XYDnWIv51VfIhfNGuA==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "memory-fs": "^0.5.0", - "tapable": "^1.0.0" - } - }, - "entities": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", - "integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==" - }, - "errno": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", - "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", - "dev": true, - "requires": { - "prr": "~1.0.1" - } - }, - "es-abstract": { - "version": "1.17.6", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", - "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.0", - "is-regex": "^1.1.0", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "es6-promise": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", - "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" - }, - "es6-promisify": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", - "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", - "requires": { - "es6-promise": "^4.0.3" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint-scope": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", - "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==", - "dev": true, - "requires": { - "esrecurse": "^4.1.0", - "estraverse": "^4.1.1" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "esrecurse": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", - "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==", - "dev": true, - "requires": { - "estraverse": "^4.1.0" - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "events": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz", - "integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==", + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true }, - "evp_bytestokey": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", - "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", - "dev": true, - "requires": { - "md5.js": "^1.3.4", - "safe-buffer": "^5.1.1" - } - }, - "expand-brackets": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-0.1.5.tgz", - "integrity": "sha1-3wcoTjQqgHzXM6xa9yQR5YHRF3s=", - "dev": true, - "requires": { - "is-posix-bracket": "^0.1.0" - } - }, - "expand-range": { - "version": "1.8.2", - "resolved": "https://registry.npmjs.org/expand-range/-/expand-range-1.8.2.tgz", - "integrity": "sha1-opnv/TNf4nIeuujiV+x5ZE/IUzc=", - "dev": true, - "requires": { - "fill-range": "^2.1.0" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-0.3.2.tgz", - "integrity": "sha1-Lhj/PS9JqydlzskCPwEdqo2DSaE=", - "dev": true, + "simple-git": { + "version": "3.24.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.24.0.tgz", + "integrity": "sha512-QqAKee9Twv+3k8IFOFfPB2hnk6as6Y6ACUpwCtQvRYBAes23Wv3SZlHVobAzqcE8gfsisCvPw3HGW3HYM+VYYw==", "requires": { - "is-extglob": "^1.0.0" + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" - }, - "figgy-pudding": { - "version": "3.5.2", - "resolved": "https://registry.npmjs.org/figgy-pudding/-/figgy-pudding-3.5.2.tgz", - "integrity": "sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==", + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "dev": true, - "optional": true - }, - "filemanager-webpack-plugin": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/filemanager-webpack-plugin/-/filemanager-webpack-plugin-2.0.5.tgz", - "integrity": "sha512-Yj5XIdKI2AN2r66uZc4MZ/n18SMqe2KKlkAqHHMW1OwveDs2Vc5129CpbFcI73rq/rjqso+2HsxieS7u5sx6XA==", + "tsc-alias": { + "version": "1.8.8", + "resolved": "https://registry.npmjs.org/tsc-alias/-/tsc-alias-1.8.8.tgz", + "integrity": "sha512-OYUOd2wl0H858NvABWr/BoSKNERw3N9GTi3rHPK8Iv4O1UyUXIrTTOAZNHsjlVpXFOhpJBVARI1s+rzwLivN3Q==", "dev": true, "requires": { - "archiver": "^3.0.0", - "cpx": "^1.5.0", - "fs-extra": "^7.0.0", - "make-dir": "^1.1.0", - "mv": "^2.1.1", - "rimraf": "^2.6.2" + "chokidar": "^3.5.3", + "commander": "^9.0.0", + "globby": "^11.0.4", + "mylas": "^2.1.9", + "normalize-path": "^3.0.0", + "plimit-lit": "^1.2.6" }, - "dependencies": { - "fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "filename-regex": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz", - "integrity": "sha1-wcS5vuPglyXdsQa3XB4wH+LxiyY=", - "dev": true - }, - "fill-range": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-2.2.4.tgz", - "integrity": "sha512-cnrcCbj01+j2gTG921VZPnHbjmdAf8oQV/iGeV2kZxGSyfYjjTyY79ErsK1WJWMpw6DaApEX72binqJE+/d+5Q==", - "dev": true, - "requires": { - "is-number": "^2.1.0", - "isobject": "^2.0.0", - "randomatic": "^3.0.0", - "repeat-element": "^1.1.2", - "repeat-string": "^1.5.2" - } - }, - "find-cache-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", - "integrity": "sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==", - "dev": true, - "requires": { - "commondir": "^1.0.1", - "make-dir": "^2.0.0", - "pkg-dir": "^3.0.0" - }, - "dependencies": { - "make-dir": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", - "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", - "dev": true, - "requires": { - "pify": "^4.0.1", - "semver": "^5.6.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - } - } - }, - "find-index": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/find-index/-/find-index-0.1.1.tgz", - "integrity": "sha1-Z101iyyjiS15Whq0cjL4tuLg3eQ=", - "dev": true - }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "dev": true, - "requires": { - "locate-path": "^3.0.0" - } - }, - "flat": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", - "integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==", - "dev": true, - "requires": { - "is-buffer": "~2.0.3" - }, - "dependencies": { - "is-buffer": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", - "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==", - "dev": true - } - } - }, - "flush-write-stream": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.1.1.tgz", - "integrity": "sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "readable-stream": "^2.3.6" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "for-own": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz", - "integrity": "sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4=", - "dev": true, - "requires": { - "for-in": "^1.0.1" - } - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "dev": true - }, - "fs-extra": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz", - "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==", - "requires": { - "at-least-node": "^1.0.0", - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^1.0.0" - }, - "dependencies": { - "jsonfile": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.0.1.tgz", - "integrity": "sha512-jR2b5v7d2vIOust+w3wtFKZIfpC2pnRmFAhAC/BuweZFQR8qZzxH1OyrQ10HmdVYiXWkYUqPVsz91cG7EL2FBg==", - "requires": { - "graceful-fs": "^4.1.6", - "universalify": "^1.0.0" - } - }, - "universalify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-1.0.0.tgz", - "integrity": "sha512-rb6X1W158d7pRQBg5gkR8uPaSfiids68LTJQYOtEUhoJUWBdaQHsuT/EUduxXYxcrt4r5PJ4fuHW1MHT6p0qug==" - } - } - }, - "fs-write-stream-atomic": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", - "integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "iferr": "^0.1.5", - "imurmurhash": "^0.1.4", - "readable-stream": "1 || 2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "dev": true, - "optional": true, - "requires": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "get-func-name": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", - "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-base": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/glob-base/-/glob-base-0.3.0.tgz", - "integrity": "sha1-27Fk9iIbHAscz4Kuoyi0l98Oo8Q=", - "dev": true, - "requires": { - "glob-parent": "^2.0.0", - "is-glob": "^2.0.0" - } - }, - "glob-parent": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-2.0.0.tgz", - "integrity": "sha1-gTg9ctsFT8zPUzbaqQLxgvbtuyg=", - "dev": true, - "requires": { - "is-glob": "^2.0.0" - } - }, - "glob2base": { - "version": "0.0.12", - "resolved": "https://registry.npmjs.org/glob2base/-/glob2base-0.0.12.tgz", - "integrity": "sha1-nUGbPijxLoOjYhZKJ3BVkiycDVY=", - "dev": true, - "requires": { - "find-index": "^0.1.1" - } - }, - "graceful-fs": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", - "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hash-base": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", - "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", - "dev": true, - "requires": { - "inherits": "^2.0.4", - "readable-stream": "^3.6.0", - "safe-buffer": "^5.2.0" - }, - "dependencies": { - "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "hash.js": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", - "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", - "dev": true, - "requires": { - "inherits": "^2.0.3", - "minimalistic-assert": "^1.0.1" - } - }, - "he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" - }, - "hmac-drbg": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", - "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", - "dev": true, - "requires": { - "hash.js": "^1.0.3", - "minimalistic-assert": "^1.0.0", - "minimalistic-crypto-utils": "^1.0.1" - } - }, - "html-to-text": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-4.0.0.tgz", - "integrity": "sha512-QQl5EEd97h6+3crtgBhkEAO6sQnZyDff8DAeJzoSkOc1Dqe1UvTUZER0B+KjBe6fPZqq549l2VUhtracus3ndA==", - "requires": { - "he": "^1.0.0", - "htmlparser2": "^3.9.2", - "lodash": "^4.17.4", - "optimist": "^0.6.1" - } - }, - "htmlparser2": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", - "integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==", - "requires": { - "domelementtype": "^1.3.1", - "domhandler": "^2.3.0", - "domutils": "^1.5.1", - "entities": "^1.1.1", - "inherits": "^2.0.1", - "readable-stream": "^3.1.1" - } - }, - "http-proxy-agent": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz", - "integrity": "sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg==", - "requires": { - "agent-base": "4", - "debug": "3.1.0" - } - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "https-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", - "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", - "dev": true - }, - "https-proxy-agent": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz", - "integrity": "sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg==", - "requires": { - "agent-base": "^4.3.0", - "debug": "^3.1.0" - } - }, - "ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "dev": true - }, - "iferr": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", - "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", - "dev": true - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "dev": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, - "is-callable": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", - "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", - "dev": true - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-dotfile": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-dotfile/-/is-dotfile-1.0.3.tgz", - "integrity": "sha1-pqLzL/0t+wT1yiXs0Pa4PPeYoeE=", - "dev": true - }, - "is-equal-shallow": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz", - "integrity": "sha1-IjgJj8Ih3gvPpdnqxMRdY4qhxTQ=", - "dev": true, - "requires": { - "is-primitive": "^2.0.0" - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-1.0.0.tgz", - "integrity": "sha1-rEaBd8SUNAWgkvyPKXYMb/xiBsA=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-glob": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-2.0.1.tgz", - "integrity": "sha1-0Jb5JqPe1WAPP9/ZEZjLCIjC2GM=", - "dev": true, - "requires": { - "is-extglob": "^1.0.0" - } - }, - "is-number": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-2.1.0.tgz", - "integrity": "sha1-Afy7s5NGOlSPL0ZszhbezknbkI8=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "is-path-cwd": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz", - "integrity": "sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==", - "dev": true - }, - "is-path-in-cwd": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz", - "integrity": "sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==", - "dev": true, - "requires": { - "is-path-inside": "^2.1.0" - } - }, - "is-path-inside": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-2.1.0.tgz", - "integrity": "sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==", - "dev": true, - "requires": { - "path-is-inside": "^1.0.2" - } - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "is-posix-bracket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz", - "integrity": "sha1-MzTceXdDaOkvAW5vvAqI9c1ua8Q=", - "dev": true - }, - "is-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-primitive/-/is-primitive-2.0.0.tgz", - "integrity": "sha1-IHurkWOEmcB7Kt8kCkGochADRXU=", - "dev": true - }, - "is-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", - "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", - "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, - "js-tokens": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", - "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } - } - }, - "jsonc-parser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.2.1.tgz", - "integrity": "sha512-o6/yDBYccGvTz1+QFevz6l6OBZ2+fMVu2JZ9CIhzsYRX4mjaK5IyX9eldUdCmga16zlgQxyrj5pt9kzuj2C02w==" - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsonpath-plus": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/jsonpath-plus/-/jsonpath-plus-3.0.0.tgz", - "integrity": "sha512-WQwgWEBgn+SJU1tlDa/GiY5/ngRpa9yrSj8n4BYPHcwoxTDaMEaYCHMOn42hIHHDd3CrUoRr3+HpsK0hCKoxzA==" - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { - "buffer-equal-constant-time": "1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - }, - "lazystream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", - "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", - "dev": true, - "requires": { - "readable-stream": "^2.0.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "loader-runner": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-2.4.0.tgz", - "integrity": "sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==", - "dev": true - }, - "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", - "dev": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^1.0.1" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "dev": true, - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lodash": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz", - "integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==" - }, - "lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=", - "dev": true - }, - "lodash.difference": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", - "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=", - "dev": true - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "dev": true - }, - "lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", - "dev": true - }, - "lodash.set": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", - "integrity": "sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=", - "dev": true - }, - "lodash.union": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", - "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=", - "dev": true - }, - "log-symbols": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz", - "integrity": "sha512-dSkNGuI7iG3mfvDzUuYZyvk5dD9ocYCYzNU6CYDE6+Xqd+gwme6Z00NS3dUh8mq/73HaEtT7m6W+yUPtU6BZnQ==", - "dev": true, - "requires": { - "chalk": "^2.4.2" - } - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "requires": { - "yallist": "^3.0.2" - } - }, - "make-dir": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", - "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", - "dev": true, - "requires": { - "pify": "^3.0.0" - }, - "dependencies": { - "pify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", - "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", - "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", - "dev": true - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "math-random": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/math-random/-/math-random-1.0.4.tgz", - "integrity": "sha512-rUxjysqif/BZQH2yhd5Aaq7vXMSx9NdEsQcyA07uEzIvxgI7zIr33gGsh+RU0/XjmQpCW7RsVof1vlkvQVCK5A==", - "dev": true - }, - "md5.js": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", - "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1", - "safe-buffer": "^5.1.2" - } - }, - "memory-fs": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.5.0.tgz", - "integrity": "sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==", - "dev": true, - "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "micromatch": { - "version": "2.3.11", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-2.3.11.tgz", - "integrity": "sha1-hmd8l9FyCzY0MdBNDRUpO9OMFWU=", - "dev": true, - "requires": { - "arr-diff": "^2.0.0", - "array-unique": "^0.2.1", - "braces": "^1.8.2", - "expand-brackets": "^0.1.4", - "extglob": "^0.3.1", - "filename-regex": "^2.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.1", - "kind-of": "^3.0.2", - "normalize-path": "^2.0.1", - "object.omit": "^2.0.0", - "parse-glob": "^3.0.4", - "regex-cache": "^0.4.2" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "miller-rabin": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", - "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", - "dev": true, - "requires": { - "bn.js": "^4.0.0", - "brorand": "^1.0.1" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - } - } - }, - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - } - }, - "minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true - }, - "minimalistic-crypto-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", - "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" - }, - "mississippi": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mississippi/-/mississippi-3.0.0.tgz", - "integrity": "sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==", - "dev": true, - "requires": { - "concat-stream": "^1.5.0", - "duplexify": "^3.4.2", - "end-of-stream": "^1.1.0", - "flush-write-stream": "^1.0.0", - "from2": "^2.1.0", - "parallel-transform": "^1.1.0", - "pump": "^3.0.0", - "pumpify": "^1.3.3", - "stream-each": "^1.1.0", - "through2": "^2.0.0" - } - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-7.2.0.tgz", - "integrity": "sha512-O9CIypScywTVpNaRrCAgoUnJgozpIofjKUYmJhiCIJMiuYnLI6otcb1/kpW9/n/tJODHGZ7i8aLQoDVsMtOKQQ==", - "dev": true, - "requires": { - "ansi-colors": "3.2.3", - "browser-stdout": "1.3.1", - "chokidar": "3.3.0", - "debug": "3.2.6", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "find-up": "3.0.0", - "glob": "7.1.3", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "3.13.1", - "log-symbols": "3.0.0", - "minimatch": "3.0.4", - "mkdirp": "0.5.5", - "ms": "2.1.1", - "node-environment-flags": "1.0.6", - "object.assign": "4.1.0", - "strip-json-comments": "2.0.1", - "supports-color": "6.0.0", - "which": "1.3.1", - "wide-align": "1.1.3", - "yargs": "13.3.2", - "yargs-parser": "13.1.2", - "yargs-unparser": "1.6.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "binary-extensions": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.1.0.tgz", - "integrity": "sha512-1Yj8h9Q+QDF5FzhMs/c9+6UntbD5MkRfRwac8DoEm9ZfUBZ7tZ55YcGVAzEe4bXsdQHEk+s9S5wsOKVdZrw0tQ==", - "dev": true - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chokidar": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.3.0.tgz", - "integrity": "sha512-dGmKLDdT3Gdl7fBUe8XK+gAtGmzy5Fn0XkkWQuYxGIgWVPPse2CxFA5mtrlD0TOHaHjEUqkWNyP1XdHoJES/4A==", - "dev": true, - "requires": { - "anymatch": "~3.1.1", - "braces": "~3.0.2", - "fsevents": "~2.1.1", - "glob-parent": "~5.1.0", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.2.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "requires": { - "binary-extensions": "^2.0.0" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "ms": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", - "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==", - "dev": true - }, - "readdirp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.2.0.tgz", - "integrity": "sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ==", - "dev": true, - "requires": { - "picomatch": "^2.0.4" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true - }, - "supports-color": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", - "integrity": "sha512-on9Kwidc1IUQo+bQdhi8+Tijpo0e1SS6RoGo2guUwn5vdaxw8RXOF9Vb2ws+ihWOmh4JnCJOvaziZWP1VABaLg==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } - } - }, - "mock-require": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/mock-require/-/mock-require-3.0.3.tgz", - "integrity": "sha512-lLzfLHcyc10MKQnNUCv7dMcoY/2Qxd6wJfbqCcVk3LDb8An4hF6ohk5AztrvgKhJCqj36uyzi/p5se+tvyD+Wg==", - "dev": true, - "requires": { - "get-caller-file": "^1.0.2", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "move-concurrently": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", - "integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", - "dev": true, - "requires": { - "aproba": "^1.1.1", - "copy-concurrently": "^1.0.0", - "fs-write-stream-atomic": "^1.0.8", - "mkdirp": "^0.5.1", - "rimraf": "^2.5.4", - "run-queue": "^1.0.3" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "ms-rest": { - "version": "2.5.3", - "resolved": "https://registry.npmjs.org/ms-rest/-/ms-rest-2.5.3.tgz", - "integrity": "sha512-p0CnzrTzEkS8UTEwgCqT2O5YVK9E8KGBBlJVm3hFtMZvf0dmncKYXWFPyUa4PAsfBL7h4jfu39tOIFTu6exntg==", - "requires": { - "duplexer": "^0.1.1", - "is-buffer": "^1.1.6", - "is-stream": "^1.1.0", - "moment": "^2.21.0", - "request": "^2.88.0", - "through": "^2.3.8", - "tunnel": "0.0.5", - "uuid": "^3.2.1" - } - }, - "ms-rest-azure": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/ms-rest-azure/-/ms-rest-azure-2.6.0.tgz", - "integrity": "sha512-J6386a9krZ4VtU7CRt+Ypgo9RGf8+d3gjMBkH7zbkM4zzkhbbMOYiPRaZ+bHZcfihkKLlktTgA6rjshTjF329A==", - "requires": { - "adal-node": "^0.1.28", - "async": "2.6.0", - "moment": "^2.22.2", - "ms-rest": "^2.3.2", - "request": "^2.88.0", - "uuid": "^3.2.1" - } - }, - "mustache": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mustache/-/mustache-3.0.1.tgz", - "integrity": "sha512-jFI/4UVRsRYdUbuDTKT7KzfOp7FiD5WzYmmwNwXyUVypC0xjoTL78Fqc0jHUPIvvGD+6DQSPHIt1NE7D1ArsqA==" - }, - "mv": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz", - "integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=", - "dev": true, - "requires": { - "mkdirp": "~0.5.1", - "ncp": "~2.0.0", - "rimraf": "~2.4.0" - }, - "dependencies": { - "glob": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz", - "integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=", - "dev": true, - "requires": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "rimraf": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz", - "integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=", - "dev": true, - "requires": { - "glob": "^6.0.1" - } - } - } - }, - "nan": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", - "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==", - "dev": true, - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "ncp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", - "integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M=", - "dev": true - }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", - "dev": true - }, - "nock": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/nock/-/nock-13.0.2.tgz", - "integrity": "sha512-Wm8H22iT3UKPDf138tmgJ0NRfCLd9f2LByki9T2mGHnB66pEqvJh3gV/up1ZufZF24n7/pDYyLGybdqOzF3JIw==", - "dev": true, - "requires": { - "debug": "^4.1.0", - "json-stringify-safe": "^5.0.1", - "lodash.set": "^4.3.2", - "propagate": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "node-environment-flags": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", - "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", - "dev": true, - "requires": { - "object.getownpropertydescriptors": "^2.0.3", - "semver": "^5.7.0" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "node-libs-browser": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/node-libs-browser/-/node-libs-browser-2.2.1.tgz", - "integrity": "sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==", - "dev": true, - "requires": { - "assert": "^1.1.1", - "browserify-zlib": "^0.2.0", - "buffer": "^4.3.0", - "console-browserify": "^1.1.0", - "constants-browserify": "^1.0.0", - "crypto-browserify": "^3.11.0", - "domain-browser": "^1.1.1", - "events": "^3.0.0", - "https-browserify": "^1.0.0", - "os-browserify": "^0.3.0", - "path-browserify": "0.0.1", - "process": "^0.11.10", - "punycode": "^1.2.4", - "querystring-es3": "^0.2.0", - "readable-stream": "^2.3.3", - "stream-browserify": "^2.0.1", - "stream-http": "^2.7.2", - "string_decoder": "^1.0.0", - "timers-browserify": "^2.0.4", - "tty-browserify": "0.0.0", - "url": "^0.11.0", - "util": "^0.11.0", - "vm-browserify": "^1.0.1" - }, - "dependencies": { - "buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "dev": true, - "requires": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", - "dev": true - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - }, - "dependencies": { - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "util": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz", - "integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==", - "dev": true, - "requires": { - "inherits": "2.0.3" - }, - "dependencies": { - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", - "dev": true - } - } - } - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "object-inspect": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", - "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.getownpropertydescriptors": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz", - "integrity": "sha512-Z53Oah9A3TdLoblT7VKJaTDdXdT+lQO+cNpKVnya5JDe9uLvzu1YyY1yFDFrcxrlRgWrEFH0jJtD/IbuwjcEVg==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "object.omit": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-2.0.1.tgz", - "integrity": "sha1-Gpx0SCnznbuFjHbKNXmuKlTr0fo=", - "dev": true, - "requires": { - "for-own": "^0.1.4", - "is-extendable": "^0.1.1" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - }, - "dependencies": { - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "opn": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/opn/-/opn-6.0.0.tgz", - "integrity": "sha512-I9PKfIZC+e4RXZ/qr1RhgyCnGgYX0UEIlXgWnCOVACIvFgaC9rz6Won7xbdhoHrd8IIhV7YEpHjreNUNkqCGkQ==", - "requires": { - "is-wsl": "^1.1.0" - } - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "~0.0.1", - "wordwrap": "~0.0.2" - } - }, - "os-browserify": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", - "integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "dev": true, - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-map": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-2.1.0.tgz", - "integrity": "sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==", - "dev": true - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "pako": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", - "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", - "dev": true - }, - "parallel-transform": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/parallel-transform/-/parallel-transform-1.2.0.tgz", - "integrity": "sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==", - "dev": true, - "requires": { - "cyclist": "^1.0.1", - "inherits": "^2.0.3", - "readable-stream": "^2.1.5" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "parse-asn1": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.5.tgz", - "integrity": "sha512-jkMYn1dcJqF6d5CpU689bq7w/b5ALS9ROVSpQDPrZsqqesUJii9qutvoT5ltGedNXMO2e16YUWIghG9KxaViTQ==", - "dev": true, - "requires": { - "asn1.js": "^4.0.0", - "browserify-aes": "^1.0.0", - "create-hash": "^1.1.0", - "evp_bytestokey": "^1.0.0", - "pbkdf2": "^3.0.3", - "safe-buffer": "^5.1.1" - } - }, - "parse-glob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/parse-glob/-/parse-glob-3.0.4.tgz", - "integrity": "sha1-ssN2z7EfNVE7rdFz7wu246OIORw=", - "dev": true, - "requires": { - "glob-base": "^0.3.0", - "is-dotfile": "^1.0.0", - "is-extglob": "^1.0.0", - "is-glob": "^2.0.0" - } - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-browserify": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-0.0.1.tgz", - "integrity": "sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==", - "dev": true - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-is-inside": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", - "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - }, - "pathval": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", - "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", - "dev": true - }, - "pbkdf2": { - "version": "3.0.17", - "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.0.17.tgz", - "integrity": "sha512-U/il5MsrZp7mGg3mSQfn742na2T+1/vHDCG5/iTI3X9MKUuYUZVLQhyRsg06mCgDBTd57TxzgZt7P+fYfjRLtA==", - "dev": true, - "requires": { - "create-hash": "^1.1.2", - "create-hmac": "^1.1.4", - "ripemd160": "^2.0.1", - "safe-buffer": "^5.0.1", - "sha.js": "^2.4.8" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true - }, - "pify": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", - "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", - "dev": true - }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", - "dev": true, - "requires": { - "pinkie": "^2.0.0" - } - }, - "pkg-dir": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", - "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", - "dev": true, - "requires": { - "find-up": "^3.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "preserve": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz", - "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks=", - "dev": true - }, - "prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", - "optional": true - }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha1-czIwDoQBYb2j5podHZGn1LwW8YI=", - "dev": true - }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true - }, - "promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", - "dev": true - }, - "propagate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", - "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", - "dev": true - }, - "prr": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", - "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=", - "dev": true - }, - "psl": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.3.0.tgz", - "integrity": "sha512-avHdspHO+9rQTLbv1RO+MPYeP/SzsCoxofjVnHanETfQhTJrmB0HlDoW+EiN/R+C0BZ+gERab9NY0lPN2TxNag==" - }, - "public-encrypt": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", - "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", - "dev": true, - "requires": { - "bn.js": "^4.1.0", - "browserify-rsa": "^4.0.0", - "create-hash": "^1.1.0", - "parse-asn1": "^5.0.0", - "randombytes": "^2.0.1", - "safe-buffer": "^5.1.2" - }, - "dependencies": { - "bn.js": { - "version": "4.11.8", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", - "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==", - "dev": true - } - } - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "dev": true, - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" - }, - "dependencies": { - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, - "q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=" - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" - }, - "querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=", - "dev": true - }, - "querystring-es3": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz", - "integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=", - "dev": true - }, - "querystringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.1.1.tgz", - "integrity": "sha512-w7fLxIRCRT7U8Qu53jQnJyPkYZIaR4n5151KMfcJlO/A9397Wxb1amJvROTK6TOnp7PfoAmg/qXiNHI+08jRfA==", - "dev": true - }, - "randomatic": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/randomatic/-/randomatic-3.1.1.tgz", - "integrity": "sha512-TuDE5KxZ0J461RVjrJZCJc+J+zCkTb1MbH9AQUq68sMhOMcy9jLcb3BrZKgp9q9Ncltdg4QVqWrH02W2EFFVYw==", - "dev": true, - "requires": { - "is-number": "^4.0.0", - "kind-of": "^6.0.0", - "math-random": "^1.0.1" - }, - "dependencies": { - "is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "requires": { - "safe-buffer": "^5.1.0" - } - }, - "randomfill": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", - "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", - "dev": true, - "requires": { - "randombytes": "^2.0.5", - "safe-buffer": "^5.1.0" - } - }, - "readable-stream": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.4.0.tgz", - "integrity": "sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "regenerator-runtime": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", - "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==", - "dev": true - }, - "regex-cache": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/regex-cache/-/regex-cache-0.4.4.tgz", - "integrity": "sha512-nVIZwtCjkC9YgvWkpM55B5rBhBYRZhAaJbgcFYXXsHnbZ9UZI9nnVWYZpBlCqv9ho2eZryPnWrZGsOdPwVWXWQ==", - "dev": true, - "requires": { - "is-equal-shallow": "^0.1.3" - } - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.0", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.4.3", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - } - }, - "request-light": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.2.5.tgz", - "integrity": "sha512-eBEh+GzJAftUnex6tcL6eV2JCifY0+sZMIUpUPOVXbs2nV5hla4ZMmO3icYKGuGVuQ2zHE9evh4OrRcH4iyYYw==", - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.3", - "vscode-nls": "^4.1.1" - }, - "dependencies": { - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=", - "dev": true - }, - "resolve": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", - "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", - "dev": true, - "requires": { - "path-parse": "^1.0.6" - } - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "ripemd160": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", - "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", - "dev": true, - "requires": { - "hash-base": "^3.0.0", - "inherits": "^2.0.1" - } - }, - "run-queue": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", - "integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", - "dev": true, - "requires": { - "aproba": "^1.1.1" - } - }, - "safe-buffer": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", - "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" - }, - "schema-utils": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-1.0.0.tgz", - "integrity": "sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==", - "dev": true, - "requires": { - "ajv": "^6.1.0", - "ajv-errors": "^1.0.0", - "ajv-keywords": "^3.1.0" - } - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==" - }, - "serialize-javascript": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-2.1.2.tgz", - "integrity": "sha512-rs9OggEUF0V4jUSecXazOYsLfu7OGK2qIn3c7IPBiffz32XniEp/TX9Xmc9LQfK2nQ2QKHvZ2oygKUGU0lG4jQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "setimmediate": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", - "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", - "dev": true - }, - "sha.js": { - "version": "2.4.11", - "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", - "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", - "dev": true, - "requires": { - "inherits": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "shell-quote": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.7.2.tgz", - "integrity": "sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg==", - "dev": true - }, - "shelljs": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", - "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=" - }, - "shimmer": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", - "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" - }, - "simple-git": { - "version": "1.110.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-1.110.0.tgz", - "integrity": "sha512-UYY0rQkknk0P5eb+KW+03F4TevZ9ou0H+LoGaj7iiVgpnZH4wdj/HTViy/1tNNkmIPcmtxuBqXWiYt2YwlRKOQ==", - "requires": { - "debug": "^4.0.1" - }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" - } - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "requires": { - "kind-of": "^3.2.0" - } - }, - "source-list-map": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", - "integrity": "sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==", - "dev": true - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" - }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "dependencies": { - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - } - } - }, - "ssri": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-6.0.1.tgz", - "integrity": "sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA==", - "dev": true, - "requires": { - "figgy-pudding": "^3.5.1" - } - }, - "stack-chain": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/stack-chain/-/stack-chain-1.3.7.tgz", - "integrity": "sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU=" - }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "stream-browserify": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-2.0.2.tgz", - "integrity": "sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==", - "dev": true, - "requires": { - "inherits": "~2.0.1", - "readable-stream": "^2.0.2" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-each": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/stream-each/-/stream-each-1.2.3.tgz", - "integrity": "sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "stream-shift": "^1.0.0" - } - }, - "stream-http": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz", - "integrity": "sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==", - "dev": true, - "requires": { - "builtin-status-codes": "^3.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.3.6", - "to-arraybuffer": "^1.0.0", - "xtend": "^4.0.0" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } - } - }, - "string.prototype.trimend": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", - "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string.prototype.trimstart": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", - "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", - "dev": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "subarg": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", - "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", - "dev": true, - "requires": { - "minimist": "^1.1.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } - } - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "tapable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.3.tgz", - "integrity": "sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==", - "dev": true - }, - "tar-stream": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz", - "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==", - "dev": true, - "requires": { - "bl": "^4.0.1", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - } - }, - "terser": { - "version": "4.6.13", - "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.13.tgz", - "integrity": "sha512-wMvqukYgVpQlymbnNbabVZbtM6PN63AzqexpwJL8tbh/mRT9LE5o+ruVduAGL7D6Fpjl+Q+06U5I9Ul82odAhw==", - "dev": true, - "requires": { - "commander": "^2.20.0", - "source-map": "~0.6.1", - "source-map-support": "~0.5.12" - }, - "dependencies": { - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true - } - } - }, - "terser-webpack-plugin": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-1.4.3.tgz", - "integrity": "sha512-QMxecFz/gHQwteWwSo5nTc6UaICqN1bMedC5sMtUc7y3Ha3Q8y6ZO0iCR8pq4RJC8Hjf0FEPEHZqcMB/+DFCrA==", - "dev": true, - "requires": { - "cacache": "^12.0.2", - "find-cache-dir": "^2.1.0", - "is-wsl": "^1.1.0", - "schema-utils": "^1.0.0", - "serialize-javascript": "^2.1.2", - "source-map": "^0.6.1", - "terser": "^4.1.2", - "webpack-sources": "^1.4.0", - "worker-farm": "^1.7.0" - } - }, - "through": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", - "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" - }, - "through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "dev": true, - "requires": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - }, - "dependencies": { - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - } - } - } - }, - "timers-browserify": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/timers-browserify/-/timers-browserify-2.0.11.tgz", - "integrity": "sha512-60aV6sgJ5YEbzUdn9c8kYGIqOubPoUdqQCul3SBAsRCZ40s6Y5cMcrW4dt3/k/EsbLVJNl9n6Vz3fTc+k2GeKQ==", - "dev": true, - "requires": { - "setimmediate": "^1.0.4" - } - }, - "to-arraybuffer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", - "integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - } - } - } - }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "^1.1.24", - "punycode": "^1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, - "ts-loader": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-5.4.5.tgz", - "integrity": "sha512-XYsjfnRQCBum9AMRZpk2rTYSVpdZBpZK+kDh0TeT3kxmQNBDVIeUjdPjY5RZry4eIAb8XHc4gYSUiUWPYvzSRw==", - "dev": true, - "requires": { - "chalk": "^2.3.0", - "enhanced-resolve": "^4.0.0", - "loader-utils": "^1.0.2", - "micromatch": "^3.1.4", - "semver": "^5.0.1" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "ts-node": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-7.0.1.tgz", - "integrity": "sha512-BVwVbPJRspzNh2yfslyT1PSbl5uIk03EZlb493RKHN4qej/D06n1cEhjlOJG69oFsE7OT8XjpTUcYf6pKTLMhw==", - "dev": true, - "requires": { - "arrify": "^1.0.0", - "buffer-from": "^1.1.0", - "diff": "^3.1.0", - "make-error": "^1.1.1", - "minimist": "^1.2.0", - "mkdirp": "^0.5.1", - "source-map-support": "^0.5.6", - "yn": "^2.0.0" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } - } - }, - "tslib": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", - "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", - "dev": true - }, - "tslint": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.8.0.tgz", - "integrity": "sha1-H0mtWy53x2w69N3K5VKuTjYS6xM=", - "dev": true, - "requires": { - "babel-code-frame": "^6.22.0", - "builtin-modules": "^1.1.1", - "chalk": "^2.1.0", - "commander": "^2.9.0", - "diff": "^3.2.0", - "glob": "^7.1.1", - "minimatch": "^3.0.4", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.7.1", - "tsutils": "^2.12.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "tslint-microsoft-contrib": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", - "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", - "dev": true, - "requires": { - "tsutils": "^2.27.2 <2.29.0" - }, - "dependencies": { - "tsutils": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", - "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tty-browserify": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", - "integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", - "dev": true - }, - "tunnel": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.5.tgz", - "integrity": "sha512-gj5sdqherx4VZKMcBA4vewER7zdK25Td+z1npBqpbDys4eJrLx+SlYjJvq1bDXs2irkuJM5pf8ktaEQVipkrbA==" - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", - "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" - }, - "tweetsodium": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/tweetsodium/-/tweetsodium-0.0.4.tgz", - "integrity": "sha512-nTYzcaSmpd2vcfI8DriuO01l+jIF8TmjNxjFPRyJkdzi5IdJbUstT3G1rk8V9YkzrucNSWoSrA9Z5Yp3RW2cgw==", - "requires": { - "blakejs": "^1.1.0", - "tweetnacl": "^1.0.1" - } - }, - "type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "typed-rest-client": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.0.7.tgz", - "integrity": "sha512-0u+4yiprNuCoXzWllWuDB81i5Riyg0nwrMFs9RczRjU0ZzIWG4lodtXNxoBL19Jb9I8qVN/VTBG7x+mR2kvq+A==", - "requires": { - "tunnel": "0.0.4", - "underscore": "1.8.3" - }, - "dependencies": { - "tunnel": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.4.tgz", - "integrity": "sha1-LTeFoVjBdMmhbcLARuxfxfF0IhM=" - }, - "underscore": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", - "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" - } - } - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typescript": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.1.tgz", - "integrity": "sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==", - "dev": true - }, - "typescript-tslint-plugin": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/typescript-tslint-plugin/-/typescript-tslint-plugin-0.5.5.tgz", - "integrity": "sha512-tR5igNQP+6FhxaPJYRlUBVsEl0n5cSuXRbg7L1y80mL4B1jUHb8uiIcbQBJ9zWyypJEdFYFUccpXxvMwZR8+AA==", - "dev": true, - "requires": { - "minimatch": "^3.0.4", - "mock-require": "^3.0.3", - "vscode-languageserver": "^5.2.1" - }, - "dependencies": { - "vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", - "dev": true - }, - "vscode-languageserver": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", - "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", - "dev": true, - "requires": { - "vscode-languageserver-protocol": "3.14.1", - "vscode-uri": "^1.0.6" - } - }, - "vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", - "dev": true, - "requires": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" - } - }, - "vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==", - "dev": true - } - } - }, - "underscore": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", - "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } - }, - "unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "dev": true, - "requires": { - "unique-slug": "^2.0.0" - } - }, - "unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4" - } - }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - } - } - }, - "upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true - }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "^2.1.0" - } - }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", - "integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", - "dev": true, - "requires": { - "punycode": "1.3.2", - "querystring": "0.2.0" - }, - "dependencies": { - "punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", - "dev": true - } - } - }, - "url-parse": { - "version": "1.4.7", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.4.7.tgz", - "integrity": "sha512-d3uaVyzDB9tQoSXFvuSUNFibTd9zxd2bkVrDRvF5TmvWWQwqE4lgYJ5m+x1DbecWkw+LK4RNl2CU1hHuOKPVlg==", - "dev": true, - "requires": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", - "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", - "dev": true, - "requires": { - "inherits": "2.0.1" - }, - "dependencies": { - "inherits": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.1.tgz", - "integrity": "sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE=", - "dev": true - } - } - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" - }, - "uuid": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "vm-browserify": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vm-browserify/-/vm-browserify-1.1.2.tgz", - "integrity": "sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==", - "dev": true - }, - "vscode": { - "version": "1.1.33", - "resolved": "https://registry.npmjs.org/vscode/-/vscode-1.1.33.tgz", - "integrity": "sha512-sXedp2oF6y4ZvqrrFiZpeMzaCLSWV+PpYkIxjG/iYquNZ9KrLL2LujltGxPLvzn49xu2sZkyC+avVNFgcJD1Iw==", - "dev": true, - "requires": { - "glob": "^7.1.2", - "mocha": "^4.0.1", - "request": "^2.88.0", - "semver": "^5.4.1", - "source-map-support": "^0.5.0", - "url-parse": "^1.4.4", - "vscode-test": "^0.1.4" - }, - "dependencies": { - "browser-stdout": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", - "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", - "dev": true - }, - "commander": { - "version": "2.11.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz", - "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==", - "dev": true - }, - "diff": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz", - "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==", - "dev": true - }, - "growl": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz", - "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==", - "dev": true - }, - "has-flag": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", - "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "mocha": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.1.0.tgz", - "integrity": "sha512-0RVnjg1HJsXY2YFDoTNzcc1NKhYuXKRrBAG2gDygmJJA136Cs2QlRliZG1mA0ap7cuaT30mw16luAeln+4RiNA==", - "dev": true, - "requires": { - "browser-stdout": "1.3.0", - "commander": "2.11.0", - "debug": "3.1.0", - "diff": "3.3.1", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.3", - "he": "1.1.1", - "mkdirp": "0.5.1", - "supports-color": "4.4.0" - }, - "dependencies": { - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "supports-color": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz", - "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==", - "dev": true, - "requires": { - "has-flag": "^2.0.0" - } - }, - "vscode-test": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-0.1.5.tgz", - "integrity": "sha512-s+lbF1Dtasc0yXVB9iQTexBe2JK6HJAUJe3fWezHKIjq+xRw5ZwCMEMBaonFIPy7s95qg2HPTRDR5W4h4kbxGw==", - "dev": true, - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.1" - } - } - } - }, - "vscode-azureextensiondev": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/vscode-azureextensiondev/-/vscode-azureextensiondev-0.3.4.tgz", - "integrity": "sha512-d8ltYPTM8TfYzVtujzOnqeC7/H10WVwadSnR2R+4Xh8tzbmrDsq1b3pUElKqyt50uaXR7bQGPJaIomWL/AfBaw==", - "dev": true, - "requires": { - "azure-arm-resource": "^3.0.0-preview", - "clean-webpack-plugin": "^3.0.0", - "filemanager-webpack-plugin": "^2.0.5", - "fs-extra": "^8.0.0", - "ms-rest": "^2.2.2", - "ms-rest-azure": "^2.4.4", - "terser-webpack-plugin": "^1.2.2", - "ts-loader": "^5.3.3", - "webpack": "4.28.1" - }, - "dependencies": { - "azure-arm-resource": { - "version": "3.0.0-preview", - "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-3.0.0-preview.tgz", - "integrity": "sha512-5AxK9Nnk9hRDtyiXkaqvHV2BvII12JpPpTTHL8p9ZKgkwy67mWk+repoe9PnjxwG2Rm1RadutonccynJ+VHVAw==", - "dev": true, - "requires": { - "ms-rest": "^2.0.0", - "ms-rest-azure": "^2.0.0" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - } - } - }, - "vscode-azureextensionui": { - "version": "0.26.3", - "resolved": "https://registry.npmjs.org/vscode-azureextensionui/-/vscode-azureextensionui-0.26.3.tgz", - "integrity": "sha512-bWWj7S4+PSAFgwoHUcZSnjo0s7qLNAHxn/yY4EbvyzjPff83BgMe6Mkla+h0F7l33Pfc75VZoZKl3v6kNNzwcA==", - "requires": { - "azure-arm-resource": "^3.0.0-preview", - "azure-arm-storage": "^3.1.0", - "fs-extra": "^4.0.3", - "html-to-text": "^4.0.0", - "ms-rest": "^2.2.2", - "ms-rest-azure": "^2.4.4", - "opn": "^6.0.0", - "semver": "^5.6.0", - "vscode-extension-telemetry": "^0.1.0", - "vscode-nls": "^4.0.0" - }, - "dependencies": { - "azure-arm-resource": { - "version": "3.0.0-preview", - "resolved": "https://registry.npmjs.org/azure-arm-resource/-/azure-arm-resource-3.0.0-preview.tgz", - "integrity": "sha512-5AxK9Nnk9hRDtyiXkaqvHV2BvII12JpPpTTHL8p9ZKgkwy67mWk+repoe9PnjxwG2Rm1RadutonccynJ+VHVAw==", - "requires": { - "ms-rest": "^2.0.0", - "ms-rest-azure": "^2.0.0" - } - }, - "fs-extra": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", - "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", - "requires": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "vscode-extension-telemetry": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.2.tgz", - "integrity": "sha512-FSbaZKlIH3VKvBJsKw7v5bESWHXzltji2rtjaJeJglpQH4tfClzwHMzlMXUZGiblV++djEzb1gW8mb5E+wxFsg==", - "requires": { - "applicationinsights": "1.4.0" - } - }, - "vscode-nls": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.1.tgz", - "integrity": "sha512-4R+2UoUUU/LdnMnFjePxfLqNhBS8lrAFyX7pjb2ud/lqDkrUavFUTcG7wR0HBZFakae0Q6KLBFjMS6W93F403A==" - } - } - }, - "vscode-extension-telemetry": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/vscode-extension-telemetry/-/vscode-extension-telemetry-0.0.18.tgz", - "integrity": "sha512-Vw3Sr+dZwl+c6PlsUwrTtCOJkgrmvS3OUVDQGcmpXWAgq9xGq6as0K4pUx+aGqTjzLAESmWSrs6HlJm6J6Khcg==", - "requires": { - "applicationinsights": "1.0.1" - }, - "dependencies": { - "applicationinsights": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/applicationinsights/-/applicationinsights-1.0.1.tgz", - "integrity": "sha1-U0Rrgw/o1dYZ7uKieLMdPSUDCSc=", - "requires": { - "diagnostic-channel": "0.2.0", - "diagnostic-channel-publishers": "0.2.1", - "zone.js": "0.7.6" - } - }, - "diagnostic-channel-publishers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz", - "integrity": "sha1-ji1geottef6IC1SLxYzGvrKIxPM=" - } - } - }, - "vscode-json-languageservice": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-3.7.0.tgz", - "integrity": "sha512-nGLqcBhTjdfkl8Dz9sYGK/ZCTjscYFoIjYw+qqkWB+vyNfM0k/AyIoT73DQvB/PArteCKjEVfQUF72GRZEDSbQ==", - "requires": { - "jsonc-parser": "^2.2.1", - "vscode-languageserver-textdocument": "^1.0.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.2" - }, - "dependencies": { - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" - } - } - }, - "vscode-jsonrpc": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==" - }, - "vscode-languageclient": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-5.2.1.tgz", - "integrity": "sha512-7jrS/9WnV0ruqPamN1nE7qCxn0phkH5LjSgSp9h6qoJGoeAKzwKz/PF6M+iGA/aklx4GLZg1prddhEPQtuXI1Q==", - "requires": { - "semver": "^5.5.0", - "vscode-languageserver-protocol": "3.14.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - } - } - }, - "vscode-languageserver-protocol": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", - "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", - "requires": { - "vscode-jsonrpc": "^4.0.0", - "vscode-languageserver-types": "3.14.0" - }, - "dependencies": { - "vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - } - } - }, - "vscode-languageserver-textdocument": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.1.tgz", - "integrity": "sha512-UIcJDjX7IFkck7cSkNNyzIz5FyvpQfY7sdzVy+wkKN/BLaD4DQ0ppXQrKePomCxTS7RrolK1I0pey0bG9eh8dA==" - }, - "vscode-languageserver-types": { - "version": "3.15.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.15.1.tgz", - "integrity": "sha512-+a9MPUQrNGRrGU630OGbYVQ+11iOIovjCkqxajPa9w57Sd5ruK8WQNsslzpa0x/QJqC8kRc2DUxWjIFwoNm4ZQ==" - }, - "vscode-nls": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-3.2.4.tgz", - "integrity": "sha512-FTjdqa4jDDoBjJqr36O8lmmZf/55kQ2w4ZY/+GL6K92fq765BqO3aYw21atnXUno/P04V5DWagNl4ybDIndJsw==" - }, - "vscode-test": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.4.0.tgz", - "integrity": "sha512-Jt7HNGvSE0+++Tvtq5wc4hiXLIr2OjDShz/gbAfM/mahQpy4rKBnmOK33D+MR67ATWviQhl+vpmU3p/qwSH/Pg==", - "dev": true, - "requires": { - "http-proxy-agent": "^2.1.0", - "https-proxy-agent": "^2.2.4", - "rimraf": "^2.6.3" - } - }, - "vscode-uri": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz", - "integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww==" - }, - "watchpack": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-1.6.1.tgz", - "integrity": "sha512-+IF9hfUFOrYOOaKyfaI7h7dquUIOgyEMoQMLA7OP5FxegKA2+XdXThAZ9TU2kucfhDH7rfMHs1oPYziVGWRnZA==", - "dev": true, - "requires": { - "chokidar": "^2.1.8", - "graceful-fs": "^4.1.2", - "neo-async": "^2.5.0" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - }, - "dependencies": { - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "dev": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "fsevents": "^1.2.7", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "dev": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "dev": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - } - } - }, - "webpack": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-4.28.1.tgz", - "integrity": "sha512-qAS7BFyS5iuOZzGJxyDXmEI289h7tVNtJ5XMxf6Tz55J2riOyH42uaEsWF0F32TRaI+54SmI6qRgHM3GzsZ+sQ==", - "dev": true, - "requires": { - "@webassemblyjs/ast": "1.7.11", - "@webassemblyjs/helper-module-context": "1.7.11", - "@webassemblyjs/wasm-edit": "1.7.11", - "@webassemblyjs/wasm-parser": "1.7.11", - "acorn": "^5.6.2", - "acorn-dynamic-import": "^3.0.0", - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0", - "chrome-trace-event": "^1.0.0", - "enhanced-resolve": "^4.1.0", - "eslint-scope": "^4.0.0", - "json-parse-better-errors": "^1.0.2", - "loader-runner": "^2.3.0", - "loader-utils": "^1.1.0", - "memory-fs": "~0.4.1", - "micromatch": "^3.1.8", - "mkdirp": "~0.5.0", - "neo-async": "^2.5.0", - "node-libs-browser": "^2.0.0", - "schema-utils": "^0.4.4", - "tapable": "^1.1.0", - "terser-webpack-plugin": "^1.1.0", - "watchpack": "^1.5.0", - "webpack-sources": "^1.3.0" - }, - "dependencies": { - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - } - }, - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dependencies": { + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" } }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", "dev": true, "requires": { - "kind-of": "^6.0.0" + "fill-range": "^7.0.1" } }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" } }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", "dev": true, "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "to-regex-range": "^5.0.1" } }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true }, - "memory-fs": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", - "integrity": "sha1-OpoguEYlI+RHz7x+i7gO1me/xVI=", + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, "requires": { - "errno": "^0.1.3", - "readable-stream": "^2.0.1" + "is-glob": "^4.0.1" } }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "binary-extensions": "^2.0.0" } }, - "readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "is-extglob": "^2.1.1" } }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "schema-utils": { - "version": "0.4.7", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-0.4.7.tgz", - "integrity": "sha512-v/iwU6wvwGK8HbU9yi3/nhGzP0yGSuhQMzL6ySiec1FSrZZDkhm4noOSWzrNFo/jEc+SJY6jRTwuwbSXJPDUnQ==", + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "requires": { - "ajv": "^6.1.0", - "ajv-keywords": "^3.1.0" + "picomatch": "^2.2.1" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "requires": { - "safe-buffer": "~5.1.0" + "is-number": "^7.0.0" } } } }, - "webpack-sources": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-1.4.3.tgz", - "integrity": "sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==", - "dev": true, + "tweetnacl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.1.tgz", + "integrity": "sha512-kcoMoKTPYnoeS50tzoqjPY3Uv9axeuuFAZY9M/9zFnhoVvRfxz9K29IMPD7jGmt2c8SW7i3gT9WqDl2+nV7p4A==" + }, + "tweetsodium": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/tweetsodium/-/tweetsodium-0.0.4.tgz", + "integrity": "sha512-nTYzcaSmpd2vcfI8DriuO01l+jIF8TmjNxjFPRyJkdzi5IdJbUstT3G1rk8V9YkzrucNSWoSrA9Z5Yp3RW2cgw==", "requires": { - "source-list-map": "^2.0.0", - "source-map": "~0.6.1" + "blakejs": "^1.1.0", + "tweetnacl": "^1.0.1" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, + "typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", "requires": { - "isexe": "^2.0.0" + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + }, + "dependencies": { + "qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "requires": { + "side-channel": "^1.0.6" + } + }, + "tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==" + } } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "typedoc": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.12.tgz", + "integrity": "sha512-F+qhkK2VoTweDXd1c42GS/By2DvI2uDF4/EpG424dTexSHdtCH52C6IcAvMA6jR3DzAWZjHpUOW+E02kyPNUNw==", "dev": true, "requires": { - "string-width": "^1.0.2 || 2" + "lunr": "^2.3.9", + "marked": "^4.3.0", + "minimatch": "^9.0.3", + "shiki": "^0.14.7" }, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" + "balanced-match": "^1.0.0" } }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", "dev": true, "requires": { - "ansi-regex": "^3.0.0" + "brace-expansion": "^2.0.1" } } } }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - }, - "worker-farm": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", - "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", - "dev": true, - "requires": { - "errno": "~0.1.7" - } - }, - "wrap-ansi": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", - "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "typedoc-plugin-keywords": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-keywords/-/typedoc-plugin-keywords-1.6.0.tgz", + "integrity": "sha512-URyCIHw6+Lwil0ywy6lVb2TckfDVGjAWnRnTAiiSZaRaglI6vaaP1EhhwEipOIlHaJSnHZfdwpWe1t4mffTIpA==", "dev": true, "requires": { - "ansi-styles": "^3.2.0", - "string-width": "^3.0.0", - "strip-ansi": "^5.0.0" + "typescript": "^5.3.2" }, "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } } } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "typedoc-plugin-mdn-links": { + "version": "3.1.19", + "resolved": "https://registry.npmjs.org/typedoc-plugin-mdn-links/-/typedoc-plugin-mdn-links-3.1.19.tgz", + "integrity": "sha512-v08h/JorBemjl6xQyw+tB1P5BX2OUI9zr9vR5ZTuLsmETUrS3NC2z6ou8Ci0FDjSL0nA1tGsdXhUn42sEgkkUA==", "dev": true }, - "xmldom": { - "version": "0.1.27", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.27.tgz", - "integrity": "sha1-1QH5ezvbQDr4757MIFcxh6rawOk=" - }, - "xpath.js": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xpath.js/-/xpath.js-1.1.0.tgz", - "integrity": "sha512-jg+qkfS4K8E7965sqaUl8mRngXiKb3WZGfONgE18pr03FUQiuSV6G+Ej4tS55B+rIQSFEIw3phdVAQ4pPqNWfQ==" - }, - "xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "typedoc-plugin-merge-modules": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-merge-modules/-/typedoc-plugin-merge-modules-5.1.0.tgz", + "integrity": "sha512-jXH27L/wlxFjErgBXleh3opVgjVTXFEuBo68Yfl18S9Oh/IqxK6NV94jlEJ9hl4TXc9Zm2l7Rfk41CEkcCyvFQ==", "dev": true }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "typedoc-plugin-remove-references": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedoc-plugin-remove-references/-/typedoc-plugin-remove-references-0.0.6.tgz", + "integrity": "sha512-QoyHpopznnJbWW/9JT2NHSK+eTmyShkPYebwe5ZnO8aohPLc5okk4puWUDXnNh2Tn7cJU8U3t1tEMO6ghbwE8Q==", "dev": true }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true + "typedoc-plugin-rename-defaults": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/typedoc-plugin-rename-defaults/-/typedoc-plugin-rename-defaults-0.7.0.tgz", + "integrity": "sha512-NudSQ1o/XLHNF9c4y7LzIZxfE9ltz09yCDklBPJpP5VMRvuBpYGIbQ0ZgmPz+EIV8vPx9Z/OyKwzp4HT2vDtfg==", + "dev": true, + "requires": { + "camelcase": "^8.0.0" + }, + "dependencies": { + "camelcase": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", + "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", + "dev": true + } + } }, - "yaml-ast-parser-custom-tags": { - "version": "0.0.43", - "resolved": "https://registry.npmjs.org/yaml-ast-parser-custom-tags/-/yaml-ast-parser-custom-tags-0.0.43.tgz", - "integrity": "sha512-R5063FF/JSAN6qXCmylwjt9PcDH6M0ExEme/nJBzLspc6FJDmHHIqM7xh2WfEmsTJqClF79A9VkXjkAqmZw9SQ==" + "typedoc-plugin-zod": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/typedoc-plugin-zod/-/typedoc-plugin-zod-1.1.2.tgz", + "integrity": "sha512-jsmuYg1xsGjwKdhKN4tgRYORnbKpU7v5B1ZpsazMH5lUsI6ZLxBqAY5iiZ06oz/01gHOsAdhpABgWD97MOjKQA==", + "dev": true }, - "yaml-language-server": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-0.8.0.tgz", - "integrity": "sha512-+mvpHHPyQo/cNnEdrydH7h13FC393FQ9Uj88W/BbTdAANDy7eTHlmqPDzvv6X5HKl5fi5RLWCWsO4SdAx0WEMw==", + "typescript-tslint-plugin": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/typescript-tslint-plugin/-/typescript-tslint-plugin-0.5.5.tgz", + "integrity": "sha512-tR5igNQP+6FhxaPJYRlUBVsEl0n5cSuXRbg7L1y80mL4B1jUHb8uiIcbQBJ9zWyypJEdFYFUccpXxvMwZR8+AA==", + "dev": true, "requires": { - "js-yaml": "^3.13.1", - "jsonc-parser": "^2.2.1", - "prettier": "^1.18.2", - "request-light": "^0.2.4", - "vscode-json-languageservice": "^3.6.0", - "vscode-languageserver": "^5.2.1", - "vscode-languageserver-types": "^3.15.1", - "vscode-nls": "^4.1.2", - "vscode-uri": "^2.1.1", - "yaml-ast-parser-custom-tags": "0.0.43" + "minimatch": "^3.0.4", + "mock-require": "^3.0.3", + "vscode-languageserver": "^5.2.1" }, "dependencies": { "vscode-jsonrpc": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-4.0.0.tgz", - "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==" + "integrity": "sha512-perEnXQdQOJMTDFNv+UF3h1Y0z4iSiaN9jIlb0OqIYgosPCZGYh/MCUlkFtV2668PL69lRDO32hmvL2yiidUYg==", + "dev": true }, "vscode-languageserver": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-5.2.1.tgz", "integrity": "sha512-GuayqdKZqAwwaCUjDvMTAVRPJOp/SLON3mJ07eGsx/Iq9HjRymhKWztX41rISqDKhHVVyFM+IywICyZDla6U3A==", + "dev": true, "requires": { "vscode-languageserver-protocol": "3.14.1", "vscode-uri": "^1.0.6" - }, - "dependencies": { - "vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==" - } } }, "vscode-languageserver-protocol": { "version": "3.14.1", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.14.1.tgz", "integrity": "sha512-IL66BLb2g20uIKog5Y2dQ0IiigW0XKrvmWiOvc0yXw80z3tMEzEnHjaGAb3ENuU7MnQqgnYJ1Cl2l9RvNgDi4g==", + "dev": true, "requires": { "vscode-jsonrpc": "^4.0.0", "vscode-languageserver-types": "3.14.0" - }, - "dependencies": { - "vscode-languageserver-types": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", - "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==" - } } }, - "vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==" - }, - "vscode-uri": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.1.2.tgz", - "integrity": "sha512-8TEXQxlldWAuIODdukIb+TR5s+9Ds40eSJrw+1iDDA9IFORPjMELarNQE3myz5XIkWWpdprmJjm1/SxMlWOC8A==" + "vscode-languageserver-types": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz", + "integrity": "sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A==", + "dev": true } } }, - "yargs": { - "version": "13.3.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.2.tgz", - "integrity": "sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==", + "underscore": { + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", + "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "requires": { + "punycode": "^2.1.0" + } + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", "dev": true, "requires": { - "cliui": "^5.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^3.0.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^13.1.2" + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==" + }, + "vscode-json-languageservice": { + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-4.1.8.tgz", + "integrity": "sha512-0vSpg6Xd9hfV+eZAaYN63xVVMOTmJ4GgHxXnkLCh+9RsQBkWKIghzLhW2B9ebfG+LQQg8uLtsQ2aUKjTgE+QOg==", + "requires": { + "jsonc-parser": "^3.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2" }, "dependencies": { - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==" + }, + "vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" } } }, - "yargs-parser": { - "version": "13.1.2", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz", - "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==", - "dev": true, + "vscode-languageserver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-7.0.0.tgz", + "integrity": "sha512-60HTx5ID+fLRcgdHfmz0LDZAXYEV68fzwG0JWwEPBode9NuMYTIxuYXPg4ngO8i8+Ou0lM7y6GzaYWbiDL0drw==", "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "vscode-languageserver-protocol": "3.16.0" + }, + "dependencies": { + "vscode-jsonrpc": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", + "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==" + }, + "vscode-languageserver-protocol": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.16.0.tgz", + "integrity": "sha512-sdeUoAawceQdgIfTI+sdcwkiK2KU+2cbEYA0agzM2uqaUy2UpnnGHtWTHVEtS0ES4zHU0eMFRGN+oQgDxlD66A==", + "requires": { + "vscode-jsonrpc": "6.0.0", + "vscode-languageserver-types": "3.16.0" + } + }, + "vscode-languageserver-types": { + "version": "3.16.0", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.16.0.tgz", + "integrity": "sha512-k8luDIWJWyenLc5ToFQQMaSrqCHiLwyKPHKPQZ5zz21vM+vIVUSvsRpcbiECH4WR88K2XZqc4ScRcZ7nk/jbeA==" + } } }, - "yargs-unparser": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.6.0.tgz", - "integrity": "sha512-W9tKgmSn0DpSatfri0nx52Joq5hVXgeLiqR/5G0sZNDoLZFOr/xjBUDcShCOGNsBnEMNo1KAMBkTej1Hm62HTw==", - "dev": true, - "requires": { - "flat": "^4.1.0", - "lodash": "^4.17.15", - "yargs": "^13.3.0" - } + "vscode-languageserver-textdocument": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", + "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" }, - "yn": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", - "integrity": "sha1-5a2ryKz0CPY4X8dklWhMiOavaJo=", + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + }, + "vscode-oniguruma": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", + "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==", + "dev": true + }, + "vscode-textmate": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-8.0.0.tgz", + "integrity": "sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==", "dev": true }, - "zip-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", - "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", + "vscode-uri": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.6.tgz", + "integrity": "sha512-sLI2L0uGov3wKVb9EB+vIQBl9tVP90nqRvxSoJ35vI3NjxE8jfsE5DSOhWgSunHSZmKS4OCi2jrtfxK7uyp2ww==", + "dev": true + }, + "which-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.15.tgz", + "integrity": "sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA==", "dev": true, "requires": { - "archiver-utils": "^2.1.0", - "compress-commons": "^2.1.1", - "readable-stream": "^3.4.0" + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.2" } }, - "zone.js": { - "version": "0.7.6", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.7.6.tgz", - "integrity": "sha1-+7w50+AmHQmG8boGMG6zrrDSIAk=" + "yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==" + }, + "yaml-language-server": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/yaml-language-server/-/yaml-language-server-1.14.0.tgz", + "integrity": "sha512-HnNiHM5AOTXuM8ZpubzqgTy+7V5kFiMXVedOT2yjhvnxCw6pbMXcD/ymHaaT5v2ue0H8GGH4NFkvCEzcIcTJDg==", + "requires": { + "ajv": "^8.11.0", + "lodash": "4.17.21", + "prettier": "2.8.7", + "request-light": "^0.5.7", + "vscode-json-languageservice": "4.1.8", + "vscode-languageserver": "^7.0.0", + "vscode-languageserver-textdocument": "^1.0.1", + "vscode-languageserver-types": "^3.16.0", + "vscode-nls": "^5.0.0", + "vscode-uri": "^3.0.2", + "yaml": "2.2.2" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==" + }, + "vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + } + } } } -} \ No newline at end of file +} diff --git a/package.json b/package.json index 9d4182a2..7d53efd0 100644 --- a/package.json +++ b/package.json @@ -1,168 +1,144 @@ { - "name": "azure-deploy", - "displayName": "Deploy to Azure", - "description": "Generating CI/CD pipelines to Azure from GitHub and Azure Repo", - "version": "1.2.3", - "publisher": "ms-vscode-deploy-azure", - "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", - "repository": { - "type": "git", - "url": "https://github.com/Microsoft/vscode-deploy-azure" - }, - "homepage": "https://github.com/Microsoft/vscode-deploy-azure/blob/master/README.md", - "bugs": "https://github.com/Microsoft/vscode-deploy-azure/issues/", - "license": "MIT", - "icon": "assets/deployToAzure.png", - "galleryBanner": { - "color": "#D4DCEC", - "theme": "light" - }, - "engines": { - "vscode": "^1.32.0" - }, - "categories": [ - "Programming Languages", - "Formatters", - "Azure" - ], - "tags": [ - "azure-pipelines", - "Azure Pipelines", - "Deploy to Azure", - "YAML" - ], - "keywords": [ - "YAML", - "Azure Pipelines", - "continuous integration", - "CI/CD" - ], - "activationEvents": [ - "*" - ], - "main": "./out/extension", - "contributes": { - "languages": [ - { - "id": "yaml", - "aliases": [ - "YAML", - "yaml" - ], - "extensions": [ - ".yml", - ".eyaml", - ".eyml", - ".yaml" - ] - } - ], - "grammars": [ - { - "language": "yaml", - "scopeName": "source.yaml", - "path": "./syntaxes/yaml.tmLanguage.json" - } - ], - "commands": [ - { - "command": "configure-cicd-pipeline", - "title": "Configure CI/CD Pipeline", - "category": "Deploy to Azure" - }, - { - "command": "browse-cicd-pipeline", - "title": "Browse Pipeline", - "category": "Deploy to Azure" - } - ], - "menus": { - "explorer/context": [ - { - "command": "configure-cicd-pipeline", - "group": "Deploy to Azure", - "when": "explorerResourceIsFolder == true" - } - ], - "commandPalette": [ - { - "command": "browse-cicd-pipeline", - "when": "never" - } - ] - }, - "configuration": { - "title": "Deploy to Azure", - "properties": { - "deployToAzure.UseAzurePipelinesForGithub": { - "type": "boolean", - "default": false, - "description": "Setup CI/CD in Azure Pipelines for GitHub Repositories. Please note that this setting is ignored while configuring the CI/CD workflows in Azure Kubernetes Service extension for Visual Studio Code." - }, - "deployToAzure.UseGithubForCreatingNewRepository": { - "type": "boolean", - "default": true, - "description": "Use GitHub for creating new repository" - } - } - } - }, - "scripts": { - "vscode:prepublish": "npm run compile", - "compile": "tsc -p ./ && node copyStaticFiles.js", - "watch": "node copyStaticFiles.js && tsc -watch -p ./", - "postinstall": "node ./node_modules/vscode/bin/install", - "pretest": "npm run compile", - "test": "node ./out/configure/test/runTest.js" - }, - "devDependencies": { - "@types/fs-extra": "4.0.5", - "@types/glob": "^7.1.1", - "@types/js-yaml": "^3.12.1", - "@types/mocha": "^7.0.2", - "@types/mustache": "0.8.32", - "@types/node": "^7.0.43", - "@types/q": "1.5.0", - "@types/underscore": "1.8.9", - "ajv": "^6.9.1", - "assert": "1.4.1", - "chai": "^4.2.0", - "glob": "^7.1.6", - "mocha": "^7.1.2", - "nock": "^13.0.2", - "ts-node": "7.0.1", - "tslint": "5.8.0", - "tslint-microsoft-contrib": "^6.2.0", - "typescript": "3.3.1", - "typescript-tslint-plugin": "^0.5.5", - "vscode": "1.1.37", - "vscode-azureextensiondev": "^0.3.1", - "vscode-test": "^1.3.0" - }, - "dependencies": { - "azure-arm-resource": "7.3.0", - "azure-arm-website": "5.7.0", - "azureintegration-repoanalysis-client-internal": "^1.0.0", - "fs-extra": "^9.0.1", - "js-yaml": "^3.13.1", - "jsonpath-plus": "^3.0.0", - "mustache": "3.0.1", - "q": "1.5.1", - "semver": "^7.3.2", - "shelljs": "^0.3.0", - "simple-git": "^1.110.0", - "tweetsodium": "0.0.4", - "typed-rest-client": "1.0.7", - "underscore": "1.9.1", - "uuid": "^3.3.2", - "vscode-azureextensionui": "0.26.3", - "vscode-extension-telemetry": "0.0.18", - "vscode-languageclient": "^5.2.1", - "vscode-nls": "3.2.4", - "vscode-uri": "1.0.6", - "yaml-language-server": "^0.8.0" - }, - "extensionDependencies": [ - "ms-vscode.azure-account" - ] -} \ No newline at end of file + "name": "azure-deploy", + "displayName": "Deploy to Azure", + "version": "0.0.1", + "private": false, + "description": "Generating CI/CD pipelines to Azure from GitHub and Azure Repo", + "keywords": [ + "land" + ], + "homepage": "HTTPS://GitHub.Com/CodeEditorLand/LandDeployAzure#readme", + "bugs": { + "url": "HTTPS://GitHub.Com/CodeEditorLand/LandDeployAzure/issues" + }, + "repository": { + "type": "git", + "url": "git+HTTPS://github.com/CodeEditorLand/LandDeployAzure.git" + }, + "license": "SEE LICENSE IN LICENSE", + "author": { + "name": "Land", + "email": "Land@Playform.Cloud", + "url": "HTTPS://Land.Playform.Cloud" + }, + "type": "module", + "main": "./out/extension", + "scripts": { + "Document": "Document 'Source/**/*.ts'", + "compile": "tsc -p ./ && node copyStaticFiles.js", + "prepublishOnly": "Build 'Source/**/*.ts'" + }, + "contributes": { + "commands": [ + { + "category": "Deploy to Azure", + "command": "configure-cicd-pipeline", + "title": "Configure CI/CD Pipeline" + }, + { + "category": "Deploy to Azure", + "command": "browse-cicd-pipeline", + "title": "Browse Pipeline" + } + ], + "configuration": { + "properties": { + "deployToAzure.UseAzurePipelinesForGithub": { + "default": false, + "description": "Setup CI/CD in Azure Pipelines for GitHub Repositories. Please note that this setting is ignored while configuring the CI/CD workflows in Azure Kubernetes Service extension for Visual Studio Code.", + "type": "boolean" + }, + "deployToAzure.UseGithubForCreatingNewRepository": { + "default": true, + "description": "Use GitHub for creating new repository", + "type": "boolean" + } + }, + "title": "Deploy to Azure" + }, + "grammars": [ + { + "language": "yaml", + "path": "./syntaxes/yaml.tmLanguage.json", + "scopeName": "source.yaml" + } + ], + "languages": [ + { + "aliases": [ + "YAML", + "yaml" + ], + "extensions": [ + ".yml", + ".eyaml", + ".eyml", + ".yaml" + ], + "id": "yaml" + } + ], + "menus": { + "commandPalette": [ + { + "command": "browse-cicd-pipeline", + "when": "never" + } + ], + "explorer/context": [ + { + "command": "configure-cicd-pipeline", + "group": "Deploy to Azure", + "when": "explorerResourceIsFolder == true" + } + ] + } + }, + "activationEvents": [ + "*" + ], + "dependencies": { + "fs-extra": "11.2.0", + "js-yaml": "4.1.0", + "jsonpath-plus": "8.1.0", + "mustache": "4.2.0", + "q": "1.5.1", + "semver": "7.6.0", + "shelljs": "0.8.5", + "simple-git": "3.24.0", + "tweetsodium": "0.0.4", + "typed-rest-client": "1.8.11", + "underscore": "1.13.6", + "uuid": "9.0.1", + "yaml-language-server": "1.14.0" + }, + "devDependencies": { + "@playform/build": "0.0.7", + "@playform/document": "0.0.6", + "@types/fs-extra": "11.0.4", + "@types/glob": "8.1.0", + "@types/js-yaml": "4.0.9", + "@types/mustache": "4.2.5", + "@types/node": "20.12.7", + "@types/q": "1.5.8", + "@types/underscore": "1.11.15", + "ajv": "8.12.0", + "assert": "2.1.0", + "chai": "5.1.0", + "glob": "10.3.12", + "nock": "13.5.4", + "typescript-tslint-plugin": "0.5.5" + }, + "extensionDependencies": [ + "ms-vscode.azure-account" + ], + "publishConfig": { + "access": "public" + }, + "icon": "assets/deployToAzure.png", + "galleryBanner": { + "color": "#D4DCEC", + "theme": "light" + }, + "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217" +}