AzureMonitor: Kusto language support (#33528)

* Add Kusto custom language to Monaco

* Load Kusto schema into monaco

* cleanup + tests

* cleanup + tests

* cleanup :)

* move monaco languages to a registry
This commit is contained in:
Josh Hunt 2021-05-11 15:48:59 +01:00 committed by GitHub
parent 4fabade35c
commit 72c9d806fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 425 additions and 196 deletions

View File

@ -81,6 +81,7 @@
"@grafana/api-documenter": "7.11.2",
"@grafana/api-extractor": "7.10.1",
"@grafana/eslint-config": "2.4.0",
"@kusto/monaco-kusto": "3.2.7",
"@rtsao/plugin-proposal-class-properties": "7.0.1-patch.1",
"@testing-library/jest-dom": "5.11.5",
"@testing-library/react": "11.1.2",

View File

@ -14,6 +14,7 @@ export * from './valueFormats';
export * from './field';
export * from './events';
export * from './themes';
export * from './monaco';
export {
ValueMatcherOptions,
BasicValueMatcherOptions,

View File

@ -0,0 +1 @@
export * from './languageRegistry';

View File

@ -0,0 +1,7 @@
import { Registry, RegistryItem } from '../utils/Registry';
export interface MonacoLanguageRegistryItem extends RegistryItem {
init: () => Promise<void>;
}
export const monacoLanguageRegistry = new Registry<MonacoLanguageRegistryItem>();

View File

@ -1,15 +1,16 @@
import React from 'react';
import { css } from '@emotion/css';
import MonacoEditor, { loader as monacoEditorLoader } from '@monaco-editor/react';
import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api';
import { selectors } from '@grafana/e2e-selectors';
import { GrafanaTheme2, monacoLanguageRegistry } from '@grafana/data';
import { withTheme2 } from '../../themes';
import { Themeable2 } from '../../types';
import { selectors } from '@grafana/e2e-selectors';
import { GrafanaTheme2 } from '@grafana/data';
import { Monaco, MonacoEditor as MonacoEditorType, CodeEditorProps, MonacoOptions } from './types';
import { registerSuggestions } from './suggestions';
import MonacoEditor, { loader as monacoEditorLoader } from '@monaco-editor/react';
import type * as monacoType from 'monaco-editor/esm/vs/editor/editor.api';
import { CodeEditorProps, Monaco, MonacoEditor as MonacoEditorType, MonacoOptions } from './types';
import { registerSuggestions } from './suggestions';
import defineThemes from './theme';
import { css } from '@emotion/css';
type Props = CodeEditorProps & Themeable2;
@ -58,9 +59,23 @@ class UnthemedCodeEditor extends React.PureComponent<Props> {
if (getSuggestions) {
this.completionCancel = registerSuggestions(this.monaco, language, getSuggestions);
}
this.loadCustomLanguage();
}
}
loadCustomLanguage = () => {
const { language } = this.props;
const customLanguage = monacoLanguageRegistry.getIfExists(language);
if (customLanguage) {
return customLanguage.init();
}
return Promise.resolve();
};
// This is replaced with a real function when the actual editor mounts
getEditorValue = () => '';
@ -83,7 +98,6 @@ class UnthemedCodeEditor extends React.PureComponent<Props> {
handleOnMount = (editor: MonacoEditorType, monaco: Monaco) => {
const { onSave, onEditorDidMount } = this.props;
this.getEditorValue = () => editor.getValue();
if (onSave) {
@ -92,8 +106,10 @@ class UnthemedCodeEditor extends React.PureComponent<Props> {
});
}
const languagePromise = this.loadCustomLanguage();
if (onEditorDidMount) {
onEditorDidMount(editor);
languagePromise.then(() => onEditorDidMount(editor, monaco));
}
};

View File

@ -21,10 +21,8 @@ export interface CodeEditorProps {
/**
* Callback after the editor has mounted that gives you raw access to monaco
*
* @alpha -- experimental
*/
onEditorDidMount?: (editor: monacoType.editor.IStandaloneCodeEditor) => void;
onEditorDidMount?: (editor: MonacoEditor, monaco: Monaco) => void;
/** Handler to be performed when editor is blurred */
onBlur?: CodeEditorChangeHandler;

View File

@ -36,7 +36,7 @@ export { QueryField } from './QueryField/QueryField';
// Code editor
export { CodeEditor } from './Monaco/CodeEditorLazy';
export { MonacoEditor, CodeEditorSuggestionItem, CodeEditorSuggestionItemKind } from './Monaco/types';
export { Monaco, MonacoEditor, CodeEditorSuggestionItem, CodeEditorSuggestionItemKind } from './Monaco/types';
export { variableSuggestionToCodeEditorSuggestion } from './Monaco/utils';
// TODO: namespace

View File

@ -15,6 +15,7 @@ import config from 'app/core/config';
// @ts-ignore ignoring this for now, otherwise we would have to extend _ interface with move
import {
locationUtil,
monacoLanguageRegistry,
setLocale,
setTimeZoneResolver,
standardEditorsRegistry,
@ -45,6 +46,7 @@ import { getTimeSrv } from './features/dashboard/services/TimeSrv';
import { getVariablesUrlParams } from './features/variables/getAllVariableValuesForUrl';
import { SafeDynamicImport } from './core/components/DynamicImports/SafeDynamicImport';
import { featureToggledRoutes } from './routes/routes';
import getDefaultMonacoLanguages from '../lib/monaco-languages';
// add move to lodash for backward compatabilty with plugins
// @ts-ignore
@ -96,6 +98,7 @@ export class GrafanaApp {
standardFieldConfigEditorRegistry.setInit(getStandardFieldConfigs);
standardTransformersRegistry.setInit(getStandardTransformers);
variableAdapters.setInit(getDefaultVariableAdapters);
monacoLanguageRegistry.setInit(getDefaultMonacoLanguages);
setQueryRunnerFactory(() => new QueryRunner());
setVariableQueryRunner(new VariableQueryRunner());

View File

@ -1,10 +1,12 @@
export class Deferred {
resolve: any;
reject: any;
promise: Promise<any>;
export class Deferred<T = any> {
resolve?: (reason?: T | PromiseLike<T>) => void;
reject?: (reason?: any) => void;
promise: Promise<T>;
constructor() {
this.resolve = null;
this.reject = null;
this.resolve = undefined;
this.reject = undefined;
this.promise = new Promise((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;

View File

@ -30,6 +30,10 @@ export default function createMockDatasource() {
supportedTimeGrains: [],
dimensions: [],
}),
azureLogAnalyticsDatasource: {
getKustoSchema: () => Promise.resolve(),
},
};
const mockDatasource = _mockDatasource as Datasource;

View File

@ -1,7 +1,7 @@
import AzureMonitorDatasource from '../datasource';
import FakeSchemaData from './__mocks__/schema';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { AzureLogsVariable, KustoSchema } from '../types';
import { AzureLogsVariable } from '../types';
import { toUtc } from '@grafana/data';
import { backendSrv } from 'app/core/services/backend_srv';
@ -125,23 +125,39 @@ describe('AzureLogAnalyticsDatasource', () => {
beforeEach(() => {
datasourceRequestMock.mockImplementation((options: { url: string }) => {
expect(options.url).toContain('metadata');
return Promise.resolve({ data: FakeSchemaData.getlogAnalyticsFakeMetadata(), status: 200 });
return Promise.resolve({ data: FakeSchemaData.getlogAnalyticsFakeMetadata(), status: 200, ok: true });
});
});
it('should return a schema with a table and rows', () => {
return ctx.ds.azureLogAnalyticsDatasource.getSchema('myWorkspace').then((result: KustoSchema) => {
expect(Object.keys(result.Databases.Default.Tables).length).toBe(2);
expect(result.Databases.Default.Tables.Alert.Name).toBe('Alert');
expect(result.Databases.Default.Tables.AzureActivity.Name).toBe('AzureActivity');
expect(result.Databases.Default.Tables.Alert.OrderedColumns.length).toBe(69);
expect(result.Databases.Default.Tables.AzureActivity.OrderedColumns.length).toBe(21);
expect(result.Databases.Default.Tables.Alert.OrderedColumns[0].Name).toBe('TimeGenerated');
expect(result.Databases.Default.Tables.Alert.OrderedColumns[0].Type).toBe('datetime');
it('should return a schema to use with monaco-kusto', async () => {
const result = await ctx.ds.azureLogAnalyticsDatasource.getKustoSchema('myWorkspace');
expect(Object.keys(result.Databases.Default.Functions).length).toBe(1);
expect(result.Databases.Default.Functions.Func1.Name).toBe('Func1');
});
expect(result.database.tables).toHaveLength(2);
expect(result.database.tables[0].name).toBe('Alert');
expect(result.database.tables[0].timespanColumn).toBe('TimeGenerated');
expect(result.database.tables[1].name).toBe('AzureActivity');
expect(result.database.tables[0].columns).toHaveLength(69);
expect(result.database.functions[1].inputParameters).toEqual([
{
name: 'RangeStart',
type: 'datetime',
defaultValue: 'datetime(null)',
cslDefaultValue: 'datetime(null)',
},
{
name: 'VaultSubscriptionList',
type: 'string',
defaultValue: '"*"',
cslDefaultValue: '"*"',
},
{
name: 'ExcludeLegacyEvent',
type: 'bool',
defaultValue: 'True',
cslDefaultValue: 'True',
},
]);
});
});

View File

@ -1,6 +1,6 @@
import { map } from 'lodash';
import LogAnalyticsQuerystringBuilder from '../log_analytics/querystring_builder';
import ResponseParser from './response_parser';
import ResponseParser, { transformMetadataToKustoSchema } from './response_parser';
import { AzureMonitorQuery, AzureDataSourceJsonData, AzureLogsVariable, AzureQueryType } from '../types';
import {
DataQueryRequest,
@ -9,9 +9,10 @@ import {
DataSourceInstanceSettings,
MetricFindValue,
} from '@grafana/data';
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend } from '@grafana/runtime';
import { getBackendSrv, getTemplateSrv, DataSourceWithBackend, FetchResponse } from '@grafana/runtime';
import { Observable, from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata';
export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
AzureMonitorQuery,
@ -107,15 +108,20 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
return this.doRequest(workspaceListUrl, true);
}
getSchema(workspace: string) {
if (!workspace) {
return Promise.resolve();
}
async getMetadata(workspace: string) {
const url = `${this.baseUrl}/${getTemplateSrv().replace(workspace, {})}/metadata`;
const resp = await this.doRequest<AzureLogAnalyticsMetadata>(url);
return this.doRequest(url).then((response: any) => {
return new ResponseParser(response.data).parseSchemaResult();
});
if (!resp.ok) {
throw new Error('Unable to get metadata for workspace');
}
return resp.data;
}
async getKustoSchema(workspace: string) {
const metadata = await this.getMetadata(workspace);
return transformMetadataToKustoSchema(metadata, workspace);
}
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): Record<string, any> {
@ -348,7 +354,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
});
}
async doRequest(url: string, useCache = false, maxRetries = 1): Promise<any> {
async doRequest<T = any>(url: string, useCache = false, maxRetries = 1): Promise<FetchResponse<T>> {
try {
if (useCache && this.cache.has(url)) {
return this.cache.get(url);

View File

@ -1,37 +0,0 @@
import ResponseParser from './response_parser';
import { expect } from '../../../../../test/lib/common';
describe('createSchemaFunctions', () => {
describe('when called and results have functions', () => {
it('then it should return correct result', () => {
const functions = [
{ name: 'some name', body: 'some body', displayName: 'some displayName', category: 'some category' },
];
const parser = new ResponseParser({ functions });
const results = parser.createSchemaFunctions();
expect(results).toEqual({
['some name']: {
Body: 'some body',
DocString: 'some displayName',
Folder: 'some category',
FunctionKind: 'Unknown',
InputParameters: [],
Name: 'some name',
OutputColumns: [],
},
});
});
});
describe('when called and results have no functions', () => {
it('then it should return an empty object', () => {
const parser = new ResponseParser({});
const results = parser.createSchemaFunctions();
expect(results).toEqual({});
});
});
});

View File

@ -1,14 +1,7 @@
import { concat, find, flattenDeep, forEach, map } from 'lodash';
import { AnnotationEvent, dateTime, TimeSeries } from '@grafana/data';
import {
AzureLogsTableData,
AzureLogsVariable,
KustoColumn,
KustoDatabase,
KustoFunction,
KustoSchema,
KustoTable,
} from '../types';
import { AzureLogsTableData, AzureLogsVariable } from '../types';
import { AzureLogAnalyticsMetadata } from '../types/logAnalyticsMetadata';
export default class ResponseParser {
columns: string[];
@ -141,73 +134,6 @@ export default class ResponseParser {
return list;
}
parseSchemaResult(): KustoSchema {
return {
Plugins: [
{
Name: 'pivot',
},
],
Databases: this.createSchemaDatabaseWithTables(),
};
}
createSchemaDatabaseWithTables(): { [key: string]: KustoDatabase } {
const databases = {
Default: {
Name: 'Default',
Tables: this.createSchemaTables(),
Functions: this.createSchemaFunctions(),
},
};
return databases;
}
createSchemaTables(): { [key: string]: KustoTable } {
const tables: { [key: string]: KustoTable } = {};
for (const table of this.results.tables) {
tables[table.name] = {
Name: table.name,
OrderedColumns: [],
};
for (const col of table.columns) {
tables[table.name].OrderedColumns.push(this.convertToKustoColumn(col));
}
}
return tables;
}
convertToKustoColumn(col: any): KustoColumn {
return {
Name: col.name,
Type: col.type,
};
}
createSchemaFunctions(): { [key: string]: KustoFunction } {
const functions: { [key: string]: KustoFunction } = {};
if (!this.results.functions) {
return functions;
}
for (const func of this.results.functions) {
functions[func.name] = {
Name: func.name,
Body: func.body,
DocString: func.displayName,
Folder: func.category,
FunctionKind: 'Unknown',
InputParameters: [],
OutputColumns: [],
};
}
return functions;
}
static findOrCreateBucket(data: TimeSeries[], target: any): TimeSeries {
let dataTarget: any = find(data, ['target', target]);
if (!dataTarget) {
@ -222,3 +148,64 @@ export default class ResponseParser {
return dateTime(dateTimeValue).valueOf();
}
}
// matches (name):(type) = (defaultValue)
// e.g. fromRangeStart:datetime = datetime(null)
// - name: fromRangeStart
// - type: datetime
// - defaultValue: datetime(null)
const METADATA_FUNCTION_PARAMS = /([\w\W]+):([\w]+)(?:\s?=\s?([\w\W]+))?/;
function transformMetadataFunction(sourceSchema: AzureLogAnalyticsMetadata) {
if (!sourceSchema.functions) {
return [];
}
return sourceSchema.functions.map((fn) => {
const params =
fn.parameters &&
fn.parameters
.split(', ')
.map((arg) => {
const match = arg.match(METADATA_FUNCTION_PARAMS);
if (!match) {
return;
}
const [, name, type, defaultValue] = match;
return {
name,
type,
defaultValue,
cslDefaultValue: defaultValue,
};
})
.filter(<T>(v: T): v is Exclude<T, undefined> => !!v);
return {
name: fn.name,
body: fn.body,
inputParameters: params || [],
};
});
}
export function transformMetadataToKustoSchema(sourceSchema: AzureLogAnalyticsMetadata, nameOrIdOrSomething: string) {
const database = {
name: nameOrIdOrSomething,
tables: sourceSchema.tables,
functions: transformMetadataFunction(sourceSchema),
majorVersion: 0,
minorVersion: 0,
};
return {
clusterType: 'Engine',
cluster: {
connectionString: nameOrIdOrSomething,
databases: [database],
},
database: database,
};
}

View File

@ -1,8 +1,61 @@
import { CodeEditor } from '@grafana/ui';
import React, { useCallback } from 'react';
import { CodeEditor, Monaco, MonacoEditor } from '@grafana/ui';
import { Deferred } from 'app/core/utils/deferred';
import React, { useCallback, useEffect, useRef } from 'react';
import { AzureQueryEditorFieldProps } from '../../types';
const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, onQueryChange }) => {
interface MonacoPromise {
editor: MonacoEditor;
monaco: Monaco;
}
interface MonacoLanguages {
kusto: {
getKustoWorker: () => Promise<
(
url: any
) => Promise<{
setSchema: (schema: any, clusterUrl: string, name: string) => void;
}>
>;
};
}
const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, datasource, onQueryChange }) => {
const monacoPromiseRef = useRef<Deferred<MonacoPromise>>();
function getPromise() {
if (!monacoPromiseRef.current) {
monacoPromiseRef.current = new Deferred<MonacoPromise>();
}
return monacoPromiseRef.current.promise;
}
useEffect(() => {
const promises = [
datasource.azureLogAnalyticsDatasource.getKustoSchema(query.azureLogAnalytics.workspace),
getPromise(),
] as const;
// the kusto schema call might fail, but its okay for that to happen silently
Promise.all(promises).then(([schema, { monaco, editor }]) => {
const languages = (monaco.languages as unknown) as MonacoLanguages;
languages.kusto.getKustoWorker().then((kusto) => {
const model = editor.getModel();
if (!model) {
return;
}
kusto(model.uri).then((worker) => {
worker.setSchema(schema, 'https://help.kusto.windows.net', 'Samples');
});
});
});
}, [datasource.azureLogAnalyticsDatasource, query.azureLogAnalytics.workspace]);
const handleEditorMount = useCallback((editor: MonacoEditor, monaco: Monaco) => {
monacoPromiseRef.current?.resolve?.({ editor, monaco });
}, []);
const onChange = useCallback(
(newQuery: string) => {
onQueryChange({
@ -19,12 +72,13 @@ const QueryField: React.FC<AzureQueryEditorFieldProps> = ({ query, onQueryChange
return (
<CodeEditor
value={query.azureLogAnalytics.query}
language="kql"
language="kusto"
height={200}
width="100%"
showMiniMap={false}
onBlur={onChange}
onSave={onChange}
onEditorDidMount={handleEditorMount}
/>
);
};

View File

@ -1,5 +1,5 @@
import { DataQuery, DataSourceJsonData, DataSourceSettings, TableData } from '@grafana/data';
import Datasource from './datasource';
import Datasource from '../datasource';
export type AzureDataSourceSettings = DataSourceSettings<AzureDataSourceJsonData, AzureDataSourceSecureJsonData>;
@ -139,37 +139,6 @@ export interface AzureMonitorResourceGroupsResponse {
statusText: string;
}
// Azure Log Analytics types
export interface KustoSchema {
Databases: { [key: string]: KustoDatabase };
Plugins: any[];
}
export interface KustoDatabase {
Name: string;
Tables: { [key: string]: KustoTable };
Functions: { [key: string]: KustoFunction };
}
export interface KustoTable {
Name: string;
OrderedColumns: KustoColumn[];
}
export interface KustoColumn {
Name: string;
Type: string;
}
export interface KustoFunction {
Name: string;
DocString: string;
Body: string;
Folder: string;
FunctionKind: string;
InputParameters: any[];
OutputColumns: any[];
}
export interface AzureLogsVariable {
text: string;
value: string;

View File

@ -0,0 +1,96 @@
export interface AzureLogAnalyticsMetadata {
functions: AzureLogAnalyticsMetadataFunction[];
resourceTypes: AzureLogAnalyticsMetadataResourceType[];
tables: AzureLogAnalyticsMetadataTable[];
solutions: AzureLogAnalyticsMetadataSolution[];
workspaces: AzureLogAnalyticsMetadataWorkspace[];
categories: AzureLogAnalyticsMetadataCategory[];
}
export interface AzureLogAnalyticsMetadataCategory {
id: string;
displayName: string;
related: AzureLogAnalyticsMetadataCategoryRelated;
}
export interface AzureLogAnalyticsMetadataCategoryRelated {
tables: string[];
functions?: string[];
}
export interface AzureLogAnalyticsMetadataFunction {
id: string;
name: string;
displayName?: string;
description: string;
body: string;
parameters?: string;
related: AzureLogAnalyticsMetadataFunctionRelated;
}
export interface AzureLogAnalyticsMetadataFunctionRelated {
solutions: string[];
categories?: string[];
tables: string[];
}
export interface AzureLogAnalyticsMetadataResourceType {
id: string;
type: string;
displayName: string;
description: string;
related: AzureLogAnalyticsMetadataResourceTypeRelated;
}
export interface AzureLogAnalyticsMetadataResourceTypeRelated {
tables: string[];
workspaces: string[];
}
export interface AzureLogAnalyticsMetadataSolution {
id: string;
name: string;
related: AzureLogAnalyticsMetadataSolutionRelated;
}
export interface AzureLogAnalyticsMetadataSolutionRelated {
tables: string[];
functions: string[];
workspaces: string[];
}
export interface AzureLogAnalyticsMetadataTable {
id: string;
name: string;
description?: string;
timespanColumn: string;
columns: AzureLogAnalyticsMetadataColumn[];
related: AzureLogAnalyticsMetadataTableRelated;
isTroubleshootingAllowed?: boolean;
hasData?: boolean;
}
export interface AzureLogAnalyticsMetadataColumn {
name: string;
type: string;
description?: string;
isPreferredFacet?: boolean;
}
export interface AzureLogAnalyticsMetadataTableRelated {
categories?: string[];
solutions: string[];
functions?: string[];
}
export interface AzureLogAnalyticsMetadataWorkspace {
id: string;
resourceId: string;
name: string;
region: string;
related: AzureLogAnalyticsMetadataWorkspaceRelated;
}
export interface AzureLogAnalyticsMetadataWorkspaceRelated {
solutions: string[];
}

View File

@ -0,0 +1,6 @@
import loadKusto from './kusto';
export default function getDefaultMonacoLanguages() {
const kusto = { id: 'kusto', name: 'kusto', init: loadKusto };
return [kusto];
}

View File

@ -0,0 +1,65 @@
declare global {
interface Window {
__monacoKustoResolvePromise: (value: unknown) => void;
__grafana_public_path__: string;
}
}
const monacoPath = (window.__grafana_public_path__ ?? 'public/') + 'lib/monaco/min/vs';
const scripts = [
[`${monacoPath}/language/kusto/bridge.min.js`],
[
`${monacoPath}/language/kusto/kusto.javascript.client.min.js`,
`${monacoPath}/language/kusto/newtonsoft.json.min.js`,
`${monacoPath}/language/kusto/Kusto.Language.Bridge.min.js`,
],
];
function loadScript(script: HTMLScriptElement | string): Promise<void> {
return new Promise((resolve, reject) => {
let scriptEl: HTMLScriptElement;
if (typeof script === 'string') {
scriptEl = document.createElement('script');
scriptEl.src = script;
} else {
scriptEl = script;
}
scriptEl.onload = () => resolve();
scriptEl.onerror = (err) => reject(err);
document.body.appendChild(scriptEl);
});
}
const loadMonacoKusto = () => {
return new Promise((resolve) => {
window.__monacoKustoResolvePromise = resolve;
const script = document.createElement('script');
script.innerHTML = `require(['vs/language/kusto/monaco.contribution'], function() {
window.__monacoKustoResolvePromise();
});`;
return document.body.appendChild(script);
});
};
export default async function loadKusto() {
let promise = Promise.resolve();
for (const parallelScripts of scripts) {
await promise;
// Load all these scripts in parallel, then wait for them all to finish before continuing
// to the next iteration
const allPromises = parallelScripts
.filter((src) => !document.querySelector(`script[src="${src}"]`))
.map((src) => loadScript(src));
await Promise.all(allPromises);
}
await loadMonacoKusto();
}

View File

@ -88,10 +88,10 @@ module.exports = {
],
},
},
// {
// from: './node_modules/@kusto/monaco-kusto/release/min/',
// to: 'monaco/min/vs/language/kusto/',
// },
{
from: './node_modules/@kusto/monaco-kusto/release/min/',
to: '../lib/monaco/min/vs/language/kusto/',
},
],
}),
],

View File

@ -2893,6 +2893,24 @@
"@types/yargs" "^15.0.0"
chalk "^4.0.0"
"@kusto/language-service-next@0.0.42":
version "0.0.42"
resolved "https://registry.yarnpkg.com/@kusto/language-service-next/-/language-service-next-0.0.42.tgz#58d69d0a7b5727f0101e59da39928b5dfb8c2749"
integrity sha512-WI/gZjm4/qeXkA/MpnorXNlhImREafVabGwXOsjnb7VQ8fehOxUTGHbhP9kirJqqSJYx9HG7Pf1CFSYIX9CJJw==
"@kusto/language-service@2.0.0-beta.0":
version "2.0.0-beta.0"
resolved "https://registry.yarnpkg.com/@kusto/language-service/-/language-service-2.0.0-beta.0.tgz#70ea2f7c5d076d762a7c9a03194479effd870cd3"
integrity sha512-HBMASNCxtUe+BPOONpiXhzlCXuS0UIWl9YRrh521dTbEsoDwBN7Orlq6SUlDqKKdy7i4N4+7KtGFwwRjsgke7A==
"@kusto/monaco-kusto@3.2.7":
version "3.2.7"
resolved "https://registry.yarnpkg.com/@kusto/monaco-kusto/-/monaco-kusto-3.2.7.tgz#57f31022a9790a1cc1f8a2ecfcf866afe67927ba"
integrity sha512-PcMGb04G1pKsnPYD1HSkURaLsdw9gcaP9yB+qYWvb178HCwJCGrTGyCO/QmV2CbvRACUQjrtTmLo+llZOmLqDA==
dependencies:
"@kusto/language-service" "2.0.0-beta.0"
"@kusto/language-service-next" "0.0.42"
"@lerna/add@3.21.0":
version "3.21.0"
resolved "https://registry.yarnpkg.com/@lerna/add/-/add-3.21.0.tgz#27007bde71cc7b0a2969ab3c2f0ae41578b4577b"