diff --git a/_extension/package.json b/_extension/package.json index a8e30975c5d..4ca8b08a0ea 100644 --- a/_extension/package.json +++ b/_extension/package.json @@ -223,7 +223,8 @@ }, "dependencies": { "@vscode/extension-telemetry": "^1.5.1", - "vscode-languageclient": "^10.0.0-next.21" + "vscode-languageclient": "^10.0.0-next.21", + "vscode-tas-client": "0.1.84" }, "devDependencies": { "@types/vscode": "~1.110.0", diff --git a/_extension/src/commands.ts b/_extension/src/commands.ts index 1a9079deb81..4a20e567cff 100644 --- a/_extension/src/commands.ts +++ b/_extension/src/commands.ts @@ -6,7 +6,6 @@ import type { Position, } from "vscode-languageclient"; -import { Client } from "./client"; import type * as tr from "./telemetryReporting"; import { restartExtHostOnChangeIfNeeded } from "./util"; diff --git a/_extension/src/experimentationService.ts b/_extension/src/experimentationService.ts new file mode 100644 index 00000000000..ac47ae2bca1 --- /dev/null +++ b/_extension/src/experimentationService.ts @@ -0,0 +1,57 @@ +import * as vscode from "vscode"; +import * as tas from "vscode-tas-client"; + +interface ExperimentTypes { + // Experiment variables go here. + suggestNativePreview: boolean; +} + +export class ExperimentationService { + private readonly _experimentationService: tas.IExperimentationService; + private readonly _telemetryReporter: tas.IExperimentationTelemetry; + + constructor(telemetryReporter: tas.IExperimentationTelemetry, id: string, version: string, globalState: vscode.Memento) { + this._telemetryReporter = telemetryReporter; + this._experimentationService = createTasExperimentationService(this._telemetryReporter, id, version, globalState); + } + + public async getTreatmentVariable(name: K, defaultValue: ExperimentTypes[K]): Promise { + const experimentationService = this._experimentationService; + try { + const treatmentVariable = await experimentationService.getTreatmentVariableAsync("vscode", name, /*checkCache*/ true) as ExperimentTypes[K]; + return treatmentVariable ?? defaultValue; + } + catch { + return defaultValue; + } + } +} + +function createTasExperimentationService( + reporter: tas.IExperimentationTelemetry, + id: string, + version: string, + globalState: vscode.Memento, +): tas.IExperimentationService { + let targetPopulation: tas.TargetPopulation; + switch (vscode.env.uriScheme) { + case "vscode": + targetPopulation = tas.TargetPopulation.Public; + break; + case "vscode-insiders": + targetPopulation = tas.TargetPopulation.Insiders; + break; + case "vscode-exploration": + targetPopulation = tas.TargetPopulation.Internal; + break; + case "code-oss": + targetPopulation = tas.TargetPopulation.Team; + break; + default: + targetPopulation = tas.TargetPopulation.Public; + break; + } + + const experimentationService = tas.getExperimentationService(id, version, targetPopulation, reporter, globalState); + return experimentationService; +} diff --git a/_extension/src/extension.ts b/_extension/src/extension.ts index ff18f533766..9b2c28ffa8f 100644 --- a/_extension/src/extension.ts +++ b/_extension/src/extension.ts @@ -16,8 +16,12 @@ import { promptUseWorkspaceVersion, SessionManager, } from "./session"; + +import { ExperimentationService } from "./experimentationService"; import { createTelemetryReporter } from "./telemetryReporting"; +import assert from "node:assert"; + export interface ExtensionAPI { onLanguageServerInitialized: vscode.Event; initializeAPIConnection(pipe?: string): Promise; @@ -29,6 +33,14 @@ export async function activate(context: vscode.ExtensionContext): Promise = Object.create(null); + return { + // Primary reporting methods for the extension. sendTelemetryEvent, sendTelemetryErrorEvent, sendTelemetryEventUntyped: sendTelemetryEvent, sendTelemetryErrorEventUntyped: sendTelemetryErrorEvent, + // Required for the experimentation telemetry service interface. + setSharedProperty, + postEvent, + dispose: () => vscReporter.dispose(), }; + function setSharedProperty(key: string, value: string): void { + sharedProperties[key] = value; + } + + function postEvent(eventName: string, props: Map): void { + const propsAsObj = { ...sharedProperties, ...Object.fromEntries(props) }; + vscReporter.sendTelemetryEvent(eventName, propsAsObj); + } + function sendTelemetryEvent(eventName: string, data?: Record, measurements?: Record): void { + data = { ...sharedProperties, ...data }; vscReporter.sendTelemetryEvent(eventName, data, measurements); } function sendTelemetryErrorEvent(eventName: string, data?: Record, measurements?: Record): void { + data = { ...sharedProperties, ...data }; vscReporter.sendTelemetryErrorEvent(eventName, data, measurements); } } diff --git a/_extension/src/util.ts b/_extension/src/util.ts index 1d8d3cfca17..da25eb2946a 100644 --- a/_extension/src/util.ts +++ b/_extension/src/util.ts @@ -1,5 +1,3 @@ -import { exec } from "child_process"; -import { get } from "http"; import * as path from "path"; import * as vscode from "vscode"; @@ -222,3 +220,8 @@ export function readUnifiedConfig( if (explicit !== undefined) return explicit; return vscode.workspace.getConfiguration(fallbackSection, scope).get(fallbackKey, defaultValue); } + +export interface PackageInfo { + name: string; + version: string; +} diff --git a/_extension/tsconfig.json b/_extension/tsconfig.json index 6c645c7a449..1dbb37fcc55 100644 --- a/_extension/tsconfig.json +++ b/_extension/tsconfig.json @@ -1,16 +1,16 @@ { "compilerOptions": { - "target": "es2023", - "module": "NodeNext", "rootDir": "./src", "outDir": "./dist", - "esModuleInterop": true, - "strict": true, - "skipLibCheck": true, - "resolveJsonModule": true, - "sourceMap": true, "noEmit": true, - "types": ["node"] + + "target": "es2023", + "module": "preserve", + "moduleResolution": "bundler", + "types": ["node"], + + "strict": true, + "noImplicitOverride": true }, "include": ["./src/**/*"] } diff --git a/package-lock.json b/package-lock.json index c017687f1dc..6476a399d43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,7 +38,8 @@ "version": "0.0.0", "dependencies": { "@vscode/extension-telemetry": "^1.5.1", - "vscode-languageclient": "^10.0.0-next.21" + "vscode-languageclient": "^10.0.0-next.21", + "vscode-tas-client": "0.1.84" }, "devDependencies": { "@types/vscode": "~1.110.0", @@ -49,6 +50,24 @@ "vscode": "^1.110.0" } }, + "_extension/node_modules/tas-client": { + "version": "0.2.33", + "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", + "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==", + "license": "MIT" + }, + "_extension/node_modules/vscode-tas-client": { + "version": "0.1.84", + "resolved": "https://registry.npmjs.org/vscode-tas-client/-/vscode-tas-client-0.1.84.tgz", + "integrity": "sha512-rUTrUopV+70hvx1hW5ebdw1nd6djxubkLvVxjGdyD/r5v/wcVF41LIfiAtbm5qLZDtQdsMH1IaCuDoluoIa88w==", + "license": "MIT", + "dependencies": { + "tas-client": "0.2.33" + }, + "engines": { + "vscode": "^1.85.0" + } + }, "_packages/api": { "name": "@typescript/api", "version": "1.0.0",