diff --git a/extensions/sql-bindings/package.json b/extensions/sql-bindings/package.json index 53ee59e2f7..c2ff4fea35 100644 --- a/extensions/sql-bindings/package.json +++ b/extensions/sql-bindings/package.json @@ -13,7 +13,7 @@ "icon": "", "aiKey": "AIF-37eefaf0-8022-4671-a3fb-64752724682e", "activationEvents": [ - "*" + "onCommand:sqlBindings.addSqlBinding" ], "main": "./out/extension", "repository": { @@ -28,5 +28,34 @@ "untrustedWorkspaces": { "supported": true } + }, + "contributes": { + "commands": [ + { + "command": "sqlBindings.addSqlBinding", + "title": "%sqlBindings.addSqlBinding%", + "category": "MS SQL" + } + ], + "menus": { + "commandPalette": [ + { + "command": "sqlBindings.addSqlBinding", + "when": "editorLangId == csharp && !azdataAvailable && resourceScheme != untitled" + } + ] + } + }, + "dependencies": { + "@microsoft/ads-extension-telemetry": "^1.1.5", + "fast-glob": "^3.2.7", + "fs-extra": "^5.0.0", + "jsonc-parser": "^2.3.1", + "promisify-child-process": "^3.1.1", + "vscode-nls": "^4.1.2" + }, + "devDependencies": { + "@types/fs-extra": "^5.0.0", + "tslint": "^5.8.0" } } diff --git a/extensions/sql-bindings/src/common/azureFunctionsUtils.ts b/extensions/sql-bindings/src/common/azureFunctionsUtils.ts new file mode 100644 index 0000000000..d75df33fa6 --- /dev/null +++ b/extensions/sql-bindings/src/common/azureFunctionsUtils.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as fse from 'fs-extra'; +import * as path from 'path'; +import * as vscode from 'vscode'; +import * as utils from './utils'; +import * as constants from './constants'; +import { parseJson } from './parseJson'; + +/** + * Represents the settings in an Azure function project's locawl.settings.json file + */ +export interface ILocalSettingsJson { + IsEncrypted?: boolean; + Values?: { [key: string]: string }; + Host?: { [key: string]: string }; + ConnectionStrings?: { [key: string]: string }; +} + +/** + * copied and modified from vscode-azurefunctions extension + * @param localSettingsPath full path to local.settings.json + * @returns settings in local.settings.json. If no settings are found, returns default "empty" settings + */ +export async function getLocalSettingsJson(localSettingsPath: string): Promise { + if (await fse.pathExists(localSettingsPath)) { + const data: string = (await fse.readFile(localSettingsPath)).toString(); + if (/[^\s]/.test(data)) { + try { + return parseJson(data); + } catch (error) { + throw new Error(constants.failedToParse(error.message)); + } + } + } + + return { + IsEncrypted: false // Include this by default otherwise the func cli assumes settings are encrypted and fails to run + }; +} + +/** + * Adds a new setting to a project's local.settings.json file + * modified from setLocalAppSetting code from vscode-azurefunctions extension + * @param projectFolder full path to project folder + * @param key Key of the new setting + * @param value Value of the new setting + * @returns true if successful adding the new setting, false if unsuccessful + */ +export async function setLocalAppSetting(projectFolder: string, key: string, value: string): Promise { + const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName); + const settings: ILocalSettingsJson = await getLocalSettingsJson(localSettingsPath); + + settings.Values = settings.Values || {}; + if (settings.Values[key] === value) { + // don't do anything if it's the same as the existing value + return true; + } else if (settings.Values[key]) { + const result = await vscode.window.showWarningMessage(constants.settingAlreadyExists(key), { modal: true }, constants.yesString); + if (result !== constants.yesString) { + // key already exists and user doesn't want to overwrite it + return false; + } + } + + settings.Values[key] = value; + await fse.writeJson(localSettingsPath, settings, { spaces: 2 }); + + return true; +} + +/** + * Gets the Azure Functions project that contains the given file if the project is open in one of the workspace folders + * @param fileUri file that the containing project needs to be found for + * @returns uri of project or undefined if project couldn't be found + */ +export async function getAFProjectContainingFile(fileUri: vscode.Uri): Promise { + // get functions csprojs in the workspace + const projectPromises = vscode.workspace.workspaceFolders?.map(f => utils.getAllProjectsInFolder(f.uri, '.csproj')) ?? []; + const functionsProjects = (await Promise.all(projectPromises)).reduce((prev, curr) => prev.concat(curr), []).filter(p => isFunctionProject(path.dirname(p.fsPath))); + + // look for project folder containing file if there's more than one + if (functionsProjects.length > 1) { + // TODO: figure out which project contains the file + // the new style csproj doesn't list all the files in the project anymore, unless the file isn't in the same folder + // so we can't rely on using that to check + console.error('need to find which project contains the file ' + fileUri.fsPath); + return undefined; + } else if (functionsProjects.length === 0) { + throw new Error(constants.noAzureFunctionsProjectsInWorkspace); + } else { + return functionsProjects[0]; + } +} + +// Use 'host.json' as an indicator that this is a functions project +// copied from verifyIsproject.ts in vscode-azurefunctions extension +export async function isFunctionProject(folderPath: string): Promise { + return fse.pathExists(path.join(folderPath, constants.hostFileName)); +} +/** + * Adds the required nuget package to the project + * @param selectedProjectFile is the users selected project file path + */ +export async function addNugetReferenceToProjectFile(selectedProjectFile: string): Promise { + await utils.executeCommand(`dotnet add ${selectedProjectFile} package ${constants.sqlExtensionPackageName} --prerelease`); +} + diff --git a/extensions/sql-bindings/src/common/constants.ts b/extensions/sql-bindings/src/common/constants.ts new file mode 100644 index 0000000000..1096fe182e --- /dev/null +++ b/extensions/sql-bindings/src/common/constants.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vscode-nls'; +import * as utils from '../common/utils'; + +const localize = nls.loadMessageBundle(); + +// Insert SQL binding +export const hostFileName = 'host.json'; +export const sqlExtensionPackageName = 'Microsoft.Azure.WebJobs.Extensions.Sql'; +export const placeHolderObject = '[dbo].[table1]'; +export const sqlBindingsHelpLink = 'https://github.com/Azure/azure-functions-sql-extension/blob/main/README.md'; +export const passwordPlaceholder = '******'; +export const azureFunctionLocalSettingsFileName = 'local.settings.json'; +export const vscodeOpenCommand = 'vscode.open'; + +export const nameMustNotBeEmpty = localize('nameMustNotBeEmpty', "Name must not be empty"); +export const yesString = localize('yesString', "Yes"); +export const noString = localize('noString', "No"); +export const input = localize('input', "Input"); +export const output = localize('output', "Output"); +export const selectBindingType = localize('selectBindingType', "Select type of binding"); +export const selectAzureFunction = localize('selectAzureFunction', "Select an Azure function in the current file to add SQL binding to"); +export const sqlTableOrViewToQuery = localize('sqlTableOrViewToQuery', "SQL table or view to query"); +export const sqlTableToUpsert = localize('sqlTableToUpsert', "SQL table to upsert into"); +export const connectionStringSetting = localize('connectionStringSetting', "Connection string setting name"); +export const selectSetting = localize('selectSetting', "Select SQL connection string setting from local.settings.json"); +export const connectionStringSettingPlaceholder = localize('connectionStringSettingPlaceholder', "Connection string setting specified in \"local.settings.json\""); +export const noAzureFunctionsInFile = localize('noAzureFunctionsInFile', "No Azure functions in the current active file"); +export const noAzureFunctionsProjectsInWorkspace = localize('noAzureFunctionsProjectsInWorkspace', "No Azure functions projects found in the workspace"); +export const addPackage = localize('addPackage', "Add Package"); +export const createNewLocalAppSetting = localize('createNewLocalAppSetting', 'Create new local app setting'); +export const createNewLocalAppSettingWithIcon = `$(add) ${createNewLocalAppSetting}`; +export const sqlConnectionStringSetting = 'SqlConnectionString'; +export const valueMustNotBeEmpty = localize('valueMustNotBeEmpty', "Value must not be empty"); +export const enterConnectionStringSettingName = localize('enterConnectionStringSettingName', "Enter connection string setting name"); +export const enterConnectionString = localize('enterConnectionString', "Enter connection string"); +export const saveChangesInFile = localize('saveChangesInFile', "There are unsaved changes in the current file. Save now?"); +export const save = localize('save', "Save"); +export function settingAlreadyExists(settingName: string) { return localize('SettingAlreadyExists', 'Local app setting \'{0}\' already exists. Overwrite?', settingName); } +export function failedToParse(errorMessage: string) { return localize('failedToParse', 'Failed to parse "{0}": {1}.', azureFunctionLocalSettingsFileName, errorMessage); } +export function jsonParseError(error: string, line: number, column: number) { return localize('jsonParseError', '{0} near line "{1}", column "{2}"', error, line, column); } +export const moreInformation = localize('moreInformation', "More Information"); +export const addPackageReferenceMessage = localize('addPackageReferenceMessage', 'To use SQL bindings, ensure your Azure Functions project has a reference to {0}', sqlExtensionPackageName); +export const addSqlBindingPackageError = localize('addSqlBindingPackageError', 'Error adding Sql Binding extension package to project'); +export const failedToGetConnectionString = localize('failedToGetConnectionString', 'An error occurred generating the connection string for the selected connection'); +export const connectionProfile = localize('connectionProfile', 'Select a connection profile'); +export const userConnectionString = localize('userConnectionString', 'Enter connection string'); +export const selectConnectionString = localize('selectConnectionString', 'Select SQL connection string method'); +export const selectConnectionError = (err?: any) => err ? localize('selectConnectionError', "Failed to set connection string app setting: {0}", utils.getErrorMessage(err)) : localize('unableToSetConnectionString', "Failed to set connection string app setting"); +export const includePassword = localize('includePassword', 'Do you want to include the password from this connection in your local.settings.json file?'); +export const enterPasswordPrompt = localize('enterPasswordPrompt', 'Enter the password to be used for the connection string'); +export const enterPasswordManually = localize('enterPasswordManually', 'Enter password or press escape to cancel'); +export const userPasswordLater = localize('userPasswordLater', 'In order to user the SQL connection string later you will need to manually enter the password in your local.settings.json file.'); +export const openFile = localize('openFile', "Open File"); +export const closeButton = localize('closeButton', "Close"); diff --git a/extensions/sql-bindings/src/common/parseJson.ts b/extensions/sql-bindings/src/common/parseJson.ts new file mode 100644 index 0000000000..de4c00db29 --- /dev/null +++ b/extensions/sql-bindings/src/common/parseJson.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// copied from vscode-azurefunctions extension + +import * as jsonc from 'jsonc-parser'; +import * as constants from './constants'; + +/** + * Parses and returns JSON + * Has extra logic to remove a BOM character if it exists and handle comments + */ +export function parseJson(data: string): T { + if (data.charCodeAt(0) === 0xFEFF) { + data = data.slice(1); + } + + const errors: jsonc.ParseError[] = []; + const result: T = jsonc.parse(data, errors, { allowTrailingComma: true }); + if (errors.length > 0) { + const [line, column]: [number, number] = getLineAndColumnFromOffset(data, errors[0].offset); + throw new Error(constants.jsonParseError(jsonc.printParseErrorCode(errors[0].error), line, column)); + } else { + return result; + } +} + +export function getLineAndColumnFromOffset(data: string, offset: number): [number, number] { + const lines: string[] = data.split('\n'); + let charCount: number = 0; + let lineCount: number = 0; + let column: number = 0; + for (const line of lines) { + lineCount += 1; + const lineLength: number = line.length + 1; + charCount += lineLength; + if (charCount >= offset) { + column = offset - (charCount - lineLength); + break; + } + } + return [lineCount, column]; +} diff --git a/extensions/sql-bindings/src/common/telemetry.ts b/extensions/sql-bindings/src/common/telemetry.ts new file mode 100644 index 0000000000..c26717d144 --- /dev/null +++ b/extensions/sql-bindings/src/common/telemetry.ts @@ -0,0 +1,20 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import AdsTelemetryReporter from '@microsoft/ads-extension-telemetry'; + +const packageInfo = vscode.extensions.getExtension('Microsoft.sql-bindings')?.packageJSON; + +export const TelemetryReporter = new AdsTelemetryReporter(packageInfo.name, packageInfo.version, packageInfo.aiKey); + + +export enum TelemetryViews { + SqlBindingsQuickPick = 'SqlBindingsQuickPick' +} + +export enum TelemetryActions { + startAddSqlBinding = 'startAddSqlBinding', + finishAddSqlBinding = 'finishAddSqlBinding' +} diff --git a/extensions/sql-bindings/src/common/utils.ts b/extensions/sql-bindings/src/common/utils.ts new file mode 100644 index 0000000000..2bac04cc31 --- /dev/null +++ b/extensions/sql-bindings/src/common/utils.ts @@ -0,0 +1,110 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as azdataType from 'azdata'; +import * as vscode from 'vscode'; +import * as vscodeMssql from 'vscode-mssql'; +import * as fse from 'fs-extra'; +import * as path from 'path'; +import * as glob from 'fast-glob'; +import * as cp from 'child_process'; + +export interface ValidationResult { + errorMessage: string; + validated: boolean +} + +/** + * Consolidates on the error message string + */ +export function getErrorMessage(error: any): string { + return (error instanceof Error) + ? (typeof error.message === 'string' ? error.message : '') + : typeof error === 'string' ? error : `${JSON.stringify(error, undefined, '\t')}`; +} + +export async function getAzureFunctionService(): Promise { + if (getAzdataApi()) { + // this isn't supported in ADS + throw new Error('Azure Functions service is not supported in Azure Data Studio'); + } else { + const api = await getVscodeMssqlApi(); + return api.azureFunctions; + } +} + +export async function getVscodeMssqlApi(): Promise { + const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension; + return ext.activate(); +} + +export interface IPackageInfo { + name: string; + fullName: string; + version: string; + aiKey: string; +} + +// Try to load the azdata API - but gracefully handle the failure in case we're running +// in a context where the API doesn't exist (such as VS Code) +let azdataApi: typeof azdataType | undefined = undefined; +try { + azdataApi = require('azdata'); + if (!azdataApi?.version) { + // webpacking makes the require return an empty object instead of throwing an error so make sure we clear the var + azdataApi = undefined; + } +} catch { + // no-op +} + +/** + * Gets the azdata API if it's available in the context this extension is running in. + * @returns The azdata API if it's available + */ +export function getAzdataApi(): typeof azdataType | undefined { + return azdataApi; +} + +export async function createFolderIfNotExist(folderPath: string): Promise { + try { + await fse.mkdir(folderPath); + } catch { + // Ignore if failed + } +} + +export async function executeCommand(command: string, cwd?: string): Promise { + return new Promise((resolve, reject) => { + cp.exec(command, { maxBuffer: 500 * 1024, cwd: cwd }, (error: Error | null, stdout: string, stderr: string) => { + if (error) { + reject(error); + return; + } + if (stderr && stderr.length > 0) { + reject(new Error(stderr)); + return; + } + resolve(stdout); + }); + }); +} + +/** + * Gets all the projects of the specified extension in the folder + * @param folder + * @param projectExtension project extension to filter on + * @returns array of project uris + */ +export async function getAllProjectsInFolder(folder: vscode.Uri, projectExtension: string): Promise { + // path needs to use forward slashes for glob to work + const escapedPath = glob.escapePath(folder.fsPath.replace(/\\/g, '/')); + + // filter for projects with the specified project extension + const projFilter = path.posix.join(escapedPath, '**', `*${projectExtension}`); + + // glob will return an array of file paths with forward slashes, so they need to be converted back if on windows + return (await glob(projFilter)).map(p => vscode.Uri.file(path.resolve(p))); +} diff --git a/extensions/sql-bindings/src/dialogs/addSqlBindingQuickpick.ts b/extensions/sql-bindings/src/dialogs/addSqlBindingQuickpick.ts new file mode 100644 index 0000000000..e805f694bb --- /dev/null +++ b/extensions/sql-bindings/src/dialogs/addSqlBindingQuickpick.ts @@ -0,0 +1,297 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as vscode from 'vscode'; +import * as path from 'path'; +import { BindingType, ConnectionDetails, IConnectionInfo } from 'vscode-mssql'; +import * as constants from '../common/constants'; +import * as utils from '../common/utils'; +import * as azureFunctionsUtils from '../common/azureFunctionsUtils'; +import { TelemetryActions, TelemetryReporter, TelemetryViews } from '../common/telemetry'; + +export async function launchAddSqlBindingQuickpick(uri: vscode.Uri | undefined): Promise { + TelemetryReporter.sendActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.startAddSqlBinding); + + const vscodeMssqlApi = await utils.getVscodeMssqlApi(); + + if (!uri) { + // this command only shows in the command palette when the active editor is a .cs file, so we can safely assume that's the scenario + // when this is called without a uri + uri = vscode.window.activeTextEditor!.document.uri; + + if (vscode.window.activeTextEditor!.document.isDirty) { + const result = await vscode.window.showWarningMessage(constants.saveChangesInFile, { modal: true }, constants.save); + + if (result !== constants.save) { + return; + } + + await vscode.window.activeTextEditor!.document.save(); + } + } + + // get all the Azure functions in the file + const azureFunctionsService = await utils.getAzureFunctionService(); + let getAzureFunctionsResult; + try { + getAzureFunctionsResult = await azureFunctionsService.getAzureFunctions(uri.fsPath); + } catch (e) { + void vscode.window.showErrorMessage(utils.getErrorMessage(e)); + return; + } + + const azureFunctions = getAzureFunctionsResult.azureFunctions; + + if (azureFunctions.length === 0) { + void vscode.window.showErrorMessage(constants.noAzureFunctionsInFile); + return; + } + + // 1. select Azure function from the current file + const azureFunctionName = (await vscode.window.showQuickPick(azureFunctions, { + canPickMany: false, + title: constants.selectAzureFunction, + ignoreFocusOut: true + })); + + if (!azureFunctionName) { + return; + } + + // 2. select input or output binding + const inputOutputItems: (vscode.QuickPickItem & { type: BindingType })[] = [ + { + label: constants.input, + type: BindingType.input + }, + { + label: constants.output, + type: BindingType.output + } + ]; + + const selectedBinding = (await vscode.window.showQuickPick(inputOutputItems, { + canPickMany: false, + title: constants.selectBindingType, + ignoreFocusOut: true + })); + + if (!selectedBinding) { + return; + } + + // 3. ask for object name for the binding + const objectName = await vscode.window.showInputBox({ + prompt: selectedBinding.type === BindingType.input ? constants.sqlTableOrViewToQuery : constants.sqlTableToUpsert, + placeHolder: constants.placeHolderObject, + validateInput: input => input ? undefined : constants.nameMustNotBeEmpty, + ignoreFocusOut: true + }); + + if (!objectName) { + return; + } + + // 4. ask for connection string setting name + let projectUri: vscode.Uri | undefined; + try { + projectUri = await azureFunctionsUtils.getAFProjectContainingFile(uri); + } catch (e) { + // continue even if there's no AF project found. The binding should still be able to be added as long as there was an azure function found in the file earlier + } + + let connectionStringSettingName; + + // show the settings from project's local.settings.json if there's an AF functions project + if (projectUri) { + let settings; + try { + settings = await azureFunctionsUtils.getLocalSettingsJson(path.join(path.dirname(projectUri.fsPath!), constants.azureFunctionLocalSettingsFileName)); + } catch (e) { + void vscode.window.showErrorMessage(utils.getErrorMessage(e)); + return; + } + + let existingSettings: (vscode.QuickPickItem)[] = []; + if (settings?.Values) { + existingSettings = Object.keys(settings.Values).map(setting => { + return { + label: setting + } as vscode.QuickPickItem; + }); + } + + existingSettings.unshift({ label: constants.createNewLocalAppSettingWithIcon }); + let sqlConnectionStringSettingExists = existingSettings.find(s => s.label === constants.sqlConnectionStringSetting); + + while (!connectionStringSettingName) { + const selectedSetting = await vscode.window.showQuickPick(existingSettings, { + canPickMany: false, + title: constants.selectSetting, + ignoreFocusOut: true + }); + if (!selectedSetting) { + // User cancelled + return; + } + + if (selectedSetting.label === constants.createNewLocalAppSettingWithIcon) { + const newConnectionStringSettingName = await vscode.window.showInputBox( + { + title: constants.enterConnectionStringSettingName, + ignoreFocusOut: true, + value: sqlConnectionStringSettingExists ? '' : constants.sqlConnectionStringSetting, + validateInput: input => input ? undefined : constants.nameMustNotBeEmpty + } + ) ?? ''; + + if (!newConnectionStringSettingName) { + // go back to select setting quickpick if user escapes from inputting the setting name in case they changed their mind + continue; + } + + // show the connection string methods (user input and connection profile options) + const listOfConnectionStringMethods = [constants.connectionProfile, constants.userConnectionString]; + while (true) { + const selectedConnectionStringMethod = await vscode.window.showQuickPick(listOfConnectionStringMethods, { + canPickMany: false, + title: constants.selectConnectionString, + ignoreFocusOut: true + }); + if (!selectedConnectionStringMethod) { + // User cancelled + return; + } + + let connectionString: string = ''; + let includePassword: string | undefined; + let connectionInfo: IConnectionInfo | undefined; + let connectionDetails: ConnectionDetails; + if (selectedConnectionStringMethod === constants.userConnectionString) { + // User chooses to enter connection string manually + connectionString = await vscode.window.showInputBox( + { + title: constants.enterConnectionString, + ignoreFocusOut: true, + value: 'Server=localhost;Initial Catalog={db_name};User ID=sa;Password={your_password};Persist Security Info=False', + validateInput: input => input ? undefined : constants.valueMustNotBeEmpty + } + ) ?? ''; + } else { + // Let user choose from existing connections to create connection string from + connectionInfo = await vscodeMssqlApi.promptForConnection(true); + if (!connectionInfo) { + // User cancelled return to selectedConnectionStringMethod prompt + continue; + } + connectionDetails = { options: connectionInfo }; + try { + // Prompt to include password in connection string if authentication type is SqlLogin and connection has password saved + if (connectionInfo.authenticationType === 'SqlLogin' && connectionInfo.password) { + includePassword = await vscode.window.showQuickPick([constants.yesString, constants.noString], { + title: constants.includePassword, + canPickMany: false, + ignoreFocusOut: true + }); + if (includePassword === constants.yesString) { + // set connection string to include password + connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, true, false); + } + } + // set connection string to not include the password if connection info does not include password, or user chooses to not include password, or authentication type is not sql login + if (includePassword !== constants.yesString) { + connectionString = await vscodeMssqlApi.getConnectionString(connectionDetails, false, false); + } + } catch (e) { + // failed to get connection string for selected connection and will go back to prompt for connection string methods + console.warn(e); + void vscode.window.showErrorMessage(constants.failedToGetConnectionString); + continue; + } + } + if (connectionString) { + try { + const projectFolder: string = path.dirname(projectUri.fsPath); + const localSettingsPath: string = path.join(projectFolder, constants.azureFunctionLocalSettingsFileName); + let userPassword: string | undefined; + // Ask user to enter password if auth type is sql login and password is not saved + if (connectionInfo?.authenticationType === 'SqlLogin' && !connectionInfo?.password) { + userPassword = await vscode.window.showInputBox({ + prompt: constants.enterPasswordPrompt, + placeHolder: constants.enterPasswordManually, + ignoreFocusOut: true, + password: true, + validateInput: input => input ? undefined : constants.valueMustNotBeEmpty + }); + if (userPassword) { + // if user enters password replace password placeholder with user entered password + connectionString = connectionString.replace(constants.passwordPlaceholder, userPassword); + } + } + if (includePassword !== constants.yesString && !userPassword && connectionInfo?.authenticationType === 'SqlLogin') { + // if user does not want to include password or user does not enter password, show warning message that they will have to enter it manually later in local.settings.json + void vscode.window.showWarningMessage(constants.userPasswordLater, constants.openFile, constants.closeButton).then(async (result) => { + if (result === constants.openFile) { + // open local.settings.json file + void vscode.commands.executeCommand(constants.vscodeOpenCommand, vscode.Uri.file(localSettingsPath)); + } + }); + } + const success = await azureFunctionsUtils.setLocalAppSetting(projectFolder, newConnectionStringSettingName, connectionString); + if (success) { + // exit both loops and insert binding + connectionStringSettingName = newConnectionStringSettingName; + break; + } else { + void vscode.window.showErrorMessage(constants.selectConnectionError()); + } + } catch (e) { + // display error message and show select setting quickpick again + void vscode.window.showErrorMessage(constants.selectConnectionError(e)); + continue; + } + } + } + } else { + // If user cancels out of this or doesn't want to overwrite an existing setting + // just return them to the select setting quickpick in case they changed their mind + connectionStringSettingName = selectedSetting.label; + } + } + // Add sql extension package reference to project. If the reference is already there, it doesn't get added again + await azureFunctionsUtils.addNugetReferenceToProjectFile(projectUri.fsPath); + } else { + // if no AF project was found or there's more than one AF functions project in the workspace, + // ask for the user to input the setting name + connectionStringSettingName = await vscode.window.showInputBox({ + prompt: constants.connectionStringSetting, + placeHolder: constants.connectionStringSettingPlaceholder, + ignoreFocusOut: true + }); + } + + if (!connectionStringSettingName) { + return; + } + + // 5. insert binding + try { + const result = await azureFunctionsService.addSqlBinding(selectedBinding.type, uri.fsPath, azureFunctionName, objectName, connectionStringSettingName); + + if (!result.success) { + void vscode.window.showErrorMessage(result.errorMessage); + TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding); + return; + } + + TelemetryReporter.createActionEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding) + .withAdditionalProperties({ bindingType: selectedBinding.label }) + .send(); + } catch (e) { + void vscode.window.showErrorMessage(utils.getErrorMessage(e)); + TelemetryReporter.sendErrorEvent(TelemetryViews.SqlBindingsQuickPick, TelemetryActions.finishAddSqlBinding); + return; + } +} diff --git a/extensions/sql-bindings/src/extension.ts b/extensions/sql-bindings/src/extension.ts index 155e182622..87a685fbb2 100644 --- a/extensions/sql-bindings/src/extension.ts +++ b/extensions/sql-bindings/src/extension.ts @@ -2,8 +2,13 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; +import { getAzdataApi } from './common/utils'; +import { launchAddSqlBindingQuickpick } from '../src/dialogs/addSqlBindingQuickpick'; -export function activate(): void { +export function activate(context: vscode.ExtensionContext): void { + void vscode.commands.executeCommand('setContext', 'azdataAvailable', !!getAzdataApi()); + context.subscriptions.push(vscode.commands.registerCommand('sqlBindings.addSqlBinding', async (uri: vscode.Uri | undefined) => { return launchAddSqlBindingQuickpick(uri); })); } export function deactivate(): void { diff --git a/extensions/sql-bindings/src/typings/ref.d.ts b/extensions/sql-bindings/src/typings/ref.d.ts new file mode 100644 index 0000000000..cfdf5dd135 --- /dev/null +++ b/extensions/sql-bindings/src/typings/ref.d.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// +/// +/// +/// diff --git a/extensions/sql-bindings/src/typings/vscode-mssql.d.ts b/extensions/sql-bindings/src/typings/vscode-mssql.d.ts new file mode 100644 index 0000000000..7a5f03c460 --- /dev/null +++ b/extensions/sql-bindings/src/typings/vscode-mssql.d.ts @@ -0,0 +1,644 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode-mssql' { + + import * as vscode from 'vscode'; + + /** + * Covers defining what the vscode-mssql extension exports to other extensions + * + * IMPORTANT: THIS IS NOT A HARD DEFINITION unlike vscode; therefore no enums or classes should be defined here + * (const enums get evaluated when typescript -> javascript so those are fine) + */ + + + export const enum extension { + name = 'ms-mssql.mssql' + } + + /** + * The APIs provided by Mssql extension + */ + export interface IExtension { + + /** + * Path to the root of the SQL Tools Service folder + */ + readonly sqlToolsServicePath: string; + + /** + * Service for accessing DacFx functionality + */ + readonly dacFx: IDacFxService; + + /** + * Service for accessing SchemaCompare functionality + */ + readonly schemaCompare: ISchemaCompareService; + + /** + * Service for accessing AzureFunctions functionality + */ + readonly azureFunctions: IAzureFunctionsService; + + /** + * Prompts the user to select an existing connection or create a new one, and then returns the result + * @param ignoreFocusOut Whether the quickpick prompt ignores focus out (default false) + */ + promptForConnection(ignoreFocusOut?: boolean): Promise; + + /** + * Attempts to create a new connection for the given connection info. An error is thrown and displayed + * to the user if an error occurs while connecting. + * Warning: setting the saveConnection to true will save a new connection profile each time this is called. + * Make sure to use that parameter only when you want to actually save a new profile. + * @param connectionInfo The connection info + * @param saveConnection Save the connection profile if sets to true + * @returns The URI associated with this connection + */ + connect(connectionInfo: IConnectionInfo, saveConnection?: boolean): Promise; + + /** + * Lists the databases for a given connection. Must be given an already-opened connection to succeed. + * @param connectionUri The URI of the connection to list the databases for. + * @returns The list of database names + */ + listDatabases(connectionUri: string): Promise; + + /** + * Gets the database name for the node - which is the database name of the connection for a server node, the database name + * for nodes at or under a database node or a default value if it's neither of those. + * @param node The node to get the database name of + * @returns The database name + */ + getDatabaseNameFromTreeNode(node: ITreeNodeInfo): string; + + /** + * Get the connection string for the provided connection Uri or connection details. + * @param connectionUriOrDetails Either the connection Uri for the connection or the connection details for the connection is required. + * @param includePassword (optional) if password should be included in connection string. + * @param includeApplicationName (optional) if application name should be included in connection string. + * @returns connection string for the connection + */ + getConnectionString(connectionUriOrDetails: string | ConnectionDetails, includePassword?: boolean, includeApplicationName?: boolean): Promise; + } + + /** + * Information about a database connection + */ + export interface IConnectionInfo { + /** + * server name + */ + server: string; + + /** + * database name + */ + database: string; + + /** + * user name + */ + user: string; + + /** + * password + */ + password: string; + + /** + * email + */ + email: string | undefined; + + /** + * accountId + */ + accountId: string | undefined; + + /** + * The port number to connect to. + */ + port: number; + + /** + * Gets or sets the authentication to use. + */ + authenticationType: string; + + /** + * Gets or sets the azure account token to use. + */ + azureAccountToken: string | undefined; + + /** + * Access token expiry timestamp + */ + expiresOn: number | undefined; + + /** + * Gets or sets a Boolean value that indicates whether SQL Server uses SSL encryption for all data sent between the client and server if + * the server has a certificate installed. + */ + encrypt: boolean; + + /** + * Gets or sets a value that indicates whether the channel will be encrypted while bypassing walking the certificate chain to validate trust. + */ + trustServerCertificate: boolean | undefined; + + /** + * Gets or sets a Boolean value that indicates if security-sensitive information, such as the password, is not returned as part of the connection + * if the connection is open or has ever been in an open state. + */ + persistSecurityInfo: boolean | undefined; + + /** + * Gets or sets the length of time (in seconds) to wait for a connection to the server before terminating the attempt and generating an error. + */ + connectTimeout: number | undefined; + + /** + * The number of reconnections attempted after identifying that there was an idle connection failure. + */ + connectRetryCount: number | undefined; + + /** + * Amount of time (in seconds) between each reconnection attempt after identifying that there was an idle connection failure. + */ + connectRetryInterval: number | undefined; + + /** + * Gets or sets the name of the application associated with the connection string. + */ + applicationName: string | undefined; + + /** + * Gets or sets the name of the workstation connecting to SQL Server. + */ + workstationId: string | undefined; + + /** + * Declares the application workload type when connecting to a database in an SQL Server Availability Group. + */ + applicationIntent: string | undefined; + + /** + * Gets or sets the SQL Server Language record name. + */ + currentLanguage: string | undefined; + + /** + * Gets or sets a Boolean value that indicates whether the connection will be pooled or explicitly opened every time that the connection is requested. + */ + pooling: boolean | undefined; + + /** + * Gets or sets the maximum number of connections allowed in the connection pool for this specific connection string. + */ + maxPoolSize: number | undefined; + + /** + * Gets or sets the minimum number of connections allowed in the connection pool for this specific connection string. + */ + minPoolSize: number | undefined; + + /** + * Gets or sets the minimum time, in seconds, for the connection to live in the connection pool before being destroyed. + */ + loadBalanceTimeout: number | undefined; + + /** + * Gets or sets a Boolean value that indicates whether replication is supported using the connection. + */ + replication: boolean | undefined; + + /** + * Gets or sets a string that contains the name of the primary data file. This includes the full path name of an attachable database. + */ + attachDbFilename: string | undefined; + + /** + * Gets or sets the name or address of the partner server to connect to if the primary server is down. + */ + failoverPartner: string | undefined; + + /** + * If your application is connecting to an AlwaysOn availability group (AG) on different subnets, setting MultiSubnetFailover=true + * provides faster detection of and connection to the (currently) active server. + */ + multiSubnetFailover: boolean | undefined; + + /** + * When true, an application can maintain multiple active result sets (MARS). + */ + multipleActiveResultSets: boolean | undefined; + + /** + * Gets or sets the size in bytes of the network packets used to communicate with an instance of SQL Server. + */ + packetSize: number | undefined; + + /** + * Gets or sets a string value that indicates the type system the application expects. + */ + typeSystemVersion: string | undefined; + + /** + * Gets or sets the connection string to use for this connection. + */ + connectionString: string | undefined; + } + + export const enum ExtractTarget { + dacpac = 0, + file = 1, + flat = 2, + objectType = 3, + schema = 4, + schemaObjectType = 5 + } + + export interface ISchemaCompareService { + schemaCompareGetDefaultOptions(): Thenable; + } + + export interface IDacFxService { + exportBacpac(databaseName: string, packageFilePath: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; + importBacpac(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; + extractDacpac(databaseName: string, packageFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; + createProjectFromDatabase(databaseName: string, targetFilePath: string, applicationName: string, applicationVersion: string, ownerUri: string, extractTarget: ExtractTarget, taskExecutionMode: TaskExecutionMode): Thenable; + deployDacpac(packageFilePath: string, databaseName: string, upgradeExisting: boolean, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + generateDeployScript(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode, sqlCommandVariableValues?: Record, deploymentOptions?: DeploymentOptions): Thenable; + generateDeployPlan(packageFilePath: string, databaseName: string, ownerUri: string, taskExecutionMode: TaskExecutionMode): Thenable; + getOptionsFromProfile(profilePath: string): Thenable; + validateStreamingJob(packageFilePath: string, createStreamingJobTsql: string): Thenable; + } + + export interface IAzureFunctionsService { + /** + * Adds a SQL Binding to a specified Azure function in a file + * @param bindingType Type of SQL Binding + * @param filePath Path of the file where the Azure Functions are + * @param functionName Name of the function where the SQL Binding is to be added + * @param objectName Name of Object for the SQL Query + * @param connectionStringSetting Setting for the connection string + */ + addSqlBinding(bindingType: BindingType, filePath: string, functionName: string, objectName: string, connectionStringSetting: string): Thenable; + /** + * Gets the names of the Azure functions in the file + * @param filePath Path of the file to get the Azure functions + * @returns array of names of Azure functions in the file + */ + getAzureFunctions(filePath: string): Thenable; + } + + export const enum TaskExecutionMode { + execute = 0, + script = 1, + executeAndScript = 2 + } + + export interface DeploymentOptions { + ignoreTableOptions: boolean; + ignoreSemicolonBetweenStatements: boolean; + ignoreRouteLifetime: boolean; + ignoreRoleMembership: boolean; + ignoreQuotedIdentifiers: boolean; + ignorePermissions: boolean; + ignorePartitionSchemes: boolean; + ignoreObjectPlacementOnPartitionScheme: boolean; + ignoreNotForReplication: boolean; + ignoreLoginSids: boolean; + ignoreLockHintsOnIndexes: boolean; + ignoreKeywordCasing: boolean; + ignoreIndexPadding: boolean; + ignoreIndexOptions: boolean; + ignoreIncrement: boolean; + ignoreIdentitySeed: boolean; + ignoreUserSettingsObjects: boolean; + ignoreFullTextCatalogFilePath: boolean; + ignoreWhitespace: boolean; + ignoreWithNocheckOnForeignKeys: boolean; + verifyCollationCompatibility: boolean; + unmodifiableObjectWarnings: boolean; + treatVerificationErrorsAsWarnings: boolean; + scriptRefreshModule: boolean; + scriptNewConstraintValidation: boolean; + scriptFileSize: boolean; + scriptDeployStateChecks: boolean; + scriptDatabaseOptions: boolean; + scriptDatabaseCompatibility: boolean; + scriptDatabaseCollation: boolean; + runDeploymentPlanExecutors: boolean; + registerDataTierApplication: boolean; + populateFilesOnFileGroups: boolean; + noAlterStatementsToChangeClrTypes: boolean; + includeTransactionalScripts: boolean; + includeCompositeObjects: boolean; + allowUnsafeRowLevelSecurityDataMovement: boolean; + ignoreWithNocheckOnCheckConstraints: boolean; + ignoreFillFactor: boolean; + ignoreFileSize: boolean; + ignoreFilegroupPlacement: boolean; + doNotAlterReplicatedObjects: boolean; + doNotAlterChangeDataCaptureObjects: boolean; + disableAndReenableDdlTriggers: boolean; + deployDatabaseInSingleUserMode: boolean; + createNewDatabase: boolean; + compareUsingTargetCollation: boolean; + commentOutSetVarDeclarations: boolean; + blockWhenDriftDetected: boolean; + blockOnPossibleDataLoss: boolean; + backupDatabaseBeforeChanges: boolean; + allowIncompatiblePlatform: boolean; + allowDropBlockingAssemblies: boolean; + dropConstraintsNotInSource: boolean; + dropDmlTriggersNotInSource: boolean; + dropExtendedPropertiesNotInSource: boolean; + dropIndexesNotInSource: boolean; + ignoreFileAndLogFilePath: boolean; + ignoreExtendedProperties: boolean; + ignoreDmlTriggerState: boolean; + ignoreDmlTriggerOrder: boolean; + ignoreDefaultSchema: boolean; + ignoreDdlTriggerState: boolean; + ignoreDdlTriggerOrder: boolean; + ignoreCryptographicProviderFilePath: boolean; + verifyDeployment: boolean; + ignoreComments: boolean; + ignoreColumnCollation: boolean; + ignoreAuthorizer: boolean; + ignoreAnsiNulls: boolean; + generateSmartDefaults: boolean; + dropStatisticsNotInSource: boolean; + dropRoleMembersNotInSource: boolean; + dropPermissionsNotInSource: boolean; + dropObjectsNotInSource: boolean; + ignoreColumnOrder: boolean; + doNotDropObjectTypes: SchemaObjectType[]; + excludeObjectTypes: SchemaObjectType[]; + } + + /** + * Values from \Product\Source\DeploymentApi\ObjectTypes.cs + */ + export const enum SchemaObjectType { + Aggregates = 0, + ApplicationRoles = 1, + Assemblies = 2, + AssemblyFiles = 3, + AsymmetricKeys = 4, + BrokerPriorities = 5, + Certificates = 6, + ColumnEncryptionKeys = 7, + ColumnMasterKeys = 8, + Contracts = 9, + DatabaseOptions = 10, + DatabaseRoles = 11, + DatabaseTriggers = 12, + Defaults = 13, + ExtendedProperties = 14, + ExternalDataSources = 15, + ExternalFileFormats = 16, + ExternalTables = 17, + Filegroups = 18, + Files = 19, + FileTables = 20, + FullTextCatalogs = 21, + FullTextStoplists = 22, + MessageTypes = 23, + PartitionFunctions = 24, + PartitionSchemes = 25, + Permissions = 26, + Queues = 27, + RemoteServiceBindings = 28, + RoleMembership = 29, + Rules = 30, + ScalarValuedFunctions = 31, + SearchPropertyLists = 32, + SecurityPolicies = 33, + Sequences = 34, + Services = 35, + Signatures = 36, + StoredProcedures = 37, + SymmetricKeys = 38, + Synonyms = 39, + Tables = 40, + TableValuedFunctions = 41, + UserDefinedDataTypes = 42, + UserDefinedTableTypes = 43, + ClrUserDefinedTypes = 44, + Users = 45, + Views = 46, + XmlSchemaCollections = 47, + Audits = 48, + Credentials = 49, + CryptographicProviders = 50, + DatabaseAuditSpecifications = 51, + DatabaseEncryptionKeys = 52, + DatabaseScopedCredentials = 53, + Endpoints = 54, + ErrorMessages = 55, + EventNotifications = 56, + EventSessions = 57, + LinkedServerLogins = 58, + LinkedServers = 59, + Logins = 60, + MasterKeys = 61, + Routes = 62, + ServerAuditSpecifications = 63, + ServerRoleMembership = 64, + ServerRoles = 65, + ServerTriggers = 66, + ExternalStreams = 67, + ExternalStreamingJobs = 68 + } + + /** + * ResultStatus from d.ts + */ + export interface ResultStatus { + success: boolean; + errorMessage: string; + } + + export interface DacFxResult extends ResultStatus { + operationId: string; + } + + export interface GenerateDeployPlanResult extends DacFxResult { + report: string; + } + + export interface DacFxOptionsResult extends ResultStatus { + deploymentOptions: DeploymentOptions; + } + + export interface ValidateStreamingJobResult extends ResultStatus { } + + export interface ExportParams { + databaseName: string; + packageFilePath: string; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + + export interface ImportParams { + packageFilePath: string; + databaseName: string; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + + export interface ExtractParams { + databaseName: string; + packageFilePath: string; + applicationName: string; + applicationVersion: string; + ownerUri: string; + extractTarget?: ExtractTarget; + taskExecutionMode: TaskExecutionMode; + } + + export interface DeployParams { + packageFilePath: string; + databaseName: string; + upgradeExisting: boolean; + sqlCommandVariableValues?: Record; + deploymentOptions?: DeploymentOptions; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + + export interface GenerateDeployScriptParams { + packageFilePath: string; + databaseName: string; + sqlCommandVariableValues?: Record; + deploymentOptions?: DeploymentOptions; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + + export interface GenerateDeployPlanParams { + packageFilePath: string; + databaseName: string; + ownerUri: string; + taskExecutionMode: TaskExecutionMode; + } + + export interface GetOptionsFromProfileParams { + profilePath: string; + } + + export interface ValidateStreamingJobParams { + packageFilePath: string; + createStreamingJobTsql: string; + } + + export interface SchemaCompareGetOptionsParams { } + + export interface SchemaCompareOptionsResult extends ResultStatus { + defaultDeploymentOptions: DeploymentOptions; + } + + export interface ITreeNodeInfo extends vscode.TreeItem { + readonly connectionInfo: IConnectionInfo; + nodeType: string; + metadata: ObjectMetadata; + parentNode: ITreeNodeInfo; + } + + export const enum MetadataType { + Table = 0, + View = 1, + SProc = 2, + Function = 3 + } + + export interface ObjectMetadata { + metadataType: MetadataType; + + metadataTypeName: string; + + urn: string; + + name: string; + + schema: string; + + parentName?: string; + + parentTypeName?: string; + } + + /** + * Azure functions binding type + */ + export const enum BindingType { + input, + output + } + + /** + * Parameters for adding a SQL binding to an Azure function + */ + export interface AddSqlBindingParams { + /** + * Aboslute file path of file to add SQL binding + */ + filePath: string; + + /** + * Name of function to add SQL binding + */ + functionName: string; + + /** + * Name of object to use in SQL binding + */ + objectName: string; + + /** + * Type of Azure function binding + */ + bindingType: BindingType; + + /** + * Name of SQL connection string setting specified in local.settings.json + */ + connectionStringSetting: string; + } + + /** + * Parameters for getting the names of the Azure functions in a file + */ + export interface GetAzureFunctionsParams { + /** + * Absolute file path of file to get Azure functions + */ + filePath: string; + } + + /** + * Result from a get Azure functions request + */ + export interface GetAzureFunctionsResult extends ResultStatus { + /** + * Array of names of Azure functions in the file + */ + azureFunctions: string[]; + } + + /** + * Parameters to initialize a connection to a database + */ + export interface ConnectionDetails { + + options: { [name: string]: any }; + } +} diff --git a/extensions/sql-bindings/yarn.lock b/extensions/sql-bindings/yarn.lock index fb57ccd13a..fd58b166d1 100644 --- a/extensions/sql-bindings/yarn.lock +++ b/extensions/sql-bindings/yarn.lock @@ -2,3 +2,542 @@ # yarn lockfile v1 +"@babel/code-frame@^7.0.0": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.16.7.tgz#44416b6bd7624b998f5b1af5d470856c40138789" + integrity sha512-iAXqUn8IIeBTNd72xsFlgaXHkMBMt6y4HJp1tIaK465CWLT/fG1aqB7ykr95gHHmlBdGbFeWWfyB4NJJ0nmeIg== + dependencies: + "@babel/highlight" "^7.16.7" + +"@babel/helper-validator-identifier@^7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad" + integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw== + +"@babel/highlight@^7.16.7": + version "7.16.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.16.10.tgz#744f2eb81579d6eea753c227b0f570ad785aba88" + integrity sha512-5FnTQLSLswEj6IkgVw5KusNUUFY9ZGqe/TRFnP/BKYHYgfh7tc+C7mwiy95/yNP7Dh9x580Vv8r7u7ZfTBFxdw== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + chalk "^2.0.0" + js-tokens "^4.0.0" + +"@babel/runtime@^7.1.5": + version "7.17.2" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941" + integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw== + dependencies: + regenerator-runtime "^0.13.4" + +"@microsoft/ads-extension-telemetry@^1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@microsoft/ads-extension-telemetry/-/ads-extension-telemetry-1.1.5.tgz#4f2ec72a7730131fdd939ace307a1ff5feef16b6" + integrity sha512-Xq8qQi8CHxXPTCO5cRvJABgtVdFO42kQgVoHkBc7ByBhN4VMdw/kakbWgVtHbMl4oRARtpncE2xYCiVeZHK6XA== + dependencies: + vscode-extension-telemetry "^0.1.6" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@types/fs-extra@^5.0.0": + version "5.1.0" + resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-5.1.0.tgz#2a325ef97901504a3828718c390d34b8426a10a1" + integrity sha512-AInn5+UBFIK9FK5xc9yP5e3TQSPNNgjHByqYcj9g5elVBnDQcQL7PlO1CIRy2gWlbwK7UPYqi7vRvFA44dCmYQ== + dependencies: + "@types/node" "*" + +"@types/node@*": + version "17.0.21" + resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.21.tgz#864b987c0c68d07b4345845c3e63b75edd143644" + integrity sha512-DBZCJbhII3r90XbQxI8Y9IjjiiOGlZ0Hr32omXIZvwwZ7p4DMMXGrKXVyPfuoBOri9XNtL0UK69jYIBIsRX3QQ== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +applicationinsights@1.7.4: + version "1.7.4" + resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.7.4.tgz#e7d96435594d893b00cf49f70a5927105dbb8749" + integrity sha512-XFLsNlcanpjFhHNvVWEfcm6hr7lu9znnb6Le1Lk5RE03YUV9X2B2n2MfM4kJZRrUdV+C0hdHxvWyv+vWoLfY7A== + dependencies: + cls-hooked "^4.2.2" + continuation-local-storage "^3.2.1" + diagnostic-channel "0.2.0" + diagnostic-channel-publishers "^0.3.3" + +argparse@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" + integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== + dependencies: + sprintf-js "~1.0.2" + +async-hook-jl@^1.7.6: + version "1.7.6" + resolved "https://registry.yarnpkg.com/async-hook-jl/-/async-hook-jl-1.7.6.tgz#4fd25c2f864dbaf279c610d73bf97b1b28595e68" + integrity sha512-gFaHkFfSxTjvoxDMYqDuGHlcRyUuamF8s+ZTtJdDzqjws4mCt7v0vuV79/E2Wr2/riMQgtG4/yUtXWs1gZ7JMg== + dependencies: + stack-chain "^1.3.7" + +async-listener@^0.6.0: + version "0.6.10" + resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" + integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== + dependencies: + semver "^5.3.0" + shimmer "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.1: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +builtin-modules@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" + integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= + +chalk@^2.0.0, chalk@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +cls-hooked@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/cls-hooked/-/cls-hooked-4.2.2.tgz#ad2e9a4092680cdaffeb2d3551da0e225eae1908" + integrity sha512-J4Xj5f5wq/4jAvcdgoGsL3G103BtWpZrMo8NEinRltN+xpTZdI+M38pyQqhuFU/P792xkMFvnKSf+Lm81U1bxw== + dependencies: + async-hook-jl "^1.7.6" + emitter-listener "^1.0.1" + semver "^5.4.1" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" + integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= + +commander@^2.12.1: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= + +continuation-local-storage@^3.2.1: + version "3.2.1" + resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" + integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== + dependencies: + async-listener "^0.6.0" + emitter-listener "^1.1.1" + +diagnostic-channel-publishers@^0.3.3: + version "0.3.5" + resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.3.5.tgz#a84a05fd6cc1d7619fdd17791c17e540119a7536" + integrity sha512-AOIjw4T7Nxl0G2BoBPhkQ6i7T4bUd9+xvdYizwvG7vVAM1dvr+SDrcUudlmzwH0kbEwdR2V1EcnKT0wAeYLQNQ== + +diagnostic-channel@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/diagnostic-channel/-/diagnostic-channel-0.2.0.tgz#cc99af9612c23fb1fff13612c72f2cbfaa8d5a17" + integrity sha1-zJmvlhLCP7H/8TYSxy8sv6qNWhc= + dependencies: + semver "^5.3.0" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +emitter-listener@^1.0.1, emitter-listener@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" + integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== + dependencies: + shimmer "^1.2.0" + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= + +esprima@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + +fast-glob@^3.2.7: + version "3.2.11" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" + integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== + dependencies: + "@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" + +fastq@^1.6.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" + integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== + dependencies: + reusify "^1.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +fs-extra@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" + integrity sha512-66Pm4RYbjzdyeuqudYqhFiNBbCIuI9kgRqLPSHIlXHidW8NIQtVdkM1yeZ4lXwuhbTETv3EUGMNHAAw6hiundQ== + dependencies: + graceful-fs "^4.1.2" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.1.1: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6: + version "4.2.9" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" + integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +is-core-module@^2.8.1: + version "2.8.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= + +is-glob@^4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^3.13.1: + version "3.14.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" + integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== + dependencies: + argparse "^1.0.7" + esprima "^4.0.0" + +jsonc-parser@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" + integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= + optionalDependencies: + graceful-fs "^4.1.6" + +merge2@^1.3.0: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.4.tgz#896d519dfe9db25fce94ceb7a500919bf881ebf9" + integrity sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg== + dependencies: + braces "^3.0.1" + picomatch "^2.2.3" + +minimatch@^3.0.4: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" + integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== + +mkdirp@^0.5.1: + version "0.5.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" + integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== + dependencies: + minimist "^1.2.5" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= + dependencies: + wrappy "1" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picomatch@^2.2.3: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +promisify-child-process@^3.1.1: + version "3.1.4" + resolved "https://registry.yarnpkg.com/promisify-child-process/-/promisify-child-process-3.1.4.tgz#3321827f283c0be30de1354bec1c6c627f02d53c" + integrity sha512-tLifJs99E4oOXUz/dKQjRgdchfiepmYQzBVrcVX9BtUWi9aGJeGSf2KgXOWBW1JFsSYgLkl1Z9HRm8i0sf4cTg== + dependencies: + "@babel/runtime" "^7.1.5" + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +regenerator-runtime@^0.13.4: + version "0.13.9" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" + integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== + +resolve@^1.3.2: + version "1.22.0" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198" + integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw== + dependencies: + is-core-module "^2.8.1" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +semver@^5.3.0, semver@^5.4.1: + version "5.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + +shimmer@^1.1.0, shimmer@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" + integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= + +stack-chain@^1.3.7: + version "1.3.7" + resolved "https://registry.yarnpkg.com/stack-chain/-/stack-chain-1.3.7.tgz#d192c9ff4ea6a22c94c4dd459171e3f00cea1285" + integrity sha1-0ZLJ/06moiyUxN1FkXHj8AzqEoU= + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +tslib@^1.8.0, tslib@^1.8.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslint@^5.8.0: + version "5.20.1" + resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" + integrity sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg== + dependencies: + "@babel/code-frame" "^7.0.0" + builtin-modules "^1.1.1" + chalk "^2.3.0" + commander "^2.12.1" + diff "^4.0.1" + glob "^7.1.1" + js-yaml "^3.13.1" + minimatch "^3.0.4" + mkdirp "^0.5.1" + resolve "^1.3.2" + semver "^5.3.0" + tslib "^1.8.0" + tsutils "^2.29.0" + +tsutils@^2.29.0: + version "2.29.0" + resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" + integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== + dependencies: + tslib "^1.8.1" + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +vscode-extension-telemetry@^0.1.6: + version "0.1.7" + resolved "https://registry.yarnpkg.com/vscode-extension-telemetry/-/vscode-extension-telemetry-0.1.7.tgz#18389bc24127c89dade29cd2b71ba69a6ee6ad26" + integrity sha512-pZuZTHO9OpsrwlerOKotWBRLRYJ53DobYb7aWiRAXjlqkuqE+YJJaP+2WEy8GrLIF1EnitXTDMaTAKsmLQ5ORQ== + dependencies: + applicationinsights "1.7.4" + +vscode-nls@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/vscode-nls/-/vscode-nls-4.1.2.tgz#ca8bf8bb82a0987b32801f9fddfdd2fb9fd3c167" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= diff --git a/scripts/test-extensions-unit.js b/scripts/test-extensions-unit.js index 5655ca1f74..bde4a831f9 100644 --- a/scripts/test-extensions-unit.js +++ b/scripts/test-extensions-unit.js @@ -25,7 +25,6 @@ const extensionList = [ 'resource-deployment', 'schema-compare', 'sql-database-projects', - ]; let argv = require('yargs')