From ed5ae3cd931c8c139bf1425e6a0d8cf2029cc6e9 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 3 Nov 2016 15:59:52 -0700 Subject: [PATCH 1/3] enable sending telemetry events --- Jakefile.js | 2 + .../unittests/tsserverProjectSystem.ts | 3 +- src/harness/unittests/typingsInstaller.ts | 68 ++++++++++++++--- src/server/editorServices.ts | 4 +- src/server/protocol.ts | 14 ++++ src/server/server.ts | 73 +++++++++++++------ src/server/session.ts | 6 +- src/server/shared.ts | 24 ++++++ src/server/tsconfig.json | 1 + src/server/tsconfig.library.json | 1 + src/server/types.d.ts | 22 ++++-- .../typingsInstaller/nodeTypingsInstaller.ts | 16 ++-- src/server/typingsInstaller/tsconfig.json | 1 + .../typingsInstaller/typingsInstaller.ts | 16 +++- src/server/utilities.ts | 2 + 15 files changed, 197 insertions(+), 56 deletions(-) create mode 100644 src/server/shared.ts diff --git a/Jakefile.js b/Jakefile.js index 5e42276cf566a..bdb58bbfdbd59 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -127,6 +127,7 @@ var servicesSources = [ var serverCoreSources = [ "types.d.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "typingsCache.ts", @@ -149,6 +150,7 @@ var cancellationTokenSources = [ var typingsInstallerSources = [ "../types.d.ts", + "../shared.ts", "typingsInstaller.ts", "nodeTypingsInstaller.ts" ].map(function (f) { diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index be161c418e1cc..3f36014d592b6 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -54,8 +54,9 @@ namespace ts.projectSystem { throttleLimit: number, installTypingHost: server.ServerHost, readonly typesRegistry = createMap(), + telemetryEnabled?: boolean, log?: TI.Log) { - super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, log); + super(installTypingHost, globalTypingsCacheLocation, safeList.path, throttleLimit, telemetryEnabled, log); } safeFileList = safeList.path; diff --git a/src/harness/unittests/typingsInstaller.ts b/src/harness/unittests/typingsInstaller.ts index dec8a9cbd7ef3..ef2208f5a1022 100644 --- a/src/harness/unittests/typingsInstaller.ts +++ b/src/harness/unittests/typingsInstaller.ts @@ -20,12 +20,13 @@ namespace ts.projectSystem { } class Installer extends TestTypingsInstaller { - constructor(host: server.ServerHost, p?: InstallerParams, log?: TI.Log) { + constructor(host: server.ServerHost, p?: InstallerParams, telemetryEnabled?: boolean, log?: TI.Log) { super( (p && p.globalTypingsCacheLocation) || "/a/data", (p && p.throttleLimit) || 5, host, (p && p.typesRegistry), + telemetryEnabled, log); } @@ -35,15 +36,16 @@ namespace ts.projectSystem { } } + function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void { + self.addPostExecAction(installedTypings, success => { + for (const file of typingFiles) { + host.createFileOrFolder(file, /*createParentDirectory*/ true); + } + cb(success); + }); + } + describe("typingsInstaller", () => { - function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void { - self.addPostExecAction(installedTypings, success => { - for (const file of typingFiles) { - host.createFileOrFolder(file, /*createParentDirectory*/ true); - } - cb(success); - }); - } it("configured projects (typings installed) 1", () => { const file1 = { path: "/a/b/app.js", @@ -782,7 +784,7 @@ namespace ts.projectSystem { const host = createServerHost([f1, packageJson]); const installer = new (class extends Installer { constructor() { - super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) }); + super(host, { globalTypingsCacheLocation: "/tmp" }, /*telemetryEnabled*/ false, { isEnabled: () => true, writeLine: msg => messages.push(msg) }); } installWorker(requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { assert(false, "runCommand should not be invoked"); @@ -795,4 +797,50 @@ namespace ts.projectSystem { assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name"); }); }); + + describe("telemetry events", () => { + it ("should be received", () => { + const f1 = { + path: "/a/app.js", + content: "" + }; + const package = { + path: "/a/package.json", + content: JSON.stringify({ dependencies: { "commander": "1.0.0" } }) + }; + const cachePath = "/a/cache/"; + const commander = { + path: cachePath + "node_modules/@types/commander/index.d.ts", + content: "export let x: number" + }; + const host = createServerHost([f1, package]); + let seenTelemetryEvent = false; + const installer = new (class extends Installer { + constructor() { + super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") }, /*telemetryEnabled*/ true); + } + installWorker(requestId: number, args: string[], cwd: string, cb: server.typingsInstaller.RequestCompletedAction) { + const installedTypings = ["@types/commander"]; + const typingFiles = [commander]; + executeCommand(this, host, installedTypings, typingFiles, cb); + } + sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.TypingsInstallEvent) { + if (response.kind === server.EventInstall) { + assert.deepEqual(response.packagesToInstall, ["@types/commander"]); + seenTelemetryEvent = true; + return; + } + super.sendResponse(response); + } + })(); + const projectService = createProjectService(host, { typingsInstaller: installer }); + projectService.openClientFile(f1.path); + + installer.installAll(/*expectedCount*/ 1); + + assert.isTrue(seenTelemetryEvent); + checkNumberOfProjects(projectService, { inferredProjects: 1 }); + checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]); + }); + }); } \ No newline at end of file diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index da9fb49779c43..02b62a206c771 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -284,11 +284,11 @@ namespace ts.server { return; } switch (response.kind) { - case "set": + case ActionSet: this.typingsCache.updateTypingsForProject(response.projectName, response.compilerOptions, response.typingOptions, response.typings); project.updateGraph(); break; - case "invalidate": + case ActionInvalidate: this.typingsCache.invalidateCachedTypingsForProject(project); break; } diff --git a/src/server/protocol.ts b/src/server/protocol.ts index bf4f4fdfd8b69..7739c1ab62b74 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1940,6 +1940,20 @@ namespace ts.server.protocol { childItems?: NavigationTree[]; } + export type TypingsInstalledEventName = "typingsInstalled"; + + export interface TypingsInstalledEvent extends Event { + event: TypingsInstalledEventName; + body: TypingsInstalledEventBody; + } + + export interface TypingsInstalledEventBody { + /** + * Comma separated list of installed typing packages + */ + installedPackages: string; + } + export interface NavBarResponse extends Response { body?: NavigationBarItem[]; } diff --git a/src/server/server.ts b/src/server/server.ts index 5949f5ccb99dd..41ff34d47f167 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -1,4 +1,5 @@ /// +/// /// // used in fs.writeSync /* tslint:disable:no-null-keyword */ @@ -17,7 +18,6 @@ namespace ts.server { homedir(): string } = require("os"); - function getGlobalTypingsCacheLocation() { let basePath: string; switch (process.platform) { @@ -189,8 +189,10 @@ namespace ts.server { private installer: NodeChildProcess; private socket: NodeSocket; private projectService: ProjectService; + private telemetrySender: EventSender; constructor( + private readonly telemetryEnabled: boolean, private readonly logger: server.Logger, private readonly eventPort: number, readonly globalTypingsCacheLocation: string, @@ -202,15 +204,22 @@ namespace ts.server { } } + setTelemetrySender(telemetrySender: EventSender) { + this.telemetrySender = telemetrySender; + } + attach(projectService: ProjectService) { this.projectService = projectService; if (this.logger.hasLevel(LogLevel.requestTime)) { this.logger.info("Binding..."); } - const args: string[] = ["--globalTypingsCacheLocation", this.globalTypingsCacheLocation]; + const args: string[] = [Arguments.GlobalCacheLocation, this.globalTypingsCacheLocation]; + if (this.telemetryEnabled) { + args.push(Arguments.EnableTelemetry); + } if (this.logger.loggingEnabled() && this.logger.getLogFileName()) { - args.push("--logFile", combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`)); + args.push(Arguments.LogFile, combinePaths(getDirectoryPath(normalizeSlashes(this.logger.getLogFileName())), `ti-${process.pid}.log`)); } const execArgv: string[] = []; { @@ -247,12 +256,20 @@ namespace ts.server { this.installer.send(request); } - private handleMessage(response: SetTypings | InvalidateCachedTypings) { + private handleMessage(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent) { if (this.logger.hasLevel(LogLevel.verbose)) { this.logger.info(`Received response: ${JSON.stringify(response)}`); } + if (response.kind === EventInstall) { + if (this.telemetrySender) { + const body: protocol.TypingsInstalledEventBody = { installedPackages: response.packagesToInstall.join(",") }; + const eventName: protocol.TypingsInstalledEventName = "typingsInstalled"; + this.telemetrySender.event(body, eventName); + } + return; + } this.projectService.updateTypingsForProject(response); - if (response.kind == "set" && this.socket) { + if (response.kind == ActionSet && this.socket) { this.socket.write(formatMessage({ seq: 0, type: "event", message: response }, this.logger, Buffer.byteLength, this.newLine), "utf8"); } } @@ -267,18 +284,25 @@ namespace ts.server { useSingleInferredProject: boolean, disableAutomaticTypingAcquisition: boolean, globalTypingsCacheLocation: string, + telemetryEnabled: boolean, logger: server.Logger) { - super( - host, - cancellationToken, - useSingleInferredProject, - disableAutomaticTypingAcquisition - ? nullTypingsInstaller - : new NodeTypingsInstaller(logger, installerEventPort, globalTypingsCacheLocation, host.newLine), - Buffer.byteLength, - process.hrtime, - logger, - canUseEvents); + const typingsInstaller = disableAutomaticTypingAcquisition + ? undefined + : new NodeTypingsInstaller(telemetryEnabled, logger, installerEventPort, globalTypingsCacheLocation, host.newLine); + + super( + host, + cancellationToken, + useSingleInferredProject, + typingsInstaller || nullTypingsInstaller, + Buffer.byteLength, + process.hrtime, + logger, + canUseEvents); + + if (telemetryEnabled && typingsInstaller) { + typingsInstaller.setTelemetrySender(this); + } } exit() { @@ -505,17 +529,17 @@ namespace ts.server { let eventPort: number; { - const index = sys.args.indexOf("--eventPort"); - if (index >= 0 && index < sys.args.length - 1) { - const v = parseInt(sys.args[index + 1]); - if (!isNaN(v)) { - eventPort = v; - } + const str = findArgument("--eventPort"); + const v = str && parseInt(str); + if (!isNaN(v)) { + eventPort = v; } } - const useSingleInferredProject = sys.args.indexOf("--useSingleInferredProject") >= 0; - const disableAutomaticTypingAcquisition = sys.args.indexOf("--disableAutomaticTypingAcquisition") >= 0; + const useSingleInferredProject = hasArgument("--useSingleInferredProject"); + const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition"); + const telemetryEnabled = hasArgument(Arguments.EnableTelemetry); + const ioSession = new IOSession( sys, cancellationToken, @@ -524,6 +548,7 @@ namespace ts.server { useSingleInferredProject, disableAutomaticTypingAcquisition, getGlobalTypingsCacheLocation(), + telemetryEnabled, logger); process.on("uncaughtException", function (err: Error) { ioSession.logError(err, "unknown"); diff --git a/src/server/session.ts b/src/server/session.ts index 6817624e131cd..8812b1c84281e 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -61,6 +61,10 @@ namespace ts.server { project: Project; } + export interface EventSender { + event(payload: any, eventName: string): void; + } + function allEditsBeforePos(edits: ts.TextChange[], pos: number) { for (const edit of edits) { if (textSpanEnd(edit.span) >= pos) { @@ -148,7 +152,7 @@ namespace ts.server { return `Content-Length: ${1 + len}\r\n\r\n${json}${newLine}`; } - export class Session { + export class Session implements EventSender { private readonly gcTimer: GcTimer; protected projectService: ProjectService; private errorTimer: any; /*NodeJS.Timer | number*/ diff --git a/src/server/shared.ts b/src/server/shared.ts new file mode 100644 index 0000000000000..81a1f7fb55bc2 --- /dev/null +++ b/src/server/shared.ts @@ -0,0 +1,24 @@ +/// + +namespace ts.server { + export const ActionSet: ActionSet = "action::set"; + export const ActionInvalidate: ActionInvalidate = "action::invalidate"; + export const EventInstall: EventInstall = "event::install"; + + export namespace Arguments { + export const GlobalCacheLocation = "--globalTypingsCacheLocation"; + export const LogFile = "--logFile"; + export const EnableTelemetry = "--enableTelemetry"; + } + + export function hasArgument(argumentName: string) { + return sys.args.indexOf(argumentName) >= 0; + } + + export function findArgument(argumentName: string) { + const index = sys.args.indexOf(argumentName); + return index >= 0 && index < sys.args.length - 1 + ? sys.args[index + 1] + : undefined; + } +} \ No newline at end of file diff --git a/src/server/tsconfig.json b/src/server/tsconfig.json index fe26d0c14b6d4..8741e10b14ffb 100644 --- a/src/server/tsconfig.json +++ b/src/server/tsconfig.json @@ -15,6 +15,7 @@ "files": [ "../services/shims.ts", "../services/utilities.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "scriptInfo.ts", diff --git a/src/server/tsconfig.library.json b/src/server/tsconfig.library.json index a450b591a7e96..01991431ec018 100644 --- a/src/server/tsconfig.library.json +++ b/src/server/tsconfig.library.json @@ -12,6 +12,7 @@ "files": [ "../services/shims.ts", "../services/utilities.ts", + "shared.ts", "utilities.ts", "scriptVersionCache.ts", "scriptInfo.ts", diff --git a/src/server/types.d.ts b/src/server/types.d.ts index 14af59d5cd3ae..c61b7e3955e2e 100644 --- a/src/server/types.d.ts +++ b/src/server/types.d.ts @@ -36,20 +36,32 @@ declare namespace ts.server { readonly kind: "closeProject"; } + export type ActionSet = "action::set"; + export type ActionInvalidate = "action::invalidate"; + export type EventInstall = "event::install"; + export interface TypingInstallerResponse { + readonly kind: ActionSet | ActionInvalidate | EventInstall; + } + + export interface ProjectResponse extends TypingInstallerResponse { readonly projectName: string; - readonly kind: "set" | "invalidate"; } - export interface SetTypings extends TypingInstallerResponse { + export interface SetTypings extends ProjectResponse { readonly typingOptions: ts.TypingOptions; readonly compilerOptions: ts.CompilerOptions; readonly typings: string[]; - readonly kind: "set"; + readonly kind: ActionSet; + } + + export interface InvalidateCachedTypings extends ProjectResponse { + readonly kind: ActionInvalidate; } - export interface InvalidateCachedTypings extends TypingInstallerResponse { - readonly kind: "invalidate"; + export interface TypingsInstallEvent extends TypingInstallerResponse { + readonly packagesToInstall: ReadonlyArray; + readonly kind: EventInstall; } export interface InstallTypingHost extends JsTyping.TypingResolutionHost { diff --git a/src/server/typingsInstaller/nodeTypingsInstaller.ts b/src/server/typingsInstaller/nodeTypingsInstaller.ts index cce894da9146c..2e55a2003f767 100644 --- a/src/server/typingsInstaller/nodeTypingsInstaller.ts +++ b/src/server/typingsInstaller/nodeTypingsInstaller.ts @@ -75,12 +75,13 @@ namespace ts.server.typingsInstaller { private readonly npmPath: string; readonly typesRegistry: Map; - constructor(globalTypingsCacheLocation: string, throttleLimit: number, log: Log) { + constructor(globalTypingsCacheLocation: string, throttleLimit: number, telemetryEnabled: boolean, log: Log) { super( sys, globalTypingsCacheLocation, toPath("typingSafeList.json", __dirname, createGetCanonicalFileName(sys.useCaseSensitiveFileNames)), throttleLimit, + telemetryEnabled, log); if (this.log.isEnabled()) { this.log.writeLine(`Process id: ${process.pid}`); @@ -144,15 +145,10 @@ namespace ts.server.typingsInstaller { } } - function findArgument(argumentName: string) { - const index = sys.args.indexOf(argumentName); - return index >= 0 && index < sys.args.length - 1 - ? sys.args[index + 1] - : undefined; - } + const logFilePath = findArgument(server.Arguments.LogFile); + const globalTypingsCacheLocation = findArgument(server.Arguments.GlobalCacheLocation); + const telemetryEnabled = hasArgument(server.Arguments.EnableTelemetry); - const logFilePath = findArgument("--logFile"); - const globalTypingsCacheLocation = findArgument("--globalTypingsCacheLocation"); const log = new FileLog(logFilePath); if (log.isEnabled()) { process.on("uncaughtException", (e: Error) => { @@ -165,6 +161,6 @@ namespace ts.server.typingsInstaller { } process.exit(0); }); - const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, log); + const installer = new NodeTypingsInstaller(globalTypingsCacheLocation, /*throttleLimit*/5, telemetryEnabled, log); installer.listen(); } \ No newline at end of file diff --git a/src/server/typingsInstaller/tsconfig.json b/src/server/typingsInstaller/tsconfig.json index fc4b896f2679a..a8eb73283ce2e 100644 --- a/src/server/typingsInstaller/tsconfig.json +++ b/src/server/typingsInstaller/tsconfig.json @@ -14,6 +14,7 @@ }, "files": [ "../types.d.ts", + "../shared.ts", "typingsInstaller.ts", "nodeTypingsInstaller.ts" ] diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 9cb1e0084131d..940b6442506a1 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -2,6 +2,7 @@ /// /// /// +/// namespace ts.server.typingsInstaller { interface NpmConfig { @@ -85,6 +86,7 @@ namespace ts.server.typingsInstaller { readonly globalCachePath: string, readonly safeListPath: Path, readonly throttleLimit: number, + readonly telemetryEnabled: boolean, protected readonly log = nullLog) { if (this.log.isEnabled()) { this.log.writeLine(`Global cache location '${globalCachePath}', safe file path '${safeListPath}'`); @@ -292,9 +294,17 @@ namespace ts.server.typingsInstaller { this.installRunCount++; this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => { + if (this.telemetryEnabled) { + this.sendResponse({ + kind: "event::install", + packagesToInstall: scopedTypings + }); + } + if (!ok) { return; } + // TODO: watch project directory if (this.log.isEnabled()) { this.log.writeLine(`Requested to install typings ${JSON.stringify(scopedTypings)}, installed typings ${JSON.stringify(scopedTypings)}`); @@ -348,7 +358,7 @@ namespace ts.server.typingsInstaller { this.log.writeLine(`Got FS notification for ${f}, handler is already invoked '${isInvoked}'`); } if (!isInvoked) { - this.sendResponse({ projectName: projectName, kind: "invalidate" }); + this.sendResponse({ projectName: projectName, kind: server.ActionInvalidate }); isInvoked = true; } }); @@ -363,7 +373,7 @@ namespace ts.server.typingsInstaller { typingOptions: request.typingOptions, compilerOptions: request.compilerOptions, typings, - kind: "set" + kind: server.ActionSet }; } @@ -385,6 +395,6 @@ namespace ts.server.typingsInstaller { } protected abstract installWorker(requestId: number, args: string[], cwd: string, onRequestCompleted: RequestCompletedAction): void; - protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings): void; + protected abstract sendResponse(response: SetTypings | InvalidateCachedTypings | TypingsInstallEvent): void; } } \ No newline at end of file diff --git a/src/server/utilities.ts b/src/server/utilities.ts index f75341b604e2c..ca9bd37014272 100644 --- a/src/server/utilities.ts +++ b/src/server/utilities.ts @@ -1,4 +1,5 @@ /// +/// namespace ts.server { export enum LogLevel { @@ -10,6 +11,7 @@ namespace ts.server { export const emptyArray: ReadonlyArray = []; + export interface Logger { close(): void; hasLevel(level: LogLevel): boolean; From 2449902d926aadd8152b5e92543ae3189e24f419 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Thu, 3 Nov 2016 16:33:39 -0700 Subject: [PATCH 2/3] use constant instead of string literal --- src/server/typingsInstaller/typingsInstaller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/server/typingsInstaller/typingsInstaller.ts b/src/server/typingsInstaller/typingsInstaller.ts index 940b6442506a1..5db91bb430fc9 100644 --- a/src/server/typingsInstaller/typingsInstaller.ts +++ b/src/server/typingsInstaller/typingsInstaller.ts @@ -296,7 +296,7 @@ namespace ts.server.typingsInstaller { this.installTypingsAsync(requestId, scopedTypings, cachePath, ok => { if (this.telemetryEnabled) { this.sendResponse({ - kind: "event::install", + kind: EventInstall, packagesToInstall: scopedTypings }); } From 6c51034e4c77311dea4eec25b0e2301b553e2fc0 Mon Sep 17 00:00:00 2001 From: Vladimir Matveev Date: Fri, 4 Nov 2016 07:55:19 -0700 Subject: [PATCH 3/3] introduce generic telemetry event --- src/server/protocol.ts | 22 +++++++++++++++++----- src/server/server.ts | 9 +++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 7739c1ab62b74..986e030962f6f 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -1940,14 +1940,26 @@ namespace ts.server.protocol { childItems?: NavigationTree[]; } - export type TypingsInstalledEventName = "typingsInstalled"; + export type TelemetryEventName = "telemetry"; - export interface TypingsInstalledEvent extends Event { - event: TypingsInstalledEventName; - body: TypingsInstalledEventBody; + export interface TelemetryEvent extends Event { + event: TelemetryEventName; + body: TelemetryEventBody; } - export interface TypingsInstalledEventBody { + export interface TelemetryEventBody { + telemetryEventName: string; + payload: any; + } + + export type TypingsInstalledTelemetryEventName = "typingsInstalled"; + + export interface TypingsInstalledTelemetryEventBody extends TelemetryEventBody { + telemetryEventName: TypingsInstalledTelemetryEventName; + payload: TypingsInstalledTelemetryEventPayload; + } + + export interface TypingsInstalledTelemetryEventPayload { /** * Comma separated list of installed typing packages */ diff --git a/src/server/server.ts b/src/server/server.ts index 41ff34d47f167..30c9a157efe6e 100644 --- a/src/server/server.ts +++ b/src/server/server.ts @@ -262,8 +262,13 @@ namespace ts.server { } if (response.kind === EventInstall) { if (this.telemetrySender) { - const body: protocol.TypingsInstalledEventBody = { installedPackages: response.packagesToInstall.join(",") }; - const eventName: protocol.TypingsInstalledEventName = "typingsInstalled"; + const body: protocol.TypingsInstalledTelemetryEventBody = { + telemetryEventName: "typingsInstalled", + payload: { + installedPackages: response.packagesToInstall.join(",") + } + }; + const eventName: protocol.TelemetryEventName = "telemetry"; this.telemetrySender.event(body, eventName); } return;